Merging ub-launcher3-qt-dev, build 5633311

Test: Manual

Bug:119330044 P1 [STOPSHIP] Add back shape detection in Launcher icons
Bug:123900446 P1 App to home animation should zoom into the app icon
Bug:132298752 P1 Adding widget locks home screen page
Bug:132757019 P1 Swipe up twice quickly from app in 2 button mode; can't access all apps
Bug:133518678 P1 In 2 button mode, flinging up strongly favours quickswitch
Bug:133806285 P1 All apps causes overview to peek in
Bug:133844237 P1 Respect limited width Exclusion Rects in bottom area?
Bug:133867119 P2 Lab-only flake: want to switch from workspace to all apps; Swipe failed to receive an event for the swipe end
Bug:134062513 P1 Quickswitch sends lifecycle events to -1
Bug:134094839 P1 Launcher kept crashing on OTA update
Bug:134096917 P1 Launcher animation component gets stuck during swipe up
Bug:134164918 P1 STOPSHIP: Disable chips hard-coded value in qt_dev Launcher
Bug:134172603 P1 Tracing for lab-only flakes
Bug:134442147 P1 Sometimes All Apps won't update on app install/uninstall/update
Change-Id: Ia530fbf9bb5fa67b602ce44aef0cc4a1d1494e13
diff --git a/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java b/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java
index 8b6f8bc..2db8b39 100644
--- a/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java
+++ b/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java
@@ -29,7 +29,7 @@
     }
 
     @Override
-    public void onSwipeUpComplete(T activity) {
+    public void onSwipeUpToRecentsComplete(T activity) {
         // Go does not support swipe up gesture.
     }
 
diff --git a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
index 7cd99ef..60320d6 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
@@ -46,13 +46,15 @@
 
     private IconNormalizer mNormalizer;
     private ShadowGenerator mShadowGenerator;
+    private final boolean mShapeDetection;
 
     private Drawable mWrapperIcon;
     private int mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND;
 
-    protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize) {
+    protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize,
+            boolean shapeDetection) {
         mContext = context.getApplicationContext();
-
+        mShapeDetection = shapeDetection;
         mFillResIconDpi = fillResIconDpi;
         mIconBitmapSize = iconBitmapSize;
 
@@ -64,6 +66,10 @@
         clear();
     }
 
+    protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize) {
+        this(context, fillResIconDpi, iconBitmapSize, false);
+    }
+
     protected void clear() {
         mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND;
         mDisableColorExtractor = false;
@@ -78,7 +84,7 @@
 
     public IconNormalizer getNormalizer() {
         if (mNormalizer == null) {
-            mNormalizer = new IconNormalizer(mIconBitmapSize);
+            mNormalizer = new IconNormalizer(mContext, mIconBitmapSize, mShapeDetection);
         }
         return mNormalizer;
     }
@@ -209,18 +215,19 @@
             }
             AdaptiveIconDrawable dr = (AdaptiveIconDrawable) mWrapperIcon;
             dr.setBounds(0, 0, 1, 1);
-            scale = getNormalizer().getScale(icon, outIconBounds);
-            if (!(icon instanceof AdaptiveIconDrawable)) {
+            boolean[] outShape = new boolean[1];
+            scale = getNormalizer().getScale(icon, outIconBounds, dr.getIconMask(), outShape);
+            if (!(icon instanceof AdaptiveIconDrawable) && !outShape[0]) {
                 FixedScaleDrawable fsd = ((FixedScaleDrawable) dr.getForeground());
                 fsd.setDrawable(icon);
                 fsd.setScale(scale);
                 icon = dr;
-                scale = getNormalizer().getScale(icon, outIconBounds);
+                scale = getNormalizer().getScale(icon, outIconBounds, null, null);
 
                 ((ColorDrawable) dr.getBackground()).setColor(mWrapperBackgroundColor);
             }
         } else {
-            scale = getNormalizer().getScale(icon, outIconBounds);
+            scale = getNormalizer().getScale(icon, outIconBounds, null, null);
         }
 
         outScale[0] = scale;
diff --git a/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java b/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java
index 4a2a7cf..de39e79 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java
@@ -18,16 +18,22 @@
 
 import android.annotation.TargetApi;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
 import android.graphics.Path;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.util.Log;
 
 import java.nio.ByteBuffer;
 
@@ -51,6 +57,9 @@
 
     private static final int MIN_VISIBLE_ALPHA = 40;
 
+    // Shape detection related constants
+    private static final float BOUND_RATIO_MARGIN = .05f;
+    private static final float PIXEL_DIFF_PERCENTAGE_THRESHOLD = 0.005f;
     private static final float SCALE_NOT_INITIALIZED = 0;
 
     // Ratio of the diameter of an normalized circular icon to the actual icon size.
@@ -59,18 +68,24 @@
     private final int mMaxSize;
     private final Bitmap mBitmap;
     private final Canvas mCanvas;
+    private final Paint mPaintMaskShape;
+    private final Paint mPaintMaskShapeOutline;
     private final byte[] mPixels;
 
     private final RectF mAdaptiveIconBounds;
     private float mAdaptiveIconScale;
 
+    private boolean mEnableShapeDetection;
+
     // for each y, stores the position of the leftmost x and the rightmost x
     private final float[] mLeftBorder;
     private final float[] mRightBorder;
     private final Rect mBounds;
+    private final Path mShapePath;
+    private final Matrix mMatrix;
 
     /** package private **/
