Merge "Update the KQS overflow icon" into main
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 7a69c55..747c9cb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -803,6 +803,11 @@
     private void addJankMonitorListener(
             AnimatorSet animator, boolean expanding, @StashAnimation int animationType) {
         View v = mControllers.taskbarActivityContext.getDragLayer();
+        if (!v.isAttachedToWindow()) {
+            // If the task bar drag layer is not attached to window, we don't need to monitor jank
+            // (actually we can't pass in an unattached view either).
+            return;
+        }
         int action = expanding ? InteractionJankMonitor.CUJ_TASKBAR_EXPAND :
                 InteractionJankMonitor.CUJ_TASKBAR_COLLAPSE;
         animator.addListener(new AnimatorListenerAdapter() {
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
index 87cbdd1..fc50853 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
@@ -6,7 +6,6 @@
 import android.view.Surface.Rotation
 import android.view.View
 import android.view.ViewGroup
-import android.widget.FrameLayout
 import android.widget.ImageView
 import android.widget.LinearLayout
 import android.widget.Space
@@ -14,7 +13,9 @@
 import com.android.launcher3.DeviceProfile
 import com.android.launcher3.R
 import com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION
-import com.android.launcher3.taskbar.TaskbarManager
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_END_CONTEXTUAL_BUTTONS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_END_NAV_BUTTONS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_START_CONTEXTUAL_BUTTONS
 import com.android.systemui.shared.rotation.RotationButton
 import java.lang.IllegalStateException
 import org.junit.Assume.assumeTrue
@@ -52,11 +53,11 @@
         whenever(mockNavLayout.findViewById<View>(R.id.recent_apps)).thenReturn(mockRecentsButton)
 
         // Init top level layout
-        whenever(mockParentButtonContainer.findViewById<LinearLayout>(R.id.end_nav_buttons))
+        whenever(mockParentButtonContainer.requireViewById<LinearLayout>(ID_END_NAV_BUTTONS))
             .thenReturn(mockNavLayout)
-        whenever(mockParentButtonContainer.findViewById<ViewGroup>(R.id.end_contextual_buttons))
+        whenever(mockParentButtonContainer.requireViewById<ViewGroup>(ID_END_CONTEXTUAL_BUTTONS))
             .thenReturn(mockEndContextualLayout)
-        whenever(mockParentButtonContainer.findViewById<ViewGroup>(R.id.start_contextual_buttons))
+        whenever(mockParentButtonContainer.requireViewById<ViewGroup>(ID_START_CONTEXTUAL_BUTTONS))
             .thenReturn(mockStartContextualLayout)
     }
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 496cb4e..2b203e1 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -258,7 +258,7 @@
 import com.android.systemui.plugins.LauncherOverlayPlugin;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.shared.LauncherOverlayManager;
-import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
+import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayTouchProxy;
 import com.android.wm.shell.Flags;
 
 import java.io.FileDescriptor;
@@ -2810,7 +2810,7 @@
     /**
      * Call this after onCreate to set or clear overlay.
      */
-    public void setLauncherOverlay(LauncherOverlay overlay) {
+    public void setLauncherOverlay(LauncherOverlayTouchProxy overlay) {
         mWorkspace.setLauncherOverlay(overlay);
     }
 
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index ca83245..1fede56 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1140,7 +1140,7 @@
             mEdgeGlowLeft.onPullDistance(0f, 1f - displacement);
         }
         if (!mEdgeGlowRight.isFinished()) {
-            mEdgeGlowRight.onPullDistance(0f, displacement);
+            mEdgeGlowRight.onPullDistance(0f, displacement, ev);
         }
     }
 
@@ -1320,10 +1320,10 @@
                     int consumed = 0;
                     if (delta < 0 && mEdgeGlowRight.getDistance() != 0f) {
                         consumed = Math.round(size *
-                                mEdgeGlowRight.onPullDistance(delta / size, displacement));
+                                mEdgeGlowRight.onPullDistance(delta / size, displacement, ev));
                     } else if (delta > 0 && mEdgeGlowLeft.getDistance() != 0f) {
                         consumed = Math.round(-size *
-                                mEdgeGlowLeft.onPullDistance(-delta / size, 1 - displacement));
+                                mEdgeGlowLeft.onPullDistance(-delta / size, 1 - displacement, ev));
                     }
                     delta -= consumed;
                 }
