Merge "Use device locked input consumer when an activity is showing when locked" into ub-launcher3-qt-dev
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 3c55854..db5515b 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;
     }
@@ -212,18 +218,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/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/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index 336cdc9..d84362c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -205,15 +205,6 @@
     }
 
     /**
-     * Clean-up logic that occurs when recents is no longer in use/visible.
-     *
-     * @param launcher the launcher activity
-     */
-    public static void resetOverview(Launcher launcher) {
-        launcher.<RecentsView>getOverviewPanel().reset();
-    }
-
-    /**
      * Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
      *
      * @param launcher the launcher activity
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 8436fe5..f1d6450 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
@@ -21,7 +21,6 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.quickstep.util.ClipAnimationHelper;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
@@ -45,8 +44,6 @@
 
     @Override
     public void onStateEnabled(Launcher launcher) {
-        RecentsView rv = launcher.getOverviewPanel();
-        rv.setOverviewStateEnabled(true);
         AbstractFloatingView.closeAllOpenViews(launcher, false);
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
index cda03dc..9a99c15 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -86,18 +86,10 @@
 
     @Override
     public void onStateEnabled(Launcher launcher) {
-        RecentsView rv = launcher.getOverviewPanel();
-        rv.setOverviewStateEnabled(true);
         AbstractFloatingView.closeAllOpenViews(launcher);
     }
 
     @Override
-    public void onStateDisabled(Launcher launcher) {
-        RecentsView rv = launcher.getOverviewPanel();
-        rv.setOverviewStateEnabled(false);
-    }
-
-    @Override
     public void onStateTransitionEnd(Launcher launcher) {
         launcher.getRotationHelper().setCurrentStateRequest(REQUEST_ROTATE);
         DiscoveryBounce.showForOverviewIfNeeded(launcher);
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/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index 84af109..5104fb8 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -36,8 +36,6 @@
  */
 public class TaskOverlayFactory implements ResourceBasedOverride {
 
-    public static final String AIAI_PACKAGE = "com.google.android.as";
-
     /** Note that these will be shown in order from top to bottom, if available for the task. */
     private static final TaskSystemShortcut[] MENU_OPTIONS = new TaskSystemShortcut[]{
             new TaskSystemShortcut.AppInfo(),
@@ -51,29 +49,33 @@
             new MainThreadInitializedObject<>(c -> Overrides.getObject(TaskOverlayFactory.class,
                     c, R.string.task_overlay_factory_class));
 
+    public List<TaskSystemShortcut> getEnabledShortcuts(TaskView taskView) {
+        final ArrayList<TaskSystemShortcut> shortcuts = new ArrayList<>();
+        final BaseDraggingActivity activity = BaseActivity.fromContext(taskView.getContext());
+        for (TaskSystemShortcut menuOption : MENU_OPTIONS) {
+            View.OnClickListener onClickListener =
+                    menuOption.getOnClickListener(activity, taskView);
+            if (onClickListener != null) {
+                shortcuts.add(menuOption);
+            }
+        }
+        return shortcuts;
+    }
+
     public TaskOverlay createOverlay(View thumbnailView) {
         return new TaskOverlay();
     }
 
     public static class TaskOverlay {
 
-        public void setTaskInfo(Task task, ThumbnailData thumbnail, Matrix matrix) {
-        }
+        /**
+         * Called when the current task is interactive for the user
+         */
+        public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix) { }
 
-        public void reset() {
-        }
-
-        public List<TaskSystemShortcut> getEnabledShortcuts(TaskView taskView) {
-            final ArrayList<TaskSystemShortcut> shortcuts = new ArrayList<>();
-            final BaseDraggingActivity activity = BaseActivity.fromContext(taskView.getContext());
-            for (TaskSystemShortcut menuOption : MENU_OPTIONS) {
-                View.OnClickListener onClickListener =
-                        menuOption.getOnClickListener(activity, taskView);
-                if (onClickListener != null) {
-                    shortcuts.add(menuOption);
-                }
-            }
-            return shortcuts;
-        }
+        /**
+         * Called when the overlay is no longer used.
+         */
+        public void reset() { }
     }
 }
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 c7aaa9b..87b7326 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -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,7 +647,10 @@
     }
 
     private void buildAnimationController() {
-        if (mStateCallback.hasStates(STATE_GESTURE_COMPLETED)) {
+        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());
@@ -1276,12 +1279,7 @@
     }
 
     private void invalidateHandlerWithLauncher() {
-        if (mLauncherTransitionController != null) {
-            if (mLauncherTransitionController.getAnimationPlayer().isStarted()) {
-                mLauncherTransitionController.getAnimationPlayer().cancel();
-            }
-            mLauncherTransitionController = null;
-        }
+        endLauncherTransitionController();
 
         mRecentsView.onGestureAnimationEnd();
 
@@ -1289,6 +1287,13 @@
         mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
     }
 