-    IconNormalizer(int iconBitmapSize) {
+    IconNormalizer(Context context, int iconBitmapSize, boolean shapeDetection) {
         // Use twice the icon size as maximum size to avoid scaling down twice.
         mMaxSize = iconBitmapSize * 2;
         mBitmap = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ALPHA_8);
@@ -81,7 +96,22 @@
         mBounds = new Rect();
         mAdaptiveIconBounds = new RectF();
 
+        mPaintMaskShape = new Paint();
+        mPaintMaskShape.setColor(Color.RED);
+        mPaintMaskShape.setStyle(Paint.Style.FILL);
+        mPaintMaskShape.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
+
+        mPaintMaskShapeOutline = new Paint();
+        mPaintMaskShapeOutline.setStrokeWidth(
+                2 * context.getResources().getDisplayMetrics().density);
+        mPaintMaskShapeOutline.setStyle(Paint.Style.STROKE);
+        mPaintMaskShapeOutline.setColor(Color.BLACK);
+        mPaintMaskShapeOutline.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+
+        mShapePath = new Path();
+        mMatrix = new Matrix();
         mAdaptiveIconScale = SCALE_NOT_INITIALIZED;
+        mEnableShapeDetection = shapeDetection;
     }
 
     private static float getScale(float hullArea, float boundingArea, float fullArea) {
@@ -127,6 +157,72 @@
     }
 
     /**
+     * Returns if the shape of the icon is same as the path.
+     * For this method to work, the shape path bounds should be in [0,1]x[0,1] bounds.
+     */
+    private boolean isShape(Path maskPath) {
+        // Condition1:
+        // If width and height of the path not close to a square, then the icon shape is
+        // not same as the mask shape.
+        float iconRatio = ((float) mBounds.width()) / mBounds.height();
+        if (Math.abs(iconRatio - 1) > BOUND_RATIO_MARGIN) {
+            if (DEBUG) {
+                Log.d(TAG, "Not same as mask shape because width != height. " + iconRatio);
+            }
+            return false;
+        }
+
+        // Condition 2:
+        // Actual icon (white) and the fitted shape (e.g., circle)(red) XOR operation
+        // should generate transparent image, if the actual icon is equivalent to the shape.
+
+        // Fit the shape within the icon's bounding box
+        mMatrix.reset();
+        mMatrix.setScale(mBounds.width(), mBounds.height());
+        mMatrix.postTranslate(mBounds.left, mBounds.top);
+        maskPath.transform(mMatrix, mShapePath);
+
+        // XOR operation
+        mCanvas.drawPath(mShapePath, mPaintMaskShape);
+
+        // DST_OUT operation around the mask path outline
+        mCanvas.drawPath(mShapePath, mPaintMaskShapeOutline);
+
+        // Check if the result is almost transparent
+        return isTransparentBitmap();
+    }
+
+    /**
+     * Used to determine if certain the bitmap is transparent.
+     */
+    private boolean isTransparentBitmap() {
+        ByteBuffer buffer = ByteBuffer.wrap(mPixels);
+        buffer.rewind();
+        mBitmap.copyPixelsToBuffer(buffer);
+
+        int y = mBounds.top;
+        // buffer position
+        int index = y * mMaxSize;
+        // buffer shift after every row, width of buffer = mMaxSize
+        int rowSizeDiff = mMaxSize - mBounds.right;
+
+        int sum = 0;
+        for (; y < mBounds.bottom; y++) {
+            index += mBounds.left;
+            for (int x = mBounds.left; x < mBounds.right; x++) {
+                if ((mPixels[index] & 0xFF) > MIN_VISIBLE_ALPHA) {
+                    sum++;
+                }
+                index++;
+            }
+            index += rowSizeDiff;
+        }
+
+        float percentageDiffPixels = ((float) sum) / (mBounds.width() * mBounds.height());
+        return percentageDiffPixels < PIXEL_DIFF_PERCENTAGE_THRESHOLD;
+    }
+
+    /**
      * Returns the amount by which the {@param d} should be scaled (in both dimensions) so that it
      * matches the design guidelines for a launcher icon.
      *
@@ -140,7 +236,8 @@
      *
      * @param outBounds optional rect to receive the fraction distance from each edge.
      */
-    public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds) {
+    public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds,
+            @Nullable Path path, @Nullable boolean[] outMaskShape) {
         if (BaseIconFactory.ATLEAST_OREO && d instanceof AdaptiveIconDrawable) {
             if (mAdaptiveIconScale == SCALE_NOT_INITIALIZED) {
                 mAdaptiveIconScale = normalizeAdaptiveIcon(d, mMaxSize, mAdaptiveIconBounds);
@@ -242,7 +339,9 @@
                     1 - ((float) mBounds.right) / width,
                     1 - ((float) mBounds.bottom) / height);
         }
-
+        if (outMaskShape != null && mEnableShapeDetection && outMaskShape.length > 0) {
+            outMaskShape[0] = isShape(path);
+        }
         // Area of the rectangle required to fit the convex hull
         float rectArea = (bottomY + 1 - topY) * (rightX + 1 - leftX);
         return getScale(area, rectArea, width * height);
diff --git a/quickstep/recents_ui_overrides/res/values/dimens.xml b/quickstep/recents_ui_overrides/res/values/dimens.xml
index c80e531..863a8ba 100644
--- a/quickstep/recents_ui_overrides/res/values/dimens.xml
+++ b/quickstep/recents_ui_overrides/res/values/dimens.xml
@@ -27,5 +27,5 @@
     <!-- Swipe up to home related -->
     <dimen name="swipe_up_fling_min_visible_change">18dp</dimen>
     <dimen name="swipe_up_y_overshoot">10dp</dimen>
-    <dimen name="swipe_up_max_workspace_trans_y">-80dp</dimen>
+    <dimen name="swipe_up_max_workspace_trans_y">-60dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
index 4a486f8..cb5cbdd 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -164,13 +164,6 @@
         mParent = parent;
     }
 
-    private void setPredictionsEnabled(boolean predictionsEnabled) {
-        if (predictionsEnabled != mPredictionsEnabled) {
-            mPredictionsEnabled = predictionsEnabled;
-            updateVisibility();
-        }
-    }
-
     private void updateVisibility() {
         setVisibility(mPredictionsEnabled ? VISIBLE : GONE);
     }
@@ -220,8 +213,7 @@
      * If the number of predicted apps is the same as the previous list of predicted apps,
      * we can optimize by swapping them in place.
      */