@@ -1341,14 +1341,14 @@
                         final float pulledToX = oldScroll + delta;
 
                         if (pulledToX < mMinScroll) {
-                            mEdgeGlowLeft.onPullDistance(-delta / size, 1.f - displacement);
+                            mEdgeGlowLeft.onPullDistance(-delta / size, 1.f - displacement, ev);
                             if (!mEdgeGlowRight.isFinished()) {
-                                mEdgeGlowRight.onRelease();
+                                mEdgeGlowRight.onRelease(ev);
                             }
                         } else if (pulledToX > mMaxScroll) {
-                            mEdgeGlowRight.onPullDistance(delta / size, displacement);
+                            mEdgeGlowRight.onPullDistance(delta / size, displacement, ev);
                             if (!mEdgeGlowLeft.isFinished()) {
-                                mEdgeGlowLeft.onRelease();
+                                mEdgeGlowLeft.onRelease(ev);
                             }
                         }
 
@@ -1356,7 +1356,6 @@
                             postInvalidateOnAnimation();
                         }
                     }
-
                 } else {
                     awakenScrollBars();
                 }
@@ -1456,10 +1455,11 @@
                     }
                     invalidate();
                 }
+                mEdgeGlowLeft.onFlingVelocity(velocity);
+                mEdgeGlowRight.onFlingVelocity(velocity);
             }
-
-            mEdgeGlowLeft.onRelease();
-            mEdgeGlowRight.onRelease();
+            mEdgeGlowLeft.onRelease(ev);
+            mEdgeGlowRight.onRelease(ev);
             // End any intermediate reordering states
             resetTouchState();
             break;
@@ -1468,8 +1468,8 @@
             if (mIsBeingDragged) {
                 runOnPageScrollsInitialized(this::snapToDestination);
             }
-            mEdgeGlowLeft.onRelease();
-            mEdgeGlowRight.onRelease();
+            mEdgeGlowLeft.onRelease(ev);
+            mEdgeGlowRight.onRelease(ev);
             resetTouchState();
             break;
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index ac0d7ce..86f31a1 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -125,8 +125,8 @@
 import com.android.launcher3.widget.WidgetManagerHelper;
 import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
 import com.android.launcher3.widget.util.WidgetSizes;
-import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
 import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks;
+import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayTouchProxy;
 
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -1237,7 +1237,7 @@
         mLauncher.onPageEndTransition();
     }
 