+    private void endLauncherTransitionController() {
+        if (mLauncherTransitionController != null) {
+            mLauncherTransitionController.getAnimationPlayer().end();
+            mLauncherTransitionController = null;
+        }
+    }
+
     private void notifyTransitionCancelled() {
         mAnimationFactory.onTransitionCancelled();
     }
@@ -1390,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/fallback/FallbackRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
index e254e24..a5aa1bf 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -35,6 +35,7 @@
     public FallbackRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         setOverviewStateEnabled(true);
+        setOverlayEnabled(true);
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index deedd21..4162845 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -20,6 +20,7 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherState.RECENTS_CLEAR_ALL_BUTTON;
+import static com.android.launcher3.LauncherState.SPRING_LOADED;
 import static com.android.launcher3.QuickstepAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
@@ -30,7 +31,6 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.os.Build;
 import android.util.AttributeSet;
 import android.view.View;
@@ -40,6 +40,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager.StateListener;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.appprediction.PredictionUiStateManager;
@@ -57,7 +58,7 @@
  * {@link RecentsView} used in Launcher activity
  */
 @TargetApi(Build.VERSION_CODES.O)
-public class LauncherRecentsView extends RecentsView<Launcher> {
+public class LauncherRecentsView extends RecentsView<Launcher> implements StateListener {
 
     private final TransformParams mTransformParams = new TransformParams();
     private final int mChipOverhang;
@@ -75,6 +76,7 @@
         super(context, attrs, defStyleAttr);
         setContentAlpha(0);
         mChipOverhang = (int) context.getResources().getDimension(R.dimen.chip_hint_overhang);
+        mActivity.getStateManager().addStateListener(this);
     }
 
     @Override
@@ -287,6 +289,20 @@
     }
 
     @Override
+    public void onStateTransitionStart(LauncherState toState) {
+        setOverviewStateEnabled(toState.overviewUi);
+    }
+
+    @Override
+    public void onStateTransitionComplete(LauncherState finalState) {
+        if (finalState == NORMAL || finalState == SPRING_LOADED) {
+            // Clean-up logic that occurs when recents is no longer in use/visible.
+            reset();
+        }
+        setOverlayEnabled(finalState == OVERVIEW);
+    }
+
+    @Override
     public void setOverviewStateEnabled(boolean enabled) {
         super.setOverviewStateEnabled(enabled);
         if (enabled) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 1e1007e..38bd461 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -72,6 +72,9 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.ListView;
 
+import androidx.annotation.Nullable;
+import androidx.dynamicanimation.animation.SpringForce;
+
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
@@ -111,9 +114,6 @@
 import java.util.ArrayList;
 import java.util.function.Consumer;
 
-import androidx.annotation.Nullable;
-import androidx.dynamicanimation.animation.SpringForce;
-
 /**
  * A list of recent tasks.
  */
@@ -168,8 +168,6 @@
     // The threshold at which we update the SystemUI flags when animating from the task into the app
     public static final float UPDATE_SYSUI_FLAGS_THRESHOLD = 0.85f;
 
-    private static final float[] sTempFloatArray = new float[3];
-
     protected final T mActivity;
     private final float mFastFlingVelocity;
     private final RecentsModel mModel;
@@ -189,6 +187,7 @@
 
     private boolean mDwbToastShown;
     private boolean mDisallowScrollToClearAll;
+    private boolean mOverlayEnabled;
 
     /**
      * TODO: Call reloadIdNeeded in onTaskStackChanged.
@@ -428,11 +427,7 @@
         // Clear the task data for the removed child if it was visible
         if (child != mClearAllButton) {
             TaskView taskView = (TaskView) child;
-            Task task = taskView.getTask();
-            if (mHasVisibleTaskData.get(task.key.id)) {
-                mHasVisibleTaskData.delete(task.key.id);
-                taskView.onTaskListVisibilityChanged(false /* visible */);
-            }
+            mHasVisibleTaskData.delete(taskView.getTask().key.id);
             mTaskViewPool.recycle(taskView);
         }
     }