-    public void setPredictedApps(boolean predictionsEnabled, List<ComponentKeyMapper> apps) {
-        setPredictionsEnabled(predictionsEnabled);
+    public void setPredictedApps(List<ComponentKeyMapper> apps) {
         mPredictedAppComponents.clear();
         mPredictedAppComponents.addAll(apps);
 
@@ -237,11 +229,6 @@
     }
 
     private void applyPredictionApps() {
-        if (!mPredictionsEnabled) {
-            mParent.onHeightUpdated();
-            return;
-        }
-
         if (getChildCount() != mNumPredictedAppsPerRow) {
             while (getChildCount() > mNumPredictedAppsPerRow) {
                 removeViewAt(0);
@@ -282,8 +269,11 @@
             }
         }
 
-        if (predictionCount == 0) {
-            setPredictionsEnabled(false);
+        boolean predictionsEnabled = predictionCount > 0;
+        if (predictionsEnabled != mPredictionsEnabled) {
+            mPredictionsEnabled = predictionsEnabled;
+            mLauncher.reapplyUi();
+            updateVisibility();
         }
         mParent.onHeightUpdated();
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
index 64cb4b4..085bbc4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
@@ -177,16 +177,10 @@
     }
 
     private void applyState(PredictionState state) {
-        boolean wasEnabled = mCurrentState.isEnabled;
         mCurrentState = state;
         if (mAppsView != null) {
             mAppsView.getFloatingHeaderView().findFixedRowByType(PredictionRowView.class)
-                    .setPredictedApps(mCurrentState.isEnabled, mCurrentState.apps);
-
-            if (wasEnabled != mCurrentState.isEnabled) {
-                // Reapply state as the State UI might have changed.
-                Launcher.getLauncher(mAppsView.getContext()).getStateManager().reapplyState(true);
-            }
+                    .setPredictedApps(mCurrentState.apps);
         }
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index a662d74..8436fe5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -32,7 +32,8 @@
 public class BackgroundAppState extends OverviewState {
 
     private static final int STATE_FLAGS =
-            FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY;
+            FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY
+                    | FLAG_DISABLE_INTERACTION;
 
     public BackgroundAppState(int id) {
         this(id, LauncherLogProto.ContainerType.TASKSWITCHER);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
index f12efc8..dc58a4e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
@@ -70,7 +70,7 @@
     }
 
     @Override
-    public void onSwipeUpComplete(RecentsActivity activity) {
+    public void onSwipeUpToRecentsComplete(RecentsActivity activity) {
         RecentsView recentsView = activity.getOverviewPanel();
         recentsView.getClearAllButton().setVisibilityAlpha(1);
         recentsView.setDisallowScrollToClearAll(false);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
index 5af09f7..d0a41f3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -91,7 +91,7 @@
     }
 
     @Override
-    public void onSwipeUpComplete(Launcher activity) {
+    public void onSwipeUpToRecentsComplete(Launcher activity) {
         // Re apply state in case we did something funky during the transition.
         activity.getStateManager().reapplyState();
         DiscoveryBounce.showForOverviewIfNeeded(activity);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 7563c3f..294a997 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -58,6 +58,8 @@
 import android.view.Surface;
 import android.view.WindowManager;
 
+import androidx.annotation.BinderThread;
+
 import com.android.launcher3.MainThreadExecutor;
 import com.android.launcher3.R;
 import com.android.launcher3.ResourceUtils;
@@ -84,6 +86,7 @@
 import com.android.systemui.shared.system.InputMonitorCompat;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
+import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -268,6 +271,9 @@
     private final RectF mSwipeTouchRegion = new RectF();
     private ComponentName mGestureBlockingActivity;
 
+    private Region mExclusionRegion;
+    private SystemGestureExclusionListenerCompat mExclusionListener;
+
     @Override
     public void onCreate() {
         super.onCreate();
@@ -284,14 +290,23 @@
             mIsUserUnlocked = false;
             registerReceiver(mUserUnlockedReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
         }
-        onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this));
 
         mDefaultDisplayId = getSystemService(WindowManager.class).getDefaultDisplay()
                 .getDisplayId();
-
         String blockingActivity = getString(R.string.gesture_blocking_activity);
         mGestureBlockingActivity = TextUtils.isEmpty(blockingActivity) ? null :
                 ComponentName.unflattenFromString(blockingActivity);
+
+        mExclusionListener = new SystemGestureExclusionListenerCompat(mDefaultDisplayId) {
+            @Override
+            @BinderThread
+            public void onExclusionChanged(Region region) {
+                // Assignments are atomic, it should be safe on binder thread
+                mExclusionRegion = region;
+            }
+        };
+
+        onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this));
         sConnected = true;
     }
 
@@ -370,6 +385,12 @@
 
         disposeEventHandlers();
         initInputMonitor();
+
+        if (mMode == Mode.NO_BUTTON) {
+            mExclusionListener.register();
+        } else {
+            mExclusionListener.unregister();
+        }
     }
 
     @Override
@@ -437,6 +458,7 @@
         sConnected = false;
         Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver);
         SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
+        mExclusionListener.unregister();
 
         super.onDestroy();
     }
@@ -557,10 +579,15 @@
         final ActivityControlHelper activityControl =
                 mOverviewComponentObserver.getActivityControlHelper();
         boolean shouldDefer = activityControl.deferStartingActivity(mActiveNavBarRegion, event);
+
+        // mExclusionRegion can change on binder thread, use a local instance here.
+        Region exclusionRegion = mExclusionRegion;
+        boolean disableHorizontalSwipe = mMode == Mode.NO_BUTTON && exclusionRegion != null
+                && exclusionRegion.contains((int) event.getX(), (int) event.getY());
         return new OtherActivityInputConsumer(this, runningTaskInfo, mRecentsModel,
                 mOverviewComponentObserver.getOverviewIntent(), activityControl,
                 shouldDefer, mOverviewCallbacks, mInputConsumer, this::onConsumerInactive,
-                mSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion);
+                mSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion, disableHorizontalSwipe);
     }
 
     private InputConsumer createDeviceLockedInputConsumer(RunningTaskInfo taskInfo) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 0d0478a..87b7326 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -19,6 +19,7 @@
 import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
 import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
 import static com.android.launcher3.Utilities.postAsyncCallback;
+import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -106,7 +107,6 @@
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.LatencyTrackerCompat;
-import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
 import com.android.systemui.shared.system.WindowCallbacksCompat;
@@ -353,7 +353,7 @@
                         | STATE_LAUNCHER_DRAWN | STATE_SCALED_CONTROLLER_RECENTS
                         | STATE_CURRENT_TASK_FINISHED | STATE_GESTURE_COMPLETED
                         | STATE_GESTURE_STARTED,
-                this::setupLauncherUiAfterSwipeUpAnimation);
+                this::setupLauncherUiAfterSwipeUpToRecentsAnimation);
 
         mStateCallback.addCallback(STATE_HANDLER_INVALIDATED, this::invalidateHandler);
         mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
@@ -647,6 +647,12 @@
     }
 
     private void buildAnimationController() {
+        if (mGestureEndTarget == HOME || (mLauncherTransitionController != null
+                && mLauncherTransitionController.getAnimationPlayer().isStarted())) {
+            // We don't want a new mLauncherTransitionController if mGestureEndTarget == HOME (it
+            // has its own animation) or if we're already animating the current controller.
+            return;
+        }
         initTransitionEndpoints(mActivity.getDeviceProfile());
         mAnimationFactory.createActivityController(mTransitionDragLength);
     }