-    public void setLauncherOverlay(LauncherOverlay overlay) {
+    public void setLauncherOverlay(LauncherOverlayTouchProxy overlay) {
         final EdgeEffectCompat newEffect;
         if (overlay == null) {
             newEffect = new EdgeEffectCompat(getContext());
diff --git a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
index 7067fa2..911612f 100644
--- a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
+++ b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
@@ -71,7 +71,7 @@
 
         @Override
         protected int getVerticalSnapPreference() {
-            return SNAP_TO_START;
+            return SNAP_TO_ANY;
         }
 
         @Override
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 051cf50..009a2aa6 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -93,11 +93,14 @@
      * Sets the current set of apps and sets mapping for {@link PackageUserKey} to Uid for
      * the current set of apps.
      *
-     * <p> Note that shouldPreinflate param should be set to {@code false} for taskbar, because this
-     * method is too late to preinflate all apps, as user will open all apps in the same frame.
+     * <p> Note that shouldPreinflate param should be set to {@code false} for taskbar, because
+     * this method is too late to preinflate all apps, as user will open all apps in the frame
+     *
+     * <p>Param: apps are required to be sorted using the comparator COMPONENT_KEY_COMPARATOR
+     * in order to enable binary search on the mApps store
      */
     public void setApps(@Nullable AppInfo[] apps, int flags, Map<PackageUserKey, Integer> map,
-            boolean shouldPreinflate)  {
+            boolean shouldPreinflate) {
         mApps = apps == null ? EMPTY_ARRAY : apps;
         mModelFlags = flags;
         notifyUpdate();
diff --git a/src/com/android/launcher3/util/EdgeEffectCompat.java b/src/com/android/launcher3/util/EdgeEffectCompat.java
index 491582b..ca37259 100644
--- a/src/com/android/launcher3/util/EdgeEffectCompat.java
+++ b/src/com/android/launcher3/util/EdgeEffectCompat.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.util;
 
 import android.content.Context;
+import android.view.MotionEvent;
 import android.widget.EdgeEffect;
 
 import com.android.launcher3.Utilities;
@@ -43,4 +44,14 @@
             return deltaDistance;
         }
     }
+
+    public float onPullDistance(float deltaDistance, float displacement, MotionEvent ev) {
+        return onPullDistance(deltaDistance, displacement);
+    }
+
+    public void onFlingVelocity(int velocity) { }
+
+    public void onRelease(MotionEvent ev) {
+        onRelease();
+    }
 }
diff --git a/src/com/android/launcher3/util/OverlayEdgeEffect.java b/src/com/android/launcher3/util/OverlayEdgeEffect.java
index 2ef1e1f..d09d801 100644
--- a/src/com/android/launcher3/util/OverlayEdgeEffect.java
+++ b/src/com/android/launcher3/util/OverlayEdgeEffect.java
@@ -17,10 +17,13 @@
 
 import android.content.Context;
 import android.graphics.Canvas;
+import android.os.SystemClock;
+import android.view.MotionEvent;
 import android.widget.EdgeEffect;
 
+import com.android.launcher3.BuildConfig;
 import com.android.launcher3.Utilities;
-import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
+import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayTouchProxy;
 
 /**
  * Extension of {@link EdgeEffect} which shows the Launcher overlay
@@ -28,11 +31,11 @@
 public class OverlayEdgeEffect extends EdgeEffectCompat {
 
     protected float mDistance;
-    protected final LauncherOverlay mOverlay;
+    protected final LauncherOverlayTouchProxy mOverlay;
     protected boolean mIsScrolling;
     protected final boolean mIsRtl;
 
-    public OverlayEdgeEffect(Context context, LauncherOverlay overlay) {
+    public OverlayEdgeEffect(Context context, LauncherOverlayTouchProxy overlay) {
         super(context);
         mOverlay = overlay;
         mIsRtl = Utilities.isRtl(context.getResources());
@@ -44,12 +47,30 @@
     }
 
     public float onPullDistance(float deltaDistance, float displacement) {
+        // Fallback implementation, will never actually get called
+        if (BuildConfig.IS_DEBUG_DEVICE) {
+            throw new RuntimeException("Wrong method called");
+        }
+        MotionEvent mv = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
+                MotionEvent.ACTION_MOVE, displacement, 0, 0);
+        try {
+            return onPullDistance(deltaDistance, displacement, mv);
+        } finally {
+            mv.recycle();
+        }
+    }
+
+    @Override
+    public float onPullDistance(float deltaDistance, float displacement, MotionEvent ev) {
         mDistance = Math.max(0f, deltaDistance + mDistance);
         if (!mIsScrolling) {
-            mOverlay.onScrollInteractionBegin();
+            int originalAction = ev.getAction();
+            ev.setAction(MotionEvent.ACTION_DOWN);
+            mOverlay.onOverlayMotionEvent(ev, 0);
+            ev.setAction(originalAction);
             mIsScrolling = true;
         }
-        mOverlay.onScrollChange(mDistance, mIsRtl);
+        mOverlay.onOverlayMotionEvent(ev, mDistance);
         return mDistance > 0 ? deltaDistance : 0;
     }
 
@@ -63,9 +84,30 @@
 
     @Override
     public void onRelease() {
+        // Fallback implementation, will never actually get called
+        if (BuildConfig.IS_DEBUG_DEVICE) {
+            throw new RuntimeException("Wrong method called");
+        }
+        MotionEvent mv = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
+                MotionEvent.ACTION_UP, mDistance, 0, 0);
+        onRelease(mv);
+        mv.recycle();
+    }
+
+    @Override
+    public void onFlingVelocity(int velocity) {
+        mOverlay.onFlingVelocity(velocity);
+    }
+
+    @Override
+    public void onRelease(MotionEvent ev) {
         if (mIsScrolling) {
+            int originalAction = ev.getAction();
+            ev.setAction(MotionEvent.ACTION_UP);
+            mOverlay.onOverlayMotionEvent(ev, mDistance);
+            ev.setAction(originalAction);
+
             mDistance = 0;
-            mOverlay.onScrollInteractionEnd();
             mIsScrolling = false;
         }
     }
diff --git a/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java b/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java
index 54cc0bc..a940774 100644
--- a/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java
+++ b/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java
@@ -15,6 +15,8 @@
  */
 package com.android.systemui.plugins.shared;
 
+import android.view.MotionEvent;
+
 import java.io.PrintWriter;
 
 /**
@@ -47,7 +49,11 @@
 
     default void onActivityDestroyed() { }
 
-    interface LauncherOverlay {
+    /**
+     * @deprecated use LauncherOverlayTouchProxy directly
+     */
+    @Deprecated
+    interface LauncherOverlay extends LauncherOverlayTouchProxy {
 
         /**
          * Touch interaction leading to overscroll has begun
@@ -70,6 +76,38 @@
          * @param callbacks A set of callbacks provided by Launcher in relation to the overlay
          */
         void setOverlayCallbacks(LauncherOverlayCallbacks callbacks);
+
+        @Override
+        default void onFlingVelocity(float velocity) { }
+
+        @Override
+        default void onOverlayMotionEvent(MotionEvent ev, float scrollProgress) {
+            switch (ev.getAction()) {
+                case MotionEvent.ACTION_DOWN ->  onScrollInteractionBegin();
+                case MotionEvent.ACTION_MOVE -> onScrollChange(scrollProgress, false);
+                case MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> onScrollInteractionEnd();
+            }
+
+        }
+    }
+
+    interface LauncherOverlayTouchProxy {
+
+        /**
+         * Called just before finishing scroll interaction to indicate the fling velocity
+         */
+        void onFlingVelocity(float velocity);
+
+        /**
+         * Called to dispatch various motion events to the overlay
+         */
+        void onOverlayMotionEvent(MotionEvent ev, float scrollProgress);
+
+        /**
+         * Called when the launcher is ready to use the overlay
+         * @param callbacks A set of callbacks provided by Launcher in relation to the overlay
+         */
+        default void setOverlayCallbacks(LauncherOverlayCallbacks callbacks) { }
     }
 
     interface LauncherOverlayCallbacks {
diff --git a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
index 6fce4c6..0f23165 100644
--- a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
+++ b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
@@ -25,6 +25,7 @@
 
 import android.content.Intent;
 import android.platform.test.annotations.PlatinumTest;
+import android.platform.test.rule.ScreenRecordRule;
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -130,6 +131,7 @@
     @Test
     @PortraitLandscape
     @PlatinumTest(focusArea = "launcher")
+    @ScreenRecordRule.ScreenRecord // b/322228038
     public void testAllAppsFromHome() {
         // Test opening all apps
         assertNotNull("switchToAllApps() returned null",