@@ -455,7 +450,6 @@
     public void setOverviewStateEnabled(boolean enabled) {
         mOverviewStateEnabled = enabled;
         updateTaskStackListenerState();
-        if (!enabled) mDwbToastShown = false;
     }
 
     public void onDigitalWellbeingToastShown() {
@@ -582,6 +576,7 @@
         }
         resetTaskVisuals();
         onTaskStackUpdated();
+        updateEnabledOverlays();
     }
 
     public int getTaskViewCount() {
@@ -780,6 +775,7 @@
 
         unloadVisibleTaskData();
         setCurrentPage(0);
+        mDwbToastShown = false;
     }
 
     public @Nullable TaskView getRunningTaskView() {
@@ -1545,6 +1541,7 @@
     protected void notifyPageSwitchListener(int prevPage) {
         super.notifyPageSwitchListener(prevPage);
         loadVisibleTaskData();
+        updateEnabledOverlays();
     }
 
     @Override
@@ -1685,4 +1682,19 @@
     public ClipAnimationHelper getTempClipAnimationHelper() {
         return mTempClipAnimationHelper;
     }
+
+    private void updateEnabledOverlays() {
+        int overlayEnabledPage = mOverlayEnabled ? getNextPage() : -1;
+        int taskCount = getTaskViewCount();
+        for (int i = 0; i < taskCount; i++) {
+            ((TaskView) getChildAt(i)).setOverlayEnabled(i == overlayEnabledPage);
+        }
+    }
+
+    public void setOverlayEnabled(boolean overlayEnabled) {
+        if (mOverlayEnabled != overlayEnabled) {
+            mOverlayEnabled = overlayEnabled;
+            updateEnabledOverlays();
+        }
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
index c47e943..c1f6b82 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
@@ -42,6 +42,7 @@
 import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.TaskOverlayFactory;
 import com.android.quickstep.TaskSystemShortcut;
 import com.android.quickstep.TaskUtils;
 import com.android.quickstep.views.IconView.OnScaleUpdateListener;
@@ -196,7 +197,7 @@
 
         final BaseDraggingActivity activity = BaseDraggingActivity.fromContext(getContext());
         final List<TaskSystemShortcut> shortcuts =
-                taskView.getTaskOverlay().getEnabledShortcuts(taskView);
+                TaskOverlayFactory.INSTANCE.get(getContext()).getEnabledShortcuts(taskView);
         final int count = shortcuts.size();
         for (int i = 0; i < count; ++i) {
             final TaskSystemShortcut menuOption = shortcuts.get(i);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
index df5831b..8b8240d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -98,6 +98,9 @@
     private float mDimAlphaMultiplier = 1f;
     private float mSaturation = 1f;
 
+    private boolean mOverlayEnabled;
+    private boolean mRotated;
+
     public TaskThumbnailView(Context context) {
         this(context, null);
     }
@@ -192,10 +195,6 @@
         return 0;
     }
 
-    public TaskOverlay getTaskOverlay() {
-        return mOverlay;
-    }
-
     @Override
     protected void onDraw(Canvas canvas) {
         RectF currentDrawnInsets = mFullscreenParams.mCurrentDrawnInsets;
@@ -257,6 +256,22 @@
         return (TaskView) getParent();
     }
 
+    public void setOverlayEnabled(boolean overlayEnabled) {
+        if (mOverlayEnabled != overlayEnabled) {
+            mOverlayEnabled = overlayEnabled;
+            updateOverlay();
+        }
+    }
+
+    private void updateOverlay() {
+        // The overlay doesn't really work when the screenshot is rotated, so don't add it.
+        if (mOverlayEnabled && !mRotated && mBitmapShader != null && mThumbnailData != null) {
+            mOverlay.initOverlay(mTask, mThumbnailData, mMatrix);
+        } else {
+            mOverlay.reset();
+        }
+    }
+
     private void updateThumbnailPaintFilter() {
         int mul = (int) ((1 - mDimAlpha * mDimAlphaMultiplier) * 255);
         ColorFilter filter = getColorFilter(mul, mIsDarkTextTheme, mSaturation);
@@ -351,12 +366,8 @@
             mPaint.setShader(mBitmapShader);
         }
 
-        if (isRotated) {
-            // The overlay doesn't really work when the screenshot is rotated, so don't add it.
-            mOverlay.reset();
-        } else {
-            mOverlay.setTaskInfo(mTask, mThumbnailData, mMatrix);
-        }
+        mRotated = isRotated;
+        updateOverlay();
         invalidate();
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index f8d454f..bf3e91f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -39,7 +39,6 @@
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Log;
-import android.util.Property;
 import android.view.View;
 import android.view.ViewOutlineProvider;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -253,10 +252,6 @@
         return mIconView;
     }
 
-    public TaskOverlayFactory.TaskOverlay getTaskOverlay() {
-        return mSnapshotView.getTaskOverlay();
-    }
-
     public AnimatorPlaybackController createLaunchAnimationForRunningTask() {
         final PendingAnimation pendingAnimation =
                 getRecentsView().createTaskLauncherAnimation(this, RECENTS_LAUNCH_DURATION);
@@ -488,6 +483,8 @@
         // Clear any references to the thumbnail (it will be re-read either from the cache or the
         // system on next bind)
         mSnapshotView.setThumbnail(mTask, null);
+        setOverlayEnabled(false);
+        onTaskListVisibilityChanged(false);
         if (mTask != null) {
             mTask.thumbnail = null;
         }
@@ -608,7 +605,7 @@
 
         final Context context = getContext();
         final List<TaskSystemShortcut> shortcuts =
-                mSnapshotView.getTaskOverlay().getEnabledShortcuts(this);
+                TaskOverlayFactory.INSTANCE.get(getContext()).getEnabledShortcuts(this);
         final int count = shortcuts.size();
         for (int i = 0; i < count; ++i) {
             final TaskSystemShortcut menuOption = shortcuts.get(i);
@@ -647,7 +644,7 @@
         }
 
         final List<TaskSystemShortcut> shortcuts =
-                mSnapshotView.getTaskOverlay().getEnabledShortcuts(this);
+                TaskOverlayFactory.INSTANCE.get(getContext()).getEnabledShortcuts(this);
         final int count = shortcuts.size();
         for (int i = 0; i < count; ++i) {
             final TaskSystemShortcut menuOption = shortcuts.get(i);
@@ -733,6 +730,10 @@
         return mShowScreenshot;
     }
 
+    public void setOverlayEnabled(boolean overlayEnabled) {
+        mSnapshotView.setOverlayEnabled(overlayEnabled);
+    }
+
     /**
      * We update and subsequently draw these in {@link #setFullscreenProgress(float)}.
      */
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/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/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 3a92dfb..c63f976 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -258,9 +258,6 @@
      * Called when the start transition ends and the user settles on this particular state.
      */
     public void onStateTransitionEnd(Launcher launcher) {
-        if (this == NORMAL || this == SPRING_LOADED) {
-            UiFactory.resetOverview(launcher);
-        }
         if (this == NORMAL) {
             // Clear any rotation locks when going to normal state
             launcher.getRotationHelper().setCurrentStateRequest(REQUEST_NONE);
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/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/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 e4591b6..95c9681 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -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(