@@ -902,10 +908,14 @@
                 // If swiping at a diagonal, base end target on the faster velocity.
                 endTarget = goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity)
                         ? NEW_TASK : HOME;
-            } else if (endVelocity < 0 && (!goingToNewTask || reachedOverviewThreshold)) {
-                // If user scrolled to a new task, only go to recents if they already passed
-                // the overview threshold. Otherwise, we'll snap to the new task and launch it.
-                endTarget = RECENTS;
+            } else if (endVelocity < 0) {
+                if (reachedOverviewThreshold) {
+                    endTarget = RECENTS;
+                } else {
+                    // If swiping at a diagonal, base end target on the faster velocity.
+                    endTarget = goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity)
+                            ? NEW_TASK : RECENTS;
+                }
             } else {
                 endTarget = goingToNewTask ? NEW_TASK : LAST_TASK;
             }
@@ -976,6 +986,12 @@
         } else if (endTarget == RECENTS) {
             mLiveTileOverlay.startIconAnimation();
             if (mRecentsView != null) {
+                int nearestPage = mRecentsView.getPageNearestToCenterOfScreen();
+                if (mRecentsView.getNextPage() != nearestPage) {
+                    // We shouldn't really scroll to the next page when swiping up to recents.
+                    // Only allow settling on the next page if it's nearest to the center.
+                    mRecentsView.snapToPage(nearestPage, Math.toIntExact(duration));
+                }
                 if (mRecentsView.getScroller().getDuration() > MAX_SWIPE_DURATION) {
                     mRecentsView.snapToPage(mRecentsView.getNextPage(), (int) MAX_SWIPE_DURATION);
                 }
@@ -1057,8 +1073,8 @@
                     setStateOnUiThread(target.endState);
                 }
             });
-            homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y);
             windowAnim.start(velocityPxPerMs);
+            homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y);
             mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
             mLauncherTransitionController = null;
         } else {
@@ -1131,16 +1147,25 @@
 
         AnimatorPlaybackController homeAnim = homeAnimationFactory.createActivityAnimationToHome();
 
+        // End on a "round-enough" radius so that the shape reveal doesn't have to do too much
+        // rounding at the end of the animation.
+        float startRadius = mClipAnimationHelper.getCurrentCornerRadius();
+        float endRadius = startRect.width() / 6f;
         // We want the window alpha to be 0 once this threshold is met, so that the
         // FolderIconView can be seen morphing into the icon shape.
         final float windowAlphaThreshold = isFloatingIconView ? 1f - SHAPE_PROGRESS_DURATION : 1f;
         anim.addOnUpdateListener((currentRect, progress) -> {
             homeAnim.setPlayFraction(progress);
 
-            float windowAlpha = Math.max(0, Utilities.mapToRange(progress, 0,
-                    windowAlphaThreshold, 1f, 0f, Interpolators.LINEAR));
+            float alphaProgress = ACCEL_1_5.getInterpolation(progress);
+            float windowAlpha = Utilities.boundToRange(Utilities.mapToRange(alphaProgress, 0,
+                    windowAlphaThreshold, 1.5f, 0f, Interpolators.LINEAR), 0, 1);
             mTransformParams.setProgress(progress)
                     .setCurrentRectAndTargetAlpha(currentRect, windowAlpha);
+            if (isFloatingIconView) {
+                mTransformParams.setCornerRadius(endRadius * progress + startRadius
+                        * (1f - progress));
+            }
             mClipAnimationHelper.applyTransform(targetSet, mTransformParams,
                     false /* launcherOnTop */);
 
@@ -1254,12 +1279,7 @@
     }
 
     private void invalidateHandlerWithLauncher() {
-        if (mLauncherTransitionController != null) {
-            if (mLauncherTransitionController.getAnimationPlayer().isStarted()) {
-                mLauncherTransitionController.getAnimationPlayer().cancel();
-            }
-            mLauncherTransitionController = null;
-        }
+        endLauncherTransitionController();
 
         mRecentsView.onGestureAnimationEnd();
 
@@ -1267,6 +1287,13 @@
         mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
     }
 
+    private void endLauncherTransitionController() {
+        if (mLauncherTransitionController != null) {
+            mLauncherTransitionController.getAnimationPlayer().end();
+            mLauncherTransitionController = null;
+        }
+    }
+
     private void notifyTransitionCancelled() {
         mAnimationFactory.onTransitionCancelled();
     }
@@ -1368,12 +1395,9 @@
         doLogGesture(HOME);
     }
 
-    private void setupLauncherUiAfterSwipeUpAnimation() {
-        if (mLauncherTransitionController != null) {
-            mLauncherTransitionController.getAnimationPlayer().end();
-            mLauncherTransitionController = null;
-        }
-        mActivityControlHelper.onSwipeUpComplete(mActivity);
+    private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
+        endLauncherTransitionController();
+        mActivityControlHelper.onSwipeUpToRecentsComplete(mActivity);
         mRecentsAnimationWrapper.setCancelWithDeferredScreenshot(true);
         mRecentsView.onSwipeUpAnimationSuccess();
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java
index bf276e1..837423a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java
@@ -62,6 +62,10 @@
     private static final String TAG = "AssistantTouchConsumer";
     private static final long RETRACT_ANIMATION_DURATION_MS = 300;
 
+    // From //java/com/google/android/apps/gsa/search/shared/util/OpaContract.java.
+    private static final String OPA_BUNDLE_TRIGGER = "triggered_by";
+    // From //java/com/google/android/apps/gsa/assistant/shared/proto/opa_trigger.proto.
+    private static final int OPA_BUNDLE_TRIGGER_DIAG_SWIPE_GESTURE = 83;
     private static final String INVOCATION_TYPE_KEY = "invocation_type";
     private static final int INVOCATION_TYPE_GESTURE = 1;
     private static final int INVOCATION_TYPE_FLING = 6;
@@ -230,6 +234,7 @@
                     startAssistantInternal(SWIPE);
 
                     Bundle args = new Bundle();
+                    args.putInt(OPA_BUNDLE_TRIGGER, OPA_BUNDLE_TRIGGER_DIAG_SWIPE_GESTURE);
                     args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);
                     mSysUiProxy.startAssistant(args);
                     mLaunchedAssistant = true;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index b0acffa..0ed4c99 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -111,6 +111,7 @@
 
     private final float mDragSlop;
     private final float mSquaredTouchSlop;
+    private final boolean mDisableHorizontalSwipe;
 
     // Slop used to check when we start moving window.
     private boolean mPassedDragSlop;
@@ -132,7 +133,7 @@
             InputConsumerController inputConsumer,
             Consumer<OtherActivityInputConsumer> onCompleteCallback,
             SwipeSharedState swipeSharedState, InputMonitorCompat inputMonitorCompat,
-            RectF swipeTouchRegion) {
+            RectF swipeTouchRegion, boolean disableHorizontalSwipe) {
         super(base);
 
         mMainThreadHandler = new Handler(Looper.getMainLooper());
@@ -162,6 +163,7 @@
         mSquaredTouchSlop = slop * slop;
 
         mPassedTouchSlop = mPassedDragSlop = continuingPreviousGesture;
+        mDisableHorizontalSwipe = !mPassedTouchSlop && disableHorizontalSwipe;
     }
 
     @Override
@@ -169,6 +171,13 @@
         return TYPE_OTHER_ACTIVITY;
     }
 
+    private void forceCancelGesture(MotionEvent ev) {
+        int action = ev.getAction();
+        ev.setAction(ACTION_CANCEL);
+        finishTouchTracking(ev);
+        ev.setAction(action);
+    }
+
     @Override
     public void onMotionEvent(MotionEvent ev) {
         if (mVelocityTracker == null) {
@@ -216,10 +225,7 @@
                     // Cancel interaction in case of multi-touch interaction
                     int ptrIdx = ev.getActionIndex();
                     if (!mSwipeTouchRegion.contains(ev.getX(ptrIdx), ev.getY(ptrIdx))) {
-                        int action = ev.getAction();
-                        ev.setAction(ACTION_CANCEL);
-                        finishTouchTracking(ev);
-                        ev.setAction(action);
+                        forceCancelGesture(ev);
                     }
                 }
                 break;
@@ -258,7 +264,15 @@
                 }
 
                 if (!mPassedTouchSlop) {
-                    if (squaredHypot(displacementX, mLastPos.y - mDownPos.y) >= mSquaredTouchSlop) {
+                    float displacementY = mLastPos.y - mDownPos.y;
+                    if (squaredHypot(displacementX, displacementY) >= mSquaredTouchSlop) {
+                        if (mDisableHorizontalSwipe
+                                && Math.abs(displacementX) > Math.abs(displacementY)) {
+                            // Horizontal gesture is not allowed in this region
+                            forceCancelGesture(ev);
+                            break;
+                        }
+
                         mPassedTouchSlop = true;
 
                         if (mIsDeferredDownTarget) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
index 0ae469c..19a4963 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -197,10 +197,15 @@
                     mTmpMatrix.postTranslate(app.position.x, app.position.y);
                     mClipRectF.roundOut(crop);
                     if (mSupportsRoundedCornersOnWindows) {
-                        float windowCornerRadius = mUseRoundedCornersOnWindows
-                                ? mWindowCornerRadius : 0;
-                        cornerRadius = Utilities.mapRange(progress, windowCornerRadius,
-                                mTaskCornerRadius);
+                        if (params.cornerRadius > -1) {
+                            cornerRadius = params.cornerRadius;
+                            scale = params.currentRect.width() / crop.width();
+                        } else {
+                            float windowCornerRadius = mUseRoundedCornersOnWindows
+                                    ? mWindowCornerRadius : 0;
+                            cornerRadius = Utilities.mapRange(progress, windowCornerRadius,
+                                    mTaskCornerRadius);
+                        }
                         mCurrentCornerRadius = cornerRadius;
                     }
                 }
@@ -355,6 +360,7 @@
         @Nullable RectF currentRect;
         float targetAlpha;
         boolean forLiveTile;
+        float cornerRadius;
 
         SyncRtSurfaceTransactionApplierCompat syncTransactionApplier;
 
@@ -365,6 +371,7 @@
             currentRect = null;
             targetAlpha = 0;
             forLiveTile = false;
+            cornerRadius = -1;
         }
 
         public TransformParams setProgress(float progress) {
@@ -373,6 +380,11 @@
             return this;
         }
 
+        public TransformParams setCornerRadius(float cornerRadius) {
+            this.cornerRadius = cornerRadius;
+            return this;
+        }
+
         public TransformParams setCurrentRectAndTargetAlpha(RectF currentRect, float targetAlpha) {
             this.currentRect = currentRect;
             this.targetAlpha = targetAlpha;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 93b6e4b..837c2dc 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -44,14 +44,13 @@
  */
 public class StaggeredWorkspaceAnim {
 
-    private static final int APP_CLOSE_ROW_START_DELAY_MS = 16;
-    private static final int ALPHA_DURATION_MS = 200;
+    private static final int APP_CLOSE_ROW_START_DELAY_MS = 10;
+    private static final int ALPHA_DURATION_MS = 250;
 
     private static final float MAX_VELOCITY_PX_PER_S = 22f;
 
-    private static final float DAMPING_RATIO =
-            (SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY + SpringForce.DAMPING_RATIO_LOW_BOUNCY) / 2f;
-    private static final float STIFFNESS = SpringForce.STIFFNESS_LOW;
+    private static final float DAMPING_RATIO = 0.7f;
+    private static final float STIFFNESS = 150f;
 
     private final float mVelocity;
     private final float mSpringTransY;
@@ -71,9 +70,9 @@
 
         // Scale the translationY based on the initial velocity to better sync the workspace items
         // with the floating view.
-        float transFactor = 0.1f + 0.9f * Math.abs(velocity) / MAX_VELOCITY_PX_PER_S;
+        float transFactor = 0.2f + 0.9f * Math.abs(velocity) / MAX_VELOCITY_PX_PER_S;
         mSpringTransY = transFactor * launcher.getResources()
-                .getDimensionPixelSize(R.dimen.swipe_up_max_workspace_trans_y);;
+                .getDimensionPixelSize(R.dimen.swipe_up_max_workspace_trans_y);
 
         DeviceProfile grid = launcher.getDeviceProfile();
         ShortcutAndWidgetContainer currentPage = ((CellLayout) launcher.getWorkspace()
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index 79540c1..dcf2e3c 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -119,6 +119,9 @@
     private static final long APP_LAUNCH_ALPHA_DOWN_DURATION =
             (long) (APP_LAUNCH_ALPHA_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
 
+    private static final long CROP_DURATION = 375;
+    private static final long RADIUS_DURATION = 375;
+
     public static final int RECENTS_LAUNCH_DURATION = 336;
     private static final int LAUNCHER_RESUME_START_DELAY = 100;
     private static final int CLOSING_TRANSITION_DURATION_MS = 250;
@@ -494,10 +497,10 @@
                     EXAGGERATED_EASE);
             FloatProp mIconAlpha = new FloatProp(1f, 0f, APP_LAUNCH_ALPHA_START_DELAY,
                     alphaDuration, LINEAR);
-            FloatProp mCroppedSize = new FloatProp(startCrop, endCrop, 0, APP_LAUNCH_DURATION,
+            FloatProp mCroppedSize = new FloatProp(startCrop, endCrop, 0, CROP_DURATION,
                     EXAGGERATED_EASE);
             FloatProp mWindowRadius = new FloatProp(startCrop / 2f, windowRadius, 0,
-                    APP_LAUNCH_DURATION, EXAGGERATED_EASE);
+                    RADIUS_DURATION, EXAGGERATED_EASE);
 
             @Override
             public void onUpdate(float percent) {
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index 279a946..b17d8d6 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -51,7 +51,7 @@
 
     int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect);
 
-    void onSwipeUpComplete(T activity);
+    void onSwipeUpToRecentsComplete(T activity);
 
     void onAssistantVisibilityChanged(float visibility);
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index ed0b90f..855535b 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -277,6 +277,7 @@
 
     final Handler mHandler = new Handler();
     private final Runnable mHandleDeferredResume = this::handleDeferredResume;
+    private boolean mDeferredResumePending;
 
     private float mCurrentAssistantVisibility = 0f;
 
@@ -889,26 +890,40 @@
     }
 
     private void handleDeferredResume() {
-        if (hasBeenResumed()) {
+        if (hasBeenResumed() && !mStateManager.getState().disableInteraction) {
             getUserEventDispatcher().logActionCommand(Action.Command.RESUME,
                     mStateManager.getState().containerType, -1);
             getUserEventDispatcher().startSession();
 
             UiFactory.onLauncherStateOrResumeChanged(this);
             AppLaunchTracker.INSTANCE.get(this).onReturnedToHome();
-            resetPendingActivityResultIfNeeded();
-        }
-    }
 
-    private void resetPendingActivityResultIfNeeded() {
-        if (hasBeenResumed() && mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
-            UiFactory.resetPendingActivityResults(this, mPendingActivityRequestCode);
+            // Process any items that were added while Launcher was away.
+            InstallShortcutReceiver.disableAndFlushInstallQueue(
+                    InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this);
+
+            // Refresh shortcuts if the permission changed.
+            mModel.refreshShortcutsIfRequired();
+
+            DiscoveryBounce.showForHomeIfNeeded(this);
+
+            if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
+                UiFactory.resetPendingActivityResults(this, mPendingActivityRequestCode);
+            }
+            mDeferredResumePending = false;
+        } else {
+            mDeferredResumePending = true;
         }
     }
 
     protected void onStateSet(LauncherState state) {
         getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
-        resetPendingActivityResultIfNeeded();
+        if (mDeferredResumePending) {
+            handleDeferredResume();
+        }
+        if (mLauncherCallbacks != null) {
+            mLauncherCallbacks.onStateChanged();
+        }
     }
 
     @Override
@@ -922,14 +937,7 @@
         Utilities.postAsyncCallback(mHandler, mHandleDeferredResume);
 
         setOnResumeCallback(null);
-        // Process any items that were added while Launcher was away.
-        InstallShortcutReceiver.disableAndFlushInstallQueue(
-                InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this);
 
-        // Refresh shortcuts if the permission changed.
-        mModel.refreshShortcutsIfRequired();
-
-        DiscoveryBounce.showForHomeIfNeeded(this);
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onResume();
         }
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index dc27516..d07638a 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -68,9 +68,6 @@
     }
 
     private LauncherAppState(Context context) {
-        if (!UserManagerCompat.getInstance(context).isUserUnlocked(Process.myUserHandle())) {
-            throw new RuntimeException("LauncherAppState should not start in direct boot mode");
-        }
         if (getLocalProvider(context) == null) {
             throw new RuntimeException(
                     "Initializing LauncherAppState in the absence of LauncherProvider");
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index edac516..dfe75ec 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -21,7 +21,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.ArrayList;
 
 /**
  * LauncherCallbacks is an interface used to extend the Launcher activity. It includes many hooks
@@ -53,6 +52,11 @@
     boolean handleBackPressed();
     void onTrimMemory(int level);
 
+    /**
+     * Called when the launcher state changed
+     */
+    default void onStateChanged() { }
+
     /*
      * Extension points for providing custom behavior on certain user interactions.
      */
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 3edd838..fe6b522 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -40,6 +40,7 @@
 import android.animation.AnimatorSet;
 import android.os.Handler;
 import android.os.Looper;
+import android.util.Log;
 
 import androidx.annotation.IntDef;
 
@@ -474,6 +475,11 @@
         // Only change the stable states after the transitions have finished
         if (state != mCurrentStableState) {
             mLastStableState = state.getHistoryForState(mCurrentStableState);
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG,
+                        "mCurrentStableState = " + state.getClass().getSimpleName() + " @ " +
+                                android.util.Log.getStackTraceString(new Throwable()));
+            }
             mCurrentStableState = state;
         }
 
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 053c570..0d43e21 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -625,15 +625,16 @@
 
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
+        final boolean result = super.dispatchTouchEvent(ev);
         switch (ev.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
-                mAllAppsStore.setDeferUpdates(true);
+                if (result) mAllAppsStore.setDeferUpdates(true);
                 break;
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
                 mAllAppsStore.setDeferUpdates(false);
                 break;
         }
-        return super.dispatchTouchEvent(ev);
+        return result;
     }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index a7f89d9..c62fc3d 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -14,6 +14,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.util.Log;
 import android.util.Property;
 import android.view.animation.Interpolator;
 
@@ -29,6 +30,7 @@
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.SpringObjectAnimator;
 import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ScrimView;
@@ -162,6 +164,10 @@
     @Override
     public void setStateWithAnimation(LauncherState toState,
             AnimatorSetBuilder builder, AnimationConfig config) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG,
+                    "setStateWithAnimation " + toState.getClass().getSimpleName());
+        }
         float targetProgress = toState.getVerticalProgress(mLauncher);
         if (Float.compare(mProgress, targetProgress) == 0) {
             setAlphas(toState, config, builder);
diff --git a/src/com/android/launcher3/anim/FlingSpringAnim.java b/src/com/android/launcher3/anim/FlingSpringAnim.java
index f53ea51..eaf3b1c 100644
--- a/src/com/android/launcher3/anim/FlingSpringAnim.java
+++ b/src/com/android/launcher3/anim/FlingSpringAnim.java
@@ -29,7 +29,7 @@
 
     private static final float FLING_FRICTION = 1.5f;
     private static final float SPRING_STIFFNESS = 200;
-    private static final float SPRING_DAMPING = 0.85f;
+    private static final float SPRING_DAMPING = 0.8f;
 
     private final FlingAnimation mFlingAnim;
     private SpringAnimation mSpringAnim;
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 54d0db1..7e20d11 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -105,7 +105,7 @@
             "ENABLE_QUICKSTEP_LIVE_TILE", false, "Enable live tile in Quickstep overview");
 
     public static final TogglableFlag ENABLE_HINTS_IN_OVERVIEW = new TogglableFlag(
-            "ENABLE_HINTS_IN_OVERVIEW", true,
+            "ENABLE_HINTS_IN_OVERVIEW", false,
             "Show chip hints and gleams on the overview screen");
 
     public static final TogglableFlag FAKE_LANDSCAPE_UI = new TogglableFlag(
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index b35e23c..6ba015b 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -130,16 +130,6 @@
         return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
     }
 
-    @Override
-    protected boolean findActiveController(MotionEvent ev) {
-        if (mActivity.getStateManager().getState().disableInteraction) {
-            // You Shall Not Pass!!!
-            mActiveController = null;
-            return true;
-        }
-        return super.findActiveController(ev);
-    }
-
     private boolean isEventOverAccessibleDropTargetBar(MotionEvent ev) {
         return isInAccessibleDrag() && isEventOverView(mActivity.getDropTargetBar(), ev);
     }
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index d1bd2db..09c5e5b 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -243,7 +243,7 @@
                             nDr = new AdaptiveIconDrawable(new ColorDrawable(Color.BLACK), null);
                         }
                         Utilities.scaleRectAboutCenter(bounds,
-                                li.getNormalizer().getScale(nDr, null));
+                                li.getNormalizer().getScale(nDr, null, null, null));
                     }
                     AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) dr;
 
diff --git a/src/com/android/launcher3/graphics/IconShape.java b/src/com/android/launcher3/graphics/IconShape.java
index 88e4452..4369385 100644
--- a/src/com/android/launcher3/graphics/IconShape.java
+++ b/src/com/android/launcher3/graphics/IconShape.java
@@ -91,6 +91,10 @@
 
     private SparseArray<TypedValue> mAttrs;
 
+    public boolean enableShapeDetection(){
+        return false;
+    };
+
     public abstract void drawShape(Canvas canvas, float offsetX, float offsetY, float radius,
             Paint paint);
 
@@ -194,6 +198,11 @@
         protected float getStartRadius(Rect startRect) {
             return startRect.width() / 2f;
         }
+
+        @Override
+        public boolean enableShapeDetection() {
+            return true;
+        }
     }
 
     public static class RoundedSquare extends SimpleRectShape {
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index 5c4af1f..3b74307 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -30,6 +30,7 @@
 import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
+import com.android.launcher3.graphics.IconShape;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.util.Themes;
@@ -50,11 +51,15 @@
     private static LauncherIcons sPool;
     private static int sPoolId = 0;
 
+    public static LauncherIcons obtain(Context context) {
+        return obtain(context, IconShape.getShape().enableShapeDetection());
+    }
+
     /**
      * Return a new Message instance from the global pool. Allows us to
      * avoid allocating new objects in many cases.
      */
-    public static LauncherIcons obtain(Context context) {
+    public static LauncherIcons obtain(Context context, boolean shapeDetection) {
         int poolId;
         synchronized (sPoolSync) {
             if (sPool != null) {
@@ -67,7 +72,8 @@
         }
 
         InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
-        return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize, poolId);
+        return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize, poolId,
+                shapeDetection);
     }
 
     public static void clearPool() {
@@ -81,8 +87,9 @@
 
     private LauncherIcons next;
 
-    private LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId) {
-        super(context, fillResIconDpi, iconBitmapSize);
+    private LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId,
+            boolean shapeDetection) {
+        super(context, fillResIconDpi, iconBitmapSize, shapeDetection);
         mPoolId = poolId;
     }
 
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 9703aa6..4e5f7a5 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -31,6 +31,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.os.SystemClock;
+import android.util.Log;
 import android.view.HapticFeedbackConstants;
 import android.view.MotionEvent;
 
@@ -118,6 +119,9 @@
 
     @Override
     public final boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onControllerInterceptTouchEvent 1 " + ev);
+        }
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             mNoIntercept = !canInterceptTouch(ev);
             if (mNoIntercept) {
@@ -147,6 +151,9 @@
             return false;
         }
 
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onControllerInterceptTouchEvent 2 ");
+        }
         onControllerTouchEvent(ev);
         return mDetector.isDraggingOrSettling();
     }
@@ -234,6 +241,9 @@
 
     @Override
     public void onDragStart(boolean start) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onDragStart 1 " + start);
+        }
         mStartState = mLauncher.getStateManager().getState();
         if (mStartState == ALL_APPS) {
             mStartContainerType = LauncherLogProto.ContainerType.ALLAPPS;
@@ -243,6 +253,9 @@
             mStartContainerType = LauncherLogProto.ContainerType.TASKSWITCHER;
         }
         if (mCurrentAnimation == null) {
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onDragStart 2");
+            }
             mFromState = mStartState;
             mToState = null;
             cancelAnimationControllers();
@@ -552,6 +565,9 @@
     }
 
     private void cancelAnimationControllers() {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "cancelAnimationControllers");
+        }
         mCurrentAnimation = null;
         cancelAtomicComponentsController();
         mDetector.finishedScrolling();
diff --git a/src/com/android/launcher3/touch/SwipeDetector.java b/src/com/android/launcher3/touch/SwipeDetector.java
index 4616e58..3d45404 100644
--- a/src/com/android/launcher3/touch/SwipeDetector.java
+++ b/src/com/android/launcher3/touch/SwipeDetector.java
@@ -158,6 +158,9 @@
     // SETTLING -> (View settled) -> IDLE
 
     private void setState(ScrollState newState) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "setState -- start: " + newState);
+        }
         if (DBG) {
             Log.d(TAG, "setState:" + mState + "->" + newState);
         }
@@ -165,6 +168,9 @@
         if (newState == ScrollState.DRAGGING) {
             initializeDragging();
             if (mState == ScrollState.IDLE) {
+                if (TestProtocol.sDebugTracing) {
+                    Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "setState -- 1: " + newState);
+                }
                 reportDragStart(false /* recatch */);
             } else if (mState == ScrollState.SETTLING) {
                 reportDragStart(true /* recatch */);
@@ -318,9 +324,15 @@
                     break;
                 }
                 mDisplacement = mDir.getDisplacement(ev, pointerIndex, mDownPos, mIsRtl);
+                if (TestProtocol.sDebugTracing) {
+                    Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onTouchEvent 1");
+                }
 
                 // handle state and listener calls.
                 if (mState != ScrollState.DRAGGING && shouldScrollStart(ev, pointerIndex)) {
+                    if (TestProtocol.sDebugTracing) {
+                        Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onTouchEvent 2");
+                    }
                     setState(ScrollState.DRAGGING);
                 }
                 if (mState == ScrollState.DRAGGING) {
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index 07ddccb..310d598 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -99,7 +99,6 @@
                 handleLongPress = mTempRect.contains((int) ev.getX(), (int) ev.getY());
             }
 
-            cancelLongPress();
             if (handleLongPress) {
                 mLongPressState = STATE_REQUESTED;
                 mTouchDownPoint.set(ev.getX(), ev.getY());
@@ -148,6 +147,10 @@
             }
         }
 
+        if (action == ACTION_UP || action == ACTION_CANCEL) {
+            cancelLongPress();
+        }
+
         return result;
     }
 
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 7a6da3e..95c9681 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -77,7 +77,7 @@
 public class FloatingIconView extends View implements
         Animator.AnimatorListener, ClipPathView, OnGlobalLayoutListener {
 
-    public static final float SHAPE_PROGRESS_DURATION = 0.15f;
+    public static final float SHAPE_PROGRESS_DURATION = 0.10f;
     private static final int FADE_DURATION_MS = 200;
     private static final Rect sTmpRect = new Rect();
     private static final RectF sTmpRectF = new RectF();
@@ -85,8 +85,8 @@
 
     // We spring the foreground drawable relative to the icon's movement in the DragLayer.
     // We then use these two factor values to scale the movement of the fg within this view.
-    private static final int FG_TRANS_X_FACTOR = 80;
-    private static final int FG_TRANS_Y_FACTOR = 100;
+    private static final int FG_TRANS_X_FACTOR = 60;
+    private static final int FG_TRANS_Y_FACTOR = 75;
 
     private static final FloatPropertyCompat<FloatingIconView> mFgTransYProperty
             = new FloatPropertyCompat<FloatingIconView>("FloatingViewFgTransY") {
@@ -529,7 +529,8 @@
         bounds.inset(mBlurSizeOutline / 2, mBlurSizeOutline / 2);
 
         try (LauncherIcons li = LauncherIcons.obtain(mLauncher)) {
-            Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(drawable, null));
+            Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(drawable, null,
+                    null, null));
         }
 
         bounds.inset(
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 18a8f27..d03035a 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -20,8 +20,10 @@
 
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.widget.TextView;
 
 import androidx.annotation.NonNull;
+import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.BySelector;
 import androidx.test.uiautomator.Direction;
 import androidx.test.uiautomator.UiObject2;
@@ -42,6 +44,10 @@
         super(launcher);
         final UiObject2 allAppsContainer = verifyActiveContainer();
         mHeight = allAppsContainer.getVisibleBounds().height();
+        final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
+                "apps_list_view");
+        // Wait for the recycler to populate.
+        mLauncher.waitForObjectInContainer(appListRecycler, By.clazz(TextView.class));
     }
 
     @Override
@@ -115,7 +121,7 @@
                 verifyActiveContainer();
             }
 
-            final UiObject2 appIcon = mLauncher.getObjectInContainer(allAppsContainer,
+            final UiObject2 appIcon = mLauncher.getObjectInContainer(appListRecycler,
                     appIconSelector);
             ensureIconVisible(appIcon, allAppsContainer, appListRecycler);
             return new AppIcon(mLauncher, appIcon);
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index f7befd1..e45fca8 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -21,9 +21,9 @@
 import static android.content.pm.PackageManager.MATCH_ALL;
 import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
 
+import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName;
 import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
-import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName;
 
 import android.app.ActivityManager;
 import android.app.Instrumentation;
@@ -257,6 +257,7 @@
     }
 
     private void fail(String message) {
+        log("Hierarchy dump for: " + getContextDescription() + message);
         dumpViewHierarchy();
         Assert.fail("http://go/tapl : " + getContextDescription() + message);
     }
@@ -570,6 +571,16 @@
         return object;
     }
 
+    @NonNull
+    UiObject2 waitForObjectInContainer(UiObject2 container, BySelector selector) {
+        final UiObject2 object = container.wait(
+                Until.findObject(selector),
+                WAIT_TIME_MS);
+        assertNotNull("Can't find a launcher object id: " + selector + " in container: " +
+                container.getResourceName(), object);
+        return object;
+    }
+
     @Nullable
     private boolean hasLauncherObject(String resId) {
         return mDevice.hasObject(getLauncherObjectSelector(resId));
@@ -581,11 +592,6 @@
     }
 
     @NonNull
-    UiObject2 waitForLauncherObjectByClass(String clazz) {
-        return waitForObjectBySelector(getLauncherObjectSelectorByClass(clazz));
-    }
-
-    @NonNull
     UiObject2 waitForFallbackLauncherObject(String resName) {
         return waitForObjectBySelector(getFallbackLauncherObjectSelector(resName));
     }
@@ -600,10 +606,6 @@
         return By.res(getLauncherPackageName(), resName);
     }
 
-    BySelector getLauncherObjectSelectorByClass(String clazz) {
-        return By.pkg(getLauncherPackageName()).clazz(clazz);
-    }
-
     BySelector getFallbackLauncherObjectSelector(String resName) {
         return By.res(getOverviewPackageName(), resName);
     }