Merge "Adding support for overriding hotseat Widget" into ub-launcher3-qt-dev
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index d20910f..1b24fc8 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -52,12 +52,19 @@
     public void onStateEnabled(Launcher launcher) {
         IconRecentsView recentsView = launcher.getOverviewPanel();
         recentsView.onBeginTransitionToOverview();
+        recentsView.setShowStatusBarForegroundScrim(true);
         // Request orientation be set to unspecified, letting the system decide the best
         // orientation.
         launcher.getRotationHelper().setCurrentStateRequest(REQUEST_ROTATE);
     }
 
     @Override
+    public void onStateDisabled(Launcher launcher) {
+        IconRecentsView recentsView = launcher.getOverviewPanel();
+        recentsView.setShowStatusBarForegroundScrim(false);
+    }
+
+    @Override
     public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
         return new PageAlphaProvider(DEACCEL_2) {
             @Override
diff --git a/go/quickstep/src/com/android/quickstep/RecentsActivity.java b/go/quickstep/src/com/android/quickstep/RecentsActivity.java
index f2ca368..9fb8067 100644
--- a/go/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/go/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -37,6 +37,7 @@
         mRecentsRootView = findViewById(R.id.drag_layer);
         mIconRecentsView = findViewById(R.id.overview_panel);
         mIconRecentsView.setRecentsToActivityHelper(new FallbackRecentsToActivityHelper(this));
+        mIconRecentsView.setShowStatusBarForegroundScrim(true);
     }
 
     @Override
diff --git a/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java b/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java
index 7686543..57f49d6 100644
--- a/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java
+++ b/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java
@@ -26,16 +26,18 @@
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
+import java.util.function.Consumer;
+
 /**
  * Callback for swipe input on {@link TaskHolder} views in the recents view.
  */
 public final class TaskSwipeCallback extends ItemTouchHelper.SimpleCallback {
 
-    private final TaskActionController mTaskActionController;
+    private final Consumer<TaskHolder> mOnTaskSwipeCallback;
 
-    public TaskSwipeCallback(TaskActionController taskActionController) {
+    public TaskSwipeCallback(Consumer<TaskHolder> onTaskSwipeCallback) {
         super(0 /* dragDirs */, RIGHT);
-        mTaskActionController = taskActionController;
+        mOnTaskSwipeCallback = onTaskSwipeCallback;
     }
 
     @Override
@@ -47,7 +49,7 @@
     @Override
     public void onSwiped(ViewHolder viewHolder, int direction) {
         if (direction == RIGHT) {
-            mTaskActionController.removeTask((TaskHolder) viewHolder);
+            mOnTaskSwipeCallback.accept((TaskHolder) viewHolder);
         }
     }
 
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index f951304..7225e57 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -33,6 +33,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
@@ -54,6 +55,7 @@
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
 import com.android.quickstep.ContentFillItemAnimator;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.RecentsToActivityHelper;
@@ -114,6 +116,8 @@
     private final DefaultItemAnimator mDefaultItemAnimator = new DefaultItemAnimator();
     private final ContentFillItemAnimator mLoadingContentItemAnimator =
             new ContentFillItemAnimator();
+    private final BaseActivity mActivity;
+    private final Drawable mStatusBarForegroundScrim;
 
     private RecentsToActivityHelper mActivityHelper;
     private RecyclerView mTaskRecyclerView;
@@ -143,8 +147,10 @@
 
     public IconRecentsView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        BaseActivity activity = BaseActivity.fromContext(context);
+        mActivity = BaseActivity.fromContext(context);
         mContext = context;
+        mStatusBarForegroundScrim  =
+                Themes.getAttrDrawable(mContext, R.attr.workspaceStatusBarScrim);
         mTaskLoader = new TaskListLoader(mContext);
         mTaskAdapter = new TaskAdapter(mTaskLoader);
         mTaskAdapter.setOnClearAllClickListener(view -> animateClearAllTasks());
@@ -162,7 +168,12 @@
             mTaskRecyclerView.setAdapter(mTaskAdapter);
             mTaskRecyclerView.setLayoutManager(mTaskLayoutManager);
             ItemTouchHelper helper = new ItemTouchHelper(
-                    new TaskSwipeCallback(mTaskActionController));
+                    new TaskSwipeCallback(holder -> {
+                        mTaskActionController.removeTask(holder);
+                        if (mTaskLoader.getCurrentTaskList().isEmpty()) {
+                            mActivityHelper.leaveRecents();
+                        }
+                    }));
             helper.attachToRecyclerView(mTaskRecyclerView);
             mTaskRecyclerView.addOnChildAttachStateChangeListener(
                     new OnChildAttachStateChangeListener() {
@@ -339,6 +350,21 @@
     }
 
     /**
+     * Set whether or not to show the scrim in between the view and the top insets. This only works
+     * if the view is being insetted in the first place.
+     *
+     * The scrim is added to the activity's root view to prevent animations on this view
+     * affecting the scrim. As a result, it is the activity's responsibility to show/hide this
+     * scrim as appropriate.
+     *
+     * @param showStatusBarForegroundScrim true to show the scrim, false to hide
+     */
+    public void setShowStatusBarForegroundScrim(boolean showStatusBarForegroundScrim) {
+        boolean shouldShow = mInsets.top != 0 && showStatusBarForegroundScrim;
+        mActivity.getDragLayer().setForeground(shouldShow ? mStatusBarForegroundScrim : null);
+    }
+
+    /**
      * Get the bottom most thumbnail view to animate to.
      *
      * @return the thumbnail view if laid out
@@ -438,7 +464,6 @@
         if (mShowingContentView != mEmptyView && taskListSize == 0) {
             mShowingContentView = mEmptyView;
             crossfadeViews(mEmptyView, mContentView);
-            mActivityHelper.leaveRecents();
         }
         if (mShowingContentView != mContentView && taskListSize > 0) {
             mShowingContentView = mContentView;
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
index a5f5728..6db8013 100644
--- a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
+++ b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
@@ -49,6 +49,7 @@
     private ImageView mIconView;
     private ImageView mThumbnailView;
     private float mContentTransitionProgress;
+    private int mDisplayedOrientation;
 
     /**
      * Property representing the content transition progress of the view. 1.0f represents that the
@@ -179,13 +180,27 @@
     }
 
     @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        onOrientationChanged(getResources().getConfiguration().orientation);
+    }
+
+    @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
+        onOrientationChanged(newConfig.orientation);
+    }
+
+    private void onOrientationChanged(int newOrientation) {
+        if (mDisplayedOrientation == newOrientation) {
+            return;
+        }
+        mDisplayedOrientation = newOrientation;
         int layerCount = mThumbnailDrawable.getNumberOfLayers();
         for (int i = 0; i < layerCount; i++) {
             Drawable drawable = mThumbnailDrawable.getDrawable(i);
             if (drawable instanceof ThumbnailDrawable) {
-                ((ThumbnailDrawable) drawable).setRequestedOrientation(newConfig.orientation);
+                ((ThumbnailDrawable) drawable).setRequestedOrientation(newOrientation);
             }
         }
         mTaskIconThumbnailView.forceLayout();
diff --git a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
index ab4b64c..3c55854 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
@@ -78,7 +78,7 @@
 
     public IconNormalizer getNormalizer() {
         if (mNormalizer == null) {
-            mNormalizer = new IconNormalizer(mContext, mIconBitmapSize);
+            mNormalizer = new IconNormalizer(mIconBitmapSize);
         }
         return mNormalizer;
     }
diff --git a/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java b/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java
index 11d5eef..3e818a5 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java
@@ -16,6 +16,9 @@
 package com.android.launcher3.icons;
 
 import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.RegionIterator;
 import android.util.Log;
 
 import java.io.ByteArrayOutputStream;
@@ -60,4 +63,14 @@
             return null;
         }
     }
+
+    public static int getArea(Region r) {
+        RegionIterator itr = new RegionIterator(r);
+        int area = 0;
+        Rect tempRect = new Rect();
+        while (itr.next(tempRect)) {
+            area += tempRect.width() * tempRect.height();
+        }
+        return area;
+    }
 }
diff --git a/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java b/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java
index 05908df..4a2a7cf 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java
@@ -16,14 +16,18 @@
 
 package com.android.launcher3.icons;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Path;
 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 java.nio.ByteBuffer;
 
@@ -57,7 +61,7 @@
     private final Canvas mCanvas;
     private final byte[] mPixels;
 
-    private final Rect mAdaptiveIconBounds;
+    private final RectF mAdaptiveIconBounds;
     private float mAdaptiveIconScale;
 
     // for each y, stores the position of the leftmost x and the rightmost x
@@ -66,7 +70,7 @@
     private final Rect mBounds;
 
     /** package private **/
-    IconNormalizer(Context context, int iconBitmapSize) {
+    IconNormalizer(int iconBitmapSize) {
         // 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);
@@ -75,11 +79,53 @@
         mLeftBorder = new float[mMaxSize];
         mRightBorder = new float[mMaxSize];
         mBounds = new Rect();
-        mAdaptiveIconBounds = new Rect();
+        mAdaptiveIconBounds = new RectF();
 
         mAdaptiveIconScale = SCALE_NOT_INITIALIZED;
     }
 
+    private static float getScale(float hullArea, float boundingArea, float fullArea) {
+        float hullByRect = hullArea / boundingArea;
+        float scaleRequired;
+        if (hullByRect < CIRCLE_AREA_BY_RECT) {
+            scaleRequired = MAX_CIRCLE_AREA_FACTOR;
+        } else {
+            scaleRequired = MAX_SQUARE_AREA_FACTOR + LINEAR_SCALE_SLOPE * (1 - hullByRect);
+        }
+
+        float areaScale = hullArea / fullArea;
+        // Use sqrt of the final ratio as the images is scaled across both width and height.
+        return areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1;
+    }
+
+    /**
+     * @param d Should be AdaptiveIconDrawable
+     * @param size Canvas size to use
+     */
+    @TargetApi(Build.VERSION_CODES.O)
+    public static float normalizeAdaptiveIcon(Drawable d, int size, @Nullable RectF outBounds) {
+        Rect tmpBounds = new Rect(d.getBounds());
+        d.setBounds(0, 0, size, size);
+
+        Path path = ((AdaptiveIconDrawable) d).getIconMask();
+        Region region = new Region();
+        region.setPath(path, new Region(0, 0, size, size));
+
+        Rect hullBounds = region.getBounds();
+        int hullArea = GraphicsUtils.getArea(region);
+
+        if (outBounds != null) {
+            float sizeF = size;
+            outBounds.set(
+                    hullBounds.left / sizeF,
+                    hullBounds.top / sizeF,
+                    1 - (hullBounds.right / sizeF),
+                    1 - (hullBounds.bottom / sizeF));
+        }
+        d.setBounds(tmpBounds);
+        return getScale(hullArea, hullArea, size * size);
+    }
+
     /**
      * 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.
@@ -96,12 +142,13 @@
      */
     public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds) {
         if (BaseIconFactory.ATLEAST_OREO && d instanceof AdaptiveIconDrawable) {
-            if (mAdaptiveIconScale != SCALE_NOT_INITIALIZED) {
-                if (outBounds != null) {
-                    outBounds.set(mAdaptiveIconBounds);
-                }
-                return mAdaptiveIconScale;
+            if (mAdaptiveIconScale == SCALE_NOT_INITIALIZED) {
+                mAdaptiveIconScale = normalizeAdaptiveIcon(d, mMaxSize, mAdaptiveIconBounds);
             }
+            if (outBounds != null) {
+                outBounds.set(mAdaptiveIconBounds);
+            }
+            return mAdaptiveIconScale;
         }
         int width = d.getIntrinsicWidth();
         int height = d.getIntrinsicHeight();
@@ -184,16 +231,6 @@
             area += mRightBorder[y] - mLeftBorder[y] + 1;
         }
 
-        // Area of the rectangle required to fit the convex hull
-        float rectArea = (bottomY + 1 - topY) * (rightX + 1 - leftX);
-        float hullByRect = area / rectArea;
-
-        float scaleRequired;
-        if (hullByRect < CIRCLE_AREA_BY_RECT) {
-            scaleRequired = MAX_CIRCLE_AREA_FACTOR;
-        } else {
-            scaleRequired = MAX_SQUARE_AREA_FACTOR + LINEAR_SCALE_SLOPE * (1 - hullByRect);
-        }
         mBounds.left = leftX;
         mBounds.right = rightX;
 
@@ -205,15 +242,10 @@
                     1 - ((float) mBounds.right) / width,
                     1 - ((float) mBounds.bottom) / height);
         }
-        float areaScale = area / (width * height);
-        // Use sqrt of the final ratio as the images is scaled across both width and height.
-        float scale = areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1;
-        if (BaseIconFactory.ATLEAST_OREO && d instanceof AdaptiveIconDrawable &&
-                mAdaptiveIconScale == SCALE_NOT_INITIALIZED) {
-            mAdaptiveIconScale = scale;
-            mAdaptiveIconBounds.set(mBounds);
-        }
-        return scale;
+
+        // 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/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index 624b3dc..5e77e0a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -68,7 +68,7 @@
      * @param wasVisible true if it was visible before
      */
     boolean onActivityReady(T activity, Boolean wasVisible) {
-        activity.<RecentsView>getOverviewPanel().setCurrentTask(mTargetTaskId);
+        activity.<RecentsView>getOverviewPanel().showCurrentTask(mTargetTaskId);
         AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
         ActivityControlHelper.AnimationFactory factory =
                 mHelper.prepareRecentsUI(activity, wasVisible,
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
index 7fc5d50..db377b0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
@@ -21,7 +21,6 @@
 import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
 import static android.view.MotionEvent.INVALID_POINTER_ID;
-
 import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 import static com.android.launcher3.util.RaceConditionTracker.ENTER;
 import static com.android.launcher3.util.RaceConditionTracker.EXIT;
@@ -44,6 +43,8 @@
 import android.view.ViewConfiguration;
 import android.view.WindowManager;
 
+import androidx.annotation.UiThread;
+
 import com.android.launcher3.R;
 import com.android.launcher3.graphics.RotationMode;
 import com.android.launcher3.util.Preconditions;
@@ -62,8 +63,6 @@
 
 import java.util.function.Consumer;
 
-import androidx.annotation.UiThread;
-
 /**
  * Input consumer for handling events originating from an activity other than Launcher
  */
@@ -108,7 +107,6 @@
 
     // Might be displacement in X or Y, depending on the direction we are swiping from the nav bar.
     private float mStartDisplacement;
-    private float mStartDisplacementX;
 
     private Handler mMainThreadHandler;
     private Runnable mCancelRecentsAnimationRunnable = () -> {
@@ -227,7 +225,6 @@
                         if (Math.abs(displacement) > mDragSlop) {
                             mPassedDragSlop = true;
                             mStartDisplacement = displacement;
-                            mStartDisplacementX = displacementX;
                         }
                     }
                 }
@@ -244,19 +241,20 @@
                         if (!mPassedDragSlop) {
                             mPassedDragSlop = true;
                             mStartDisplacement = displacement;
-                            mStartDisplacementX = displacementX;
                         }
                         notifyGestureStarted();
                     }
                 }
 
-                if (mPassedDragSlop && mInteractionHandler != null) {
-                    // Move
-                    mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
+                if (mInteractionHandler != null) {
+                    if (mPassedDragSlop) {
+                        // Move
+                        mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
+                    }
 
                     if (mMode == Mode.NO_BUTTON) {
-                        float horizontalDist = Math.abs(displacementX - mStartDisplacementX);
-                        float upDist = -(displacement - mStartDisplacement);
+                        float horizontalDist = Math.abs(displacementX);
+                        float upDist = -displacement;
                         boolean isLikelyToStartNewTask = horizontalDist > upDist;
                         mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement
                                 || isLikelyToStartNewTask);
@@ -382,7 +380,7 @@
             mSwipeSharedState.canGestureBeContinued = endTarget != null && endTarget.canBeContinued;
             mSwipeSharedState.goingToLauncher = endTarget != null && endTarget.isLauncher;
             if (mSwipeSharedState.canGestureBeContinued) {
-                mInteractionHandler.cancelCurrentAnimation();
+                mInteractionHandler.cancelCurrentAnimation(mSwipeSharedState);
             } else {
                 mInteractionHandler.reset();
             }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java
index ced9afa..96d2dca 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java
@@ -59,6 +59,10 @@
         mInputProxySupplier = inputProxySupplier;
     }
 
+    public boolean hasTargets() {
+        return targetSet != null && targetSet.hasTargets();
+    }
+
     @UiThread
     public synchronized void setController(SwipeAnimationTargetSet targetSet) {
         Preconditions.assertUIThread();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
index f393387..194d073 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
@@ -38,6 +38,8 @@
 
     public boolean canGestureBeContinued;
     public boolean goingToLauncher;
+    public boolean recentsAnimationFinishInterrupted;
+    public int nextRunningTaskId = -1;
 
     public SwipeSharedState(OverviewComponentObserver overviewComponentObserver) {
         mOverviewComponentObserver = overviewComponentObserver;
@@ -114,9 +116,22 @@
         }
     }
 
+    /**
+     * Called when a recents animation has finished, but was interrupted before the next task was
+     * launched. The given {@param runningTaskId} should be used as the running task for the
+     * continuing input consumer.
+     */
+    public void setRecentsAnimationFinishInterrupted(int runningTaskId) {
+        recentsAnimationFinishInterrupted = true;
+        nextRunningTaskId = runningTaskId;
+        mLastAnimationTarget = mLastAnimationTarget.cloneWithoutTargets();
+    }
+
     public void clearAllState() {
         clearListenerState();
         canGestureBeContinued = false;
+        recentsAnimationFinishInterrupted = false;
+        nextRunningTaskId = -1;
         goingToLauncher = false;
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
index f95f9c2..d0ea73a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
@@ -150,6 +150,7 @@
 
             @Override
             public void onUpdate(float percent) {
+                // TODO: Take into account the current fullscreen progress for animating the insets
                 params.setProgress(1 - percent);
                 RectF taskBounds = inOutHelper.applyTransform(targetSet, params);
                 if (!skipViewChanges) {
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 b25865e..61ae880 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -26,6 +26,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
 
 import android.annotation.TargetApi;
+import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.KeyguardManager;
 import android.app.Service;
@@ -456,7 +457,7 @@
             return new DeviceLockedInputConsumer(this);
         }
 
-        RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0);
+        final RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0);
         if (!useSharedState) {
             mSwipeSharedState.clearAllState();
         }
@@ -465,8 +466,15 @@
                 mOverviewComponentObserver.getActivityControlHelper();
 
         InputConsumer base;
-        if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher) {
+        if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher
+                && !mSwipeSharedState.recentsAnimationFinishInterrupted) {
             base = InputConsumer.NO_OP;
+        } else if (mSwipeSharedState.recentsAnimationFinishInterrupted) {
+            // If the finish animation was interrupted, then continue using the other activity input
+            // consumer but with the next task as the running task
+            RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
+            info.id = mSwipeSharedState.nextRunningTaskId;
+            base = createOtherActivityInputConsumer(event, info);
         } else if (mSwipeSharedState.goingToLauncher || activityControl.isResumed()) {
             base = OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false);
         } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
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 9c6102e..936f0aa 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -256,7 +256,9 @@
     private AnimationFactory mAnimationFactory = (t) -> { };
     private LiveTileOverlay mLiveTileOverlay = new LiveTileOverlay();
 
+    private boolean mCanceled;
     private boolean mWasLauncherAlreadyVisible;
+    private int mFinishingRecentsAnimationForNewTaskId = -1;
 
     private boolean mPassedOverviewThreshold;
     private boolean mGestureStarted;
@@ -412,7 +414,6 @@
             mRecentsAnimationWrapper.runOnInit(() ->
                     mRecentsAnimationWrapper.targetSet.addDependentTransactionApplier(applier));
             });
-        mRecentsView.setEnableFreeScroll(false);
 
         mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
             if (mGestureEndTarget != HOME) {
@@ -500,10 +501,7 @@
         if (mContinuingLastGesture) {
             return;
         }
-        mRecentsView.setEnableDrawingLiveTile(false);
-        mRecentsView.showTask(mRunningTaskId);
-        mRecentsView.setRunningTaskHidden(true);
-        mRecentsView.setRunningTaskIconScaledDown(true);
+        mRecentsView.onGestureAnimationStart(mRunningTaskId);
     }
 
     private void launcherFrameDrawn() {
@@ -679,7 +677,7 @@
         int runningTaskIndex = mRecentsView == null ? -1 : mRecentsView.getRunningTaskIndex();
         if (runningTaskIndex >= 0) {
             for (int i = 0; i < mRecentsView.getTaskViewCount(); i++) {
-                if (i != runningTaskIndex) {
+                if (i != runningTaskIndex || !mRecentsAnimationWrapper.hasTargets()) {
                     mRecentsView.getTaskViewAt(i).setFullscreenProgress(1 - mCurrentShift.value);
                 }
             }
@@ -838,9 +836,15 @@
         Interpolator interpolator = DEACCEL;
         final boolean goingToNewTask;
         if (mRecentsView != null) {
-            final int runningTaskIndex = mRecentsView.getRunningTaskIndex();
-            final int taskToLaunch = mRecentsView.getNextPage();
-            goingToNewTask = runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex;
+            if (!mRecentsAnimationWrapper.hasTargets()) {
+                // If there are no running tasks, then we can assume that this is a continuation of
+                // the last gesture, but after the recents animation has finished
+                goingToNewTask = true;
+            } else {
+                final int runningTaskIndex = mRecentsView.getRunningTaskIndex();
+                final int taskToLaunch = mRecentsView.getNextPage();
+                goingToNewTask = runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex;
+            }
         } else {
             goingToNewTask = false;
         }
@@ -1061,7 +1065,9 @@
 
         RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect);
         if (isFloatingIconView) {
-            anim.addAnimatorListener((FloatingIconView) floatingView);
+            FloatingIconView fiv = (FloatingIconView) floatingView;
+            anim.addAnimatorListener(fiv);
+            fiv.setOnTargetChangeListener(anim::onTargetPositionChanged);
         }
 
         AnimatorPlaybackController homeAnim = homeAnimationFactory.createActivityAnimationToHome();
@@ -1122,14 +1128,22 @@
             mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false /* animate */,
                     true /* freezeTaskList */);
         } else {
+            int taskId = mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).getTask().key.id;
+            mFinishingRecentsAnimationForNewTaskId = taskId;
             mRecentsAnimationWrapper.finish(true /* toRecents */, () -> {
-                mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(
-                        false /* animate */, true /* freezeTaskList */);
+                if (!mCanceled) {
+                    TaskView nextTask = mRecentsView.getTaskView(taskId);
+                    if (nextTask != null) {
+                        nextTask.launchTask(false /* animate */, true /* freezeTaskList */);
+                        doLogGesture(NEW_TASK);
+                    }
+                    reset();
+                }
+                mCanceled = false;
+                mFinishingRecentsAnimationForNewTaskId = -1;
             });
         }
         TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true);
-        doLogGesture(NEW_TASK);
-        reset();
     }
 
     public void reset() {
@@ -1140,12 +1154,26 @@
      * Cancels any running animation so that the active target can be overriden by a new swipe
      * handle (in case of quick switch).
      */
-    public void cancelCurrentAnimation() {
+    public void cancelCurrentAnimation(SwipeSharedState sharedState) {
+        mCanceled = true;
         mCurrentShift.cancelAnimation();
         if (mLauncherTransitionController != null && mLauncherTransitionController
                 .getAnimationPlayer().isStarted()) {
             mLauncherTransitionController.getAnimationPlayer().cancel();
         }
+
+        if (mFinishingRecentsAnimationForNewTaskId != -1) {
+            // If we are canceling mid-starting a new task, switch to the screenshot since the
+            // recents animation has finished
+            switchToScreenshot();
+            TaskView newRunningTaskView = mRecentsView.getTaskView(
+                    mFinishingRecentsAnimationForNewTaskId);
+            int newRunningTaskId = newRunningTaskView != null
+                    ? newRunningTaskView.getTask().key.id
+                    : -1;
+            mRecentsView.setCurrentTask(newRunningTaskId);
+            sharedState.setRecentsAnimationFinishInterrupted(newRunningTaskId);
+        }
     }
 
     private void invalidateHandler() {
@@ -1167,11 +1195,7 @@
             mLauncherTransitionController = null;
         }
 
-        mRecentsView.setEnableFreeScroll(true);
-        mRecentsView.setRunningTaskIconScaledDown(false);
-        mRecentsView.setOnScrollChangeListener(null);
-        mRecentsView.setRunningTaskHidden(false);
-        mRecentsView.setEnableDrawingLiveTile(true);
+        mRecentsView.onGestureAnimationEnd();
 
         mActivity.getRootView().setOnApplyWindowInsetsListener(null);
         mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
@@ -1192,6 +1216,9 @@
     private void switchToScreenshot() {
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+        } else if (!mRecentsAnimationWrapper.hasTargets()) {
+            // If there are no targets, then we don't need to capture anything
+            setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
         } else {
             boolean finishTransitionPosted = false;
             SwipeAnimationTargetSet controller = mRecentsAnimationWrapper.getController();
@@ -1239,6 +1266,9 @@
     private void finishCurrentTransitionToRecents() {
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
+        } else if (!mRecentsAnimationWrapper.hasTargets()) {
+            // If there are no targets, then there is nothing to finish
+            setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
         } else {
             synchronized (mRecentsAnimationWrapper) {
                 mRecentsAnimationWrapper.finish(true /* toRecents */,
@@ -1265,10 +1295,7 @@
         }
         mActivityControlHelper.onSwipeUpComplete(mActivity);
         mRecentsAnimationWrapper.setCancelWithDeferredScreenshot(true);
-
-        // Animate the first icon.
-        mRecentsView.animateUpRunningTaskIconScale(mLiveTileOverlay.cancelIconAnimation());
-        mRecentsView.setSwipeDownShouldLaunchApp(true);
+        mRecentsView.onSwipeUpAnimationSuccess();
 
         RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG);
 
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 cb2bf1f..3109921 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
@@ -31,11 +31,12 @@
 import android.os.Build;
 import android.os.RemoteException;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.views.RecentsView;
@@ -50,8 +51,6 @@
 
 import java.util.function.BiFunction;
 
-import androidx.annotation.Nullable;
-
 /**
  * Utility class to handle window clip animation
  */
@@ -327,12 +326,14 @@
 
         float scale = mTargetRect.width() / mSourceRect.width();
         float insetProgress = (1 - progress);
+        float windowCornerRadius = mUseRoundedCornersOnWindows
+                ? mWindowCornerRadius : 0;
         ttv.drawOnCanvas(canvas,
                 -mSourceWindowClipInsets.left * insetProgress,
                 -mSourceWindowClipInsets.top * insetProgress,
                 ttv.getMeasuredWidth() + mSourceWindowClipInsets.right * insetProgress,
                 ttv.getMeasuredHeight() + mSourceWindowClipInsets.bottom * insetProgress,
-                Utilities.mapRange(progress, mWindowCornerRadius * scale, ttv.getCornerRadius()));
+                Utilities.mapRange(progress, windowCornerRadius * scale, ttv.getCornerRadius()));
     }
 
     public RectF getTargetRect() {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java
index 7159e7c..40b9c4d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java
@@ -113,6 +113,15 @@
         mCurrentCenterY = mStartRect.centerY();
     }
 
+    public void onTargetPositionChanged() {
+        if (mRectXAnim != null && mRectXAnim.getTargetPosition() != mTargetRect.centerX()) {
+            mRectXAnim.updatePosition(mCurrentCenterX, mTargetRect.centerX());
+        }
+        if (mRectYAnim != null && mRectYAnim.getTargetPosition() != mTargetRect.centerY()) {
+            mRectYAnim.updatePosition(mCurrentCenterY, mTargetRect.centerY());
+        }
+    }
+
     public void addOnUpdateListener(OnUpdateListener onUpdateListener) {
         mOnUpdateListeners.add(onUpdateListener);
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java
index 83973fa..f5a9e8a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java
@@ -53,6 +53,20 @@
         this.mOnFinishListener = onFinishListener;
     }
 
+    public boolean hasTargets() {
+        return unfilteredApps.length != 0;
+    }
+
+    /**
+     * Clones the target set without any actual targets. Used only when continuing a gesture after
+     * the actual recents animation has finished.
+     */
+    public SwipeAnimationTargetSet cloneWithoutTargets() {
+        return new SwipeAnimationTargetSet(controller, new RemoteAnimationTargetCompat[0],
+                homeContentInsets, minimizedHomeBounds, mShouldMinimizeSplitScreen,
+                mOnFinishListener);
+    }
+
     public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint) {
         mOnFinishListener.accept(this);
         BACKGROUND_EXECUTOR.execute(() -> {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewDrawable.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewDrawable.java
index 2ac61c5..bb41e5d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewDrawable.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewDrawable.java
@@ -25,6 +25,7 @@
 import android.util.FloatProperty;
 import android.view.View;
 
+import com.android.launcher3.BaseActivity;
 import com.android.launcher3.Utilities;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskThumbnailView;
@@ -75,6 +76,8 @@
         mThumbnailView = tv.getThumbnail();
         mClipAnimationHelper = new ClipAnimationHelper(parent.getContext());
         mClipAnimationHelper.fromTaskThumbnailView(mThumbnailView, parent);
+        mClipAnimationHelper.prepareAnimation(
+                BaseActivity.fromContext(tv.getContext()).getDeviceProfile(), true /* isOpening */);
     }
 
     public void setProgress(float progress) {
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 b5f90a5..525ead8 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
@@ -744,9 +744,7 @@
     public abstract void startHome();
 
     public void reset() {
-        setRunningTaskViewShowScreenshot(false);
-        mRunningTaskId = -1;
-        mRunningTaskTileHidden = false;
+        setCurrentTask(-1);
         mIgnoreResetTaskId = -1;
         mTaskListChangeId = -1;
 
@@ -757,6 +755,15 @@
         setCurrentPage(0);
     }
 
+    public @Nullable TaskView getRunningTaskView() {
+        return getTaskView(mRunningTaskId);
+    }
+
+    public int getRunningTaskIndex() {
+        TaskView tv = getRunningTaskView();
+        return tv == null ? -1 : indexOfChild(tv);
+    }
+
     /**
      * Reloads the view if anything in recents changed.
      */
@@ -767,14 +774,50 @@
     }
 
     /**
-     * Ensures that the first task in the view represents {@param task} and reloads the view
-     * if needed. This allows the swipe-up gesture to assume that the first tile always
-     * corresponds to the correct task.
-     * All subsequent calls to reload will keep the task as the first item until {@link #reset()}
-     * is called.
-     * Also scrolls the view to this task
+     * Called when a gesture from an app is starting.
      */
-    public void showTask(int runningTaskId) {
+    public void onGestureAnimationStart(int runningTaskId) {
+        // This needs to be called before the other states are set since it can create the task view
+        showCurrentTask(runningTaskId);
+        setEnableFreeScroll(false);
+        setEnableDrawingLiveTile(false);
+        setRunningTaskHidden(true);
+        setRunningTaskIconScaledDown(true);
+    }
+
+    /**
+     * Called only when a swipe-up gesture from an app has completed. Only called after
+     * {@link #onGestureAnimationStart} and {@link #onGestureAnimationEnd()}.
+     */
+    public void onSwipeUpAnimationSuccess() {
+        if (getRunningTaskView() != null) {
+            float startProgress = ENABLE_QUICKSTEP_LIVE_TILE.get()
+                    ? mLiveTileOverlay.cancelIconAnimation()
+                    : 0f;
+            animateUpRunningTaskIconScale(startProgress);
+        }
+        setSwipeDownShouldLaunchApp(true);
+    }
+
+    /**
+     * Called when a gesture from an app has finished.
+     */
+    public void onGestureAnimationEnd() {
+        setEnableFreeScroll(true);
+        setEnableDrawingLiveTile(true);
+        setOnScrollChangeListener(null);
+        setRunningTaskViewShowScreenshot(true);
+        setRunningTaskHidden(false);
+        animateUpRunningTaskIconScale();
+    }
+
+    /**
+     * Creates a task view (if necessary) to represent the task with the {@param runningTaskId}.
+     *
+     * All subsequent calls to reload will keep the task as the first item until {@link #reset()}
+     * is called.  Also scrolls the view to this task.
+     */
+    public void showCurrentTask(int runningTaskId) {
         if (getChildCount() == 0) {
             // Add an empty view for now until the task plan is loaded and applied
             final TaskView taskView = mTaskViewPool.getView();
@@ -789,16 +832,33 @@
                     new ComponentName("", ""), false);
             taskView.bind(mTmpRunningTask);
         }
+
+        boolean runningTaskTileHidden = mRunningTaskTileHidden;
         setCurrentTask(runningTaskId);
+        setCurrentPage(getRunningTaskIndex());
+        setRunningTaskViewShowScreenshot(false);
+        setRunningTaskHidden(runningTaskTileHidden);
+
+        // Reload the task list
+        mTaskListChangeId = mModel.getTasks(this::applyLoadPlan);
     }
 
-    public @Nullable TaskView getRunningTaskView() {
-        return getTaskView(mRunningTaskId);
-    }
+    /**
+     * Sets the running task id, cleaning up the old running task if necessary.
+     * @param runningTaskId
+     */
+    public void setCurrentTask(int runningTaskId) {
+        if (mRunningTaskId == runningTaskId) {
+            return;
+        }
 
-    public int getRunningTaskIndex() {
-        TaskView tv = getRunningTaskView();
-        return tv == null ? -1 : indexOfChild(tv);
+        if (mRunningTaskId != -1) {
+            // Reset the state on the old running task view
+            setRunningTaskIconScaledDown(false);
+            setRunningTaskViewShowScreenshot(true);
+            setRunningTaskHidden(false);
+        }
+        mRunningTaskId = runningTaskId;
     }
 
     /**
@@ -812,27 +872,6 @@
         }
     }
 
-    /**
-     * Similar to {@link #showTask(int)} but does not put any restrictions on the first tile.
-     */
-    public void setCurrentTask(int runningTaskId) {
-        boolean runningTaskTileHidden = mRunningTaskTileHidden;
-        boolean runningTaskIconScaledDown = mRunningTaskIconScaledDown;
-
-        setRunningTaskIconScaledDown(false);
-        setRunningTaskHidden(false);
-        setRunningTaskViewShowScreenshot(true);
-        mRunningTaskId = runningTaskId;
-        setRunningTaskViewShowScreenshot(false);
-        setRunningTaskIconScaledDown(runningTaskIconScaledDown);
-        setRunningTaskHidden(runningTaskTileHidden);
-
-        setCurrentPage(getRunningTaskIndex());
-
-        // Load the tasks (if the loading is already
-        mTaskListChangeId = mModel.getTasks(this::applyLoadPlan);
-    }
-
     private void setRunningTaskViewShowScreenshot(boolean showScreenshot) {
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             TaskView runningTaskView = getRunningTaskView();
@@ -1520,7 +1559,9 @@
     public abstract boolean shouldUseMultiWindowTaskSizeStrategy();
 
     protected void onTaskLaunched(boolean success) {
-        resetTaskVisuals();
+        if (success) {
+            resetTaskVisuals();
+        }
     }
 
     @Override
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 1117855..7e15d52 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
@@ -79,8 +79,8 @@
     private final BaseActivity mActivity;
     private final TaskOverlay mOverlay;
     private final boolean mIsDarkTextTheme;
-    private final Paint mPaint = new Paint();
-    private final Paint mBackgroundPaint = new Paint();
+    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     private final Paint mClearPaint = new Paint();
     private final Paint mDimmingPaintAfterClearing = new Paint();
 
@@ -210,9 +210,9 @@
                 mCurrentDrawnCornerRadius);
     }
 
-    public Rect getInsetsToDrawInFullscreen() {
-        // Don't show insets in the wrong orientation.
-        return mIsRotated ? EMPTY_RECT : mScaledInsets;
+    public Rect getInsetsToDrawInFullscreen(boolean isMultiWindowMode) {
+        // Don't show insets in the wrong orientation or in multi window mode.
+        return mIsRotated || isMultiWindowMode ? EMPTY_RECT : mScaledInsets;
     }
 
     public void setCurrentDrawnInsetsAndRadius(Rect insets, float radius) {
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 c448f7a..6cd46d9 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
@@ -17,7 +17,7 @@
 package com.android.quickstep.views;
 
 import static android.widget.Toast.LENGTH_SHORT;
-import static com.android.launcher3.BaseActivity.fromContext;
+
 import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -167,8 +167,9 @@
     private float mZoomScale;
     private float mFullscreenProgress;
     private final Rect mCurrentDrawnInsets = new Rect();
-    private float mCornerRadius;
-    private float mWindowCornerRadius;
+    private final float mCornerRadius;
+    private final float mWindowCornerRadius;
+    private final BaseDraggingActivity mActivity;
 
     private ObjectAnimator mIconAndDimAnimator;
     private float mIconScaleAnimStartProgress = 0;
@@ -190,6 +191,7 @@
 
     public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
+        mActivity = BaseDraggingActivity.fromContext(context);
         setOnClickListener((view) -> {
             if (getTask() == null) {
                 return;
@@ -204,10 +206,10 @@
                 launchTask(true /* animate */);
             }
 
-            fromContext(context).getUserEventDispatcher().logTaskLaunchOrDismiss(
+            mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
                     Touch.TAP, Direction.NONE, getRecentsView().indexOfChild(this),
                     TaskUtils.getLaunchComponentKeyForTask(getTask().key));
-            fromContext(context).getStatsLogManager().logTaskLaunch(getRecentsView(),
+            mActivity.getStatsLogManager().logTaskLaunch(getRecentsView(),
                     TaskUtils.getLaunchComponentKeyForTask(getTask().key));
         });
         mCornerRadius = TaskCornerRadius.get(context);
@@ -306,8 +308,7 @@
         if (mTask != null) {
             final ActivityOptions opts;
             if (animate) {
-                opts = ((BaseDraggingActivity) fromContext(getContext()))
-                        .getActivityLaunchOptions(this);
+                opts = mActivity.getActivityLaunchOptions(this);
                 if (freezeTaskList) {
                     ActivityOptionsCompat.setFreezeRecentTasksList(opts);
                 }
@@ -571,13 +572,12 @@
                         getContext().getText(R.string.accessibility_close_task)));
 
         final Context context = getContext();
-        final BaseDraggingActivity activity = fromContext(context);
         final List<TaskSystemShortcut> shortcuts =
                 mSnapshotView.getTaskOverlay().getEnabledShortcuts(this);
         final int count = shortcuts.size();
         for (int i = 0; i < count; ++i) {
             final TaskSystemShortcut menuOption = shortcuts.get(i);
-            OnClickListener onClickListener = menuOption.getOnClickListener(activity, this);
+            OnClickListener onClickListener = menuOption.getOnClickListener(mActivity, this);
             if (onClickListener != null) {
                 info.addAction(menuOption.createAccessibilityAction(context));
             }
@@ -617,8 +617,7 @@
         for (int i = 0; i < count; ++i) {
             final TaskSystemShortcut menuOption = shortcuts.get(i);
             if (menuOption.hasHandlerForAction(action)) {
-                OnClickListener onClickListener = menuOption.getOnClickListener(
-                        fromContext(getContext()), this);
+                OnClickListener onClickListener = menuOption.getOnClickListener(mActivity, this);
                 if (onClickListener != null) {
                     onClickListener.onClick(this);
                 }
@@ -658,13 +657,15 @@
         setClipToPadding(!isFullscreen);
 
         TaskThumbnailView thumbnail = getThumbnail();
-        Rect insets = thumbnail.getInsetsToDrawInFullscreen();
+        boolean isMultiWindowMode = mActivity.getDeviceProfile().isMultiWindowMode;
+        Rect insets = thumbnail.getInsetsToDrawInFullscreen(isMultiWindowMode);
         mCurrentDrawnInsets.set((int) (insets.left * mFullscreenProgress),
                 (int) (insets.top * mFullscreenProgress),
                 (int) (insets.right * mFullscreenProgress),
                 (int) (insets.bottom * mFullscreenProgress));
+        float fullscreenCornerRadius = isMultiWindowMode ? 0 : mWindowCornerRadius;
         float cornerRadius = Utilities.mapRange(mFullscreenProgress, mCornerRadius,
-                mWindowCornerRadius) / getRecentsView().getScaleX();
+                fullscreenCornerRadius) / getRecentsView().getScaleX();
 
         thumbnail.setCurrentDrawnInsetsAndRadius(mCurrentDrawnInsets, cornerRadius);
         mOutlineProvider.setCurrentDrawnInsetsAndRadius(mCurrentDrawnInsets, cornerRadius);
diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
index e028fcd..1817135 100644
--- a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
+++ b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
@@ -85,6 +85,7 @@
      * Test that prediction UI is updated as soon as we get predictions from the system
      */
     @Test
+    @Ignore // b/131772711: this really fails (when being run as a part of the whole test suite)!
     public void testPredictionExistsInAllApps() {
         mActivityMonitor.startLauncher();
         mLauncher.pressHome().switchToAllApps();
@@ -117,7 +118,7 @@
     }
 
     @Test
-    @Ignore
+    @Ignore // b/131772711 - this was failing in the lab
     public void testPredictionsDisabled() {
         mActivityMonitor.startLauncher();
         sendPredictionUpdate();
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 960d907..20fdff2 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -44,7 +44,6 @@
 import com.android.launcher3.testcomponent.TestCommandReceiver;
 import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
 
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TestRule;
@@ -98,7 +97,6 @@
 
     @NavigationModeSwitch(mode = THREE_BUTTON)
     @Test
-    @Ignore // b/131630813
     public void goToOverviewFromHome() {
         mDevice.pressHome();
         assertTrue("Fallback Launcher not visible", mDevice.wait(Until.hasObject(By.pkg(
@@ -109,7 +107,6 @@
 
     @NavigationModeSwitch(mode = THREE_BUTTON)
     @Test
-    @Ignore // b/131630813
     public void goToOverviewFromApp() {
         startAppFast("com.android.settings");
 
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index 93e403c..e552f56 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -71,8 +71,7 @@
 
     @Override
     public Statement apply(Statement base, Description description) {
-        // b/130558787; b/131419978
-        if (false && TestHelpers.isInLauncherProcess() &&
+        if (TestHelpers.isInLauncherProcess() &&
                 description.getAnnotation(NavigationModeSwitch.class) != null) {
             Mode mode = description.getAnnotation(NavigationModeSwitch.class).mode();
             return new Statement() {
diff --git a/robolectric_tests/Android.mk b/robolectric_tests/Android.mk
index a57b97a..62915f2 100644
--- a/robolectric_tests/Android.mk
+++ b/robolectric_tests/Android.mk
@@ -46,6 +46,8 @@
 LOCAL_JAVA_LIBRARIES := \
     LauncherRoboTests
 
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
 LOCAL_TEST_PACKAGE := Launcher3
 
 LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))../src \
diff --git a/robolectric_tests/res/values/overlayable_icons_test.xml b/robolectric_tests/res/values/overlayable_icons_test.xml
new file mode 100644
index 0000000..5144e52
--- /dev/null
+++ b/robolectric_tests/res/values/overlayable_icons_test.xml
@@ -0,0 +1,36 @@
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<resources>
+    <!-- overlayable_icons references all of the drawables in this package
+         that are being overlayed by resource overlays. If you remove/rename
+         any of these resources, you must also change the resource overlay icons.-->
+    <array name="overlayable_icons">
+        <item>@drawable/ic_corp</item>
+        <item>@drawable/ic_drag_handle</item>
+        <item>@drawable/ic_hourglass_top</item>
+        <item>@drawable/ic_info_no_shadow</item>
+        <item>@drawable/ic_install_no_shadow</item>
+        <item>@drawable/ic_palette</item>
+        <item>@drawable/ic_pin</item>
+        <item>@drawable/ic_remove_no_shadow</item>
+        <item>@drawable/ic_setting</item>
+        <item>@drawable/ic_smartspace_preferences</item>
+        <item>@drawable/ic_split_screen</item>
+        <item>@drawable/ic_uninstall_no_shadow</item>
+        <item>@drawable/ic_warning</item>
+        <item>@drawable/ic_widget</item>
+    </array>
+</resources>
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index fe6bbc0..3eb01e6 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -48,6 +48,9 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 
+import androidx.annotation.IntDef;
+import androidx.core.view.ViewCompat;
+
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
 import com.android.launcher3.accessibility.FolderAccessibilityHelper;
@@ -75,9 +78,6 @@
 import java.util.Comparator;
 import java.util.Stack;
 
-import androidx.annotation.IntDef;
-import androidx.core.view.ViewCompat;
-
 public class CellLayout extends ViewGroup implements Transposable {
     public static final int WORKSPACE_ACCESSIBILITY_DRAG = 2;
     public static final int FOLDER_ACCESSIBILITY_DRAG = 1;
@@ -360,6 +360,10 @@
         mShortcutsAndWidgets.setLayerType(hasLayer ? LAYER_TYPE_HARDWARE : LAYER_TYPE_NONE, sPaint);
     }
 
+    public boolean isHardwareLayerEnabled() {
+        return mShortcutsAndWidgets.getLayerType() == LAYER_TYPE_HARDWARE;
+    }
+
     public void setCellDimensions(int width, int height) {
         mFixedCellWidth = mCellWidth = width;
         mFixedCellHeight = mCellHeight = height;
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 3e7f67b..819a551 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -42,7 +42,7 @@
 import android.view.Display;
 import android.view.WindowManager;
 
-import com.android.launcher3.folder.FolderShape;
+import com.android.launcher3.graphics.IconShape;
 import com.android.launcher3.util.ConfigMonitor;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.MainThreadInitializedObject;
@@ -304,7 +304,7 @@
             changeFlags |= CHANGE_FLAG_ICON_PARAMS;
         }
         if (!iconShapePath.equals(oldProfile.iconShapePath)) {
-            FolderShape.init(context);
+            IconShape.init(context);
         }
 
         apply(context, changeFlags);
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index b24f660..f5040b3 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -553,6 +553,8 @@
         cancelAnimation();
         if (reapplyNeeded) {
             reapplyState();
+            // Dispatch on transition end, so that any transient property is cleared.
+            onStateTransitionEnd(mState);
         }
         mConfig.setAnimation(anim, null);
     }
diff --git a/src/com/android/launcher3/MainProcessInitializer.java b/src/com/android/launcher3/MainProcessInitializer.java
index 93df025..95ee687 100644
--- a/src/com/android/launcher3/MainProcessInitializer.java
+++ b/src/com/android/launcher3/MainProcessInitializer.java
@@ -19,7 +19,7 @@
 import android.content.Context;
 
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.folder.FolderShape;
+import com.android.launcher3.graphics.IconShape;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.util.ResourceBasedOverride;
 
@@ -38,6 +38,6 @@
         FileLog.setDir(context.getApplicationContext().getFilesDir());
         FeatureFlags.initialize(context);
         SessionCommitReceiver.applyDefaultUserPrefs(context);
-        FolderShape.init(context);
+        IconShape.init(context);
     }
 }
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index b640430..80e17c9 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1072,6 +1072,10 @@
 
 
     public void setEnableFreeScroll(boolean freeScroll) {
+        if (mFreeScroll == freeScroll) {
+            return;
+        }
+
         boolean wasFreeScroll = mFreeScroll;
         mFreeScroll = freeScroll;
 
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 26364be..35b967f 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -167,7 +167,7 @@
     public static float getDescendantCoordRelativeToAncestor(
             View descendant, View ancestor, float[] coord, boolean includeRootScroll) {
         return getDescendantCoordRelativeToAncestor(descendant, ancestor, coord, includeRootScroll,
-                false);
+                false, null);
     }
 
     /**
@@ -180,12 +180,15 @@
      * @param includeRootScroll Whether or not to account for the scroll of the descendant:
      *          sometimes this is relevant as in a child's coordinates within the descendant.
      * @param ignoreTransform If true, view transform is ignored
+     * @param outRotation If not null, and {@param ignoreTransform} is true, this is set to the
+     *                   overall rotation of the view in degrees.
      * @return The factor by which this descendant is scaled relative to this DragLayer. Caution
      *         this scale factor is assumed to be equal in X and Y, and so if at any point this
      *         assumption fails, we will need to return a pair of scale factors.
      */
     public static float getDescendantCoordRelativeToAncestor(View descendant, View ancestor,
-            float[] coord, boolean includeRootScroll, boolean ignoreTransform) {
+            float[] coord, boolean includeRootScroll, boolean ignoreTransform,
+            float[] outRotation) {
         float scale = 1.0f;
         View v = descendant;
         while(v != ancestor && v != null) {
@@ -201,6 +204,10 @@
                     if (m.isTransposed) {
                         sMatrix.setRotate(m.surfaceRotation, v.getPivotX(), v.getPivotY());
                         sMatrix.mapPoints(coord);
+
+                        if (outRotation != null) {
+                            outRotation[0] += m.surfaceRotation;
+                        }
                     }
                 }
             } else {
diff --git a/src/com/android/launcher3/anim/FlingSpringAnim.java b/src/com/android/launcher3/anim/FlingSpringAnim.java
index 45d49e8..bb0f855 100644
--- a/src/com/android/launcher3/anim/FlingSpringAnim.java
+++ b/src/com/android/launcher3/anim/FlingSpringAnim.java
@@ -36,6 +36,8 @@
     private final FlingAnimation mFlingAnim;
     private SpringAnimation mSpringAnim;
 
+    private float mTargetPosition;
+
     public <K> FlingSpringAnim(K object, FloatPropertyCompat<K> property, float startPosition,
             float targetPosition, float startVelocity, OnAnimationEndListener onEndListener) {
         mFlingAnim = new FlingAnimation(object, property)
@@ -44,10 +46,12 @@
                 .setStartVelocity(startVelocity)
                 .setMinValue(Math.min(startPosition, targetPosition))
                 .setMaxValue(Math.max(startPosition, targetPosition));
+        mTargetPosition = targetPosition;
+
         mFlingAnim.addEndListener(((animation, canceled, value, velocity) -> {
             mSpringAnim = new SpringAnimation(object, property)
                     .setStartVelocity(velocity)
-                    .setSpring(new SpringForce(targetPosition)
+                    .setSpring(new SpringForce(mTargetPosition)
                             .setStiffness(SPRING_STIFFNESS)
                             .setDampingRatio(SPRING_DAMPING));
             mSpringAnim.addEndListener(onEndListener);
@@ -55,6 +59,19 @@
         }));
     }
 
+    public float getTargetPosition() {
+        return mTargetPosition;
+    }
+
+    public void updatePosition(float startPosition, float targetPosition) {
+        mFlingAnim.setMinValue(Math.min(startPosition, targetPosition))
+                .setMaxValue(Math.max(startPosition, targetPosition));
+        mTargetPosition = targetPosition;
+        if (mSpringAnim != null) {
+            mSpringAnim.animateToFinalPosition(mTargetPosition);
+        }
+    }
+
     public void start() {
         mFlingAnim.start();
     }
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index f2eae17..389e852 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -24,6 +24,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.annotation.SuppressLint;
+import android.appwidget.AppWidgetHostView;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
@@ -61,8 +62,10 @@
 import com.android.launcher3.OnAlarmListener;
 import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.Workspace;
 import com.android.launcher3.Workspace.ItemOperator;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragController;
@@ -121,6 +124,7 @@
     private static final int REORDER_DELAY = 250;
     private static final int ON_EXIT_CLOSE_DELAY = 400;
     private static final Rect sTempRect = new Rect();
+    private static final int MIN_FOLDERS_FOR_HARDWARE_OPTIMIZATION = 10;
 
     private static String sDefaultFolderName;
     private static String sHintText;
@@ -430,21 +434,44 @@
         if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
             mCurrentAnimator.cancel();
         }
+        final Workspace workspace = mLauncher.getWorkspace();
+        final CellLayout currentCellLayout =
+                (CellLayout) workspace.getChildAt(workspace.getCurrentPage());
+        final boolean useHardware = shouldUseHardwareLayerForAnimation(currentCellLayout);
+        final boolean wasHardwareAccelerated = currentCellLayout.isHardwareLayerEnabled();
+
         a.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
+                if (useHardware) {
+                    currentCellLayout.enableHardwareLayer(true);
+                }
                 mState = STATE_ANIMATING;
                 mCurrentAnimator = a;
             }
 
             @Override
             public void onAnimationEnd(Animator animation) {
+                if (useHardware) {
+                    currentCellLayout.enableHardwareLayer(wasHardwareAccelerated);
+                }
                 mCurrentAnimator = null;
             }
         });
         a.start();
     }
 
+    private boolean shouldUseHardwareLayerForAnimation(CellLayout currentCellLayout) {
+        int folderCount = 0;
+        final ShortcutAndWidgetContainer container = currentCellLayout.getShortcutsAndWidgets();
+        for (int i = container.getChildCount() - 1; i >= 0; --i) {
+            final View child = container.getChildAt(i);
+            if (child instanceof AppWidgetHostView) return false;
+            if (child instanceof FolderIcon) ++folderCount;
+        }
+        return folderCount >= MIN_FOLDERS_FOR_HARDWARE_OPTIMIZATION;
+    }
+
     /**
      * Opens the user folder described by the specified tag. The opening of the folder
      * is animated relative to the specified View. If the View is null, no animation
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 2461e28..2450039 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -19,7 +19,7 @@
 import static com.android.launcher3.BubbleTextView.TEXT_ALPHA_PROPERTY;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
-import static com.android.launcher3.folder.FolderShape.getShape;
+import static com.android.launcher3.graphics.IconShape.getShape;
 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
 
 import android.animation.Animator;
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index 46df77a..09e8276 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -16,7 +16,7 @@
 
 package com.android.launcher3.folder;
 
-import static com.android.launcher3.folder.FolderShape.getShape;
+import static com.android.launcher3.graphics.IconShape.getShape;
 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
 
 import android.animation.Animator;
@@ -311,7 +311,7 @@
 
     public Path getClipPath() {
         mPath.reset();
-        getShape().addShape(mPath, getOffsetX(), getOffsetY(), getScaledRadius());
+        getShape().addToPath(mPath, getOffsetX(), getOffsetY(), getScaledRadius());
         return mPath;
     }
 
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
index ce83a17..c9566cb 100644
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -16,15 +16,15 @@
 
 package com.android.launcher3.graphics;
 
+import static com.android.launcher3.graphics.IconShape.getShapePath;
+
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.Path;
 import android.graphics.Rect;
-import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Process;
@@ -34,7 +34,6 @@
 import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.ResourceBasedOverride;
@@ -47,16 +46,8 @@
 public class DrawableFactory implements ResourceBasedOverride {
 
     public static final MainThreadInitializedObject<DrawableFactory> INSTANCE =
-            new MainThreadInitializedObject<>(c -> {
-                DrawableFactory factory = Overrides.getObject(DrawableFactory.class,
-                        c.getApplicationContext(), R.string.drawable_factory_class);
-                factory.mContext = c;
-                return factory;
-            });
-
-
-    private Context mContext;
-    private Path mPreloadProgressPath;
+            new MainThreadInitializedObject<>(c -> Overrides.getObject(DrawableFactory.class,
+                        c.getApplicationContext(), R.string.drawable_factory_class));
 
     protected final UserHandle mMyUser = Process.myUserHandle();
     protected final ArrayMap<UserHandle, Bitmap> mUserBadges = new ArrayMap<>();
@@ -66,7 +57,7 @@
      */
     public FastBitmapDrawable newIcon(Context context, ItemInfoWithIcon info) {
         FastBitmapDrawable drawable = info.usingLowResIcon()
-                ? new PlaceHolderIconDrawable(info, getPreloadProgressPath(), context)
+                ? new PlaceHolderIconDrawable(info, getShapePath(), context)
                 : new FastBitmapDrawable(info);
         drawable.setIsDisabled(info.isDisabled());
         return drawable;
@@ -74,7 +65,7 @@
 
     public FastBitmapDrawable newIcon(Context context, BitmapInfo info, ActivityInfo target) {
         return info.isLowRes()
-                ? new PlaceHolderIconDrawable(info, getPreloadProgressPath(), context)
+                ? new PlaceHolderIconDrawable(info, getShapePath(), context)
                 : new FastBitmapDrawable(info);
     }
 
@@ -82,29 +73,7 @@
      * Returns a FastBitmapDrawable with the icon.
      */
     public PreloadIconDrawable newPendingIcon(Context context, ItemInfoWithIcon info) {
-        return new PreloadIconDrawable(info, getPreloadProgressPath(), context);
-    }
-
-    protected Path getPreloadProgressPath() {
-        if (mPreloadProgressPath != null) {
-            return mPreloadProgressPath;
-        }
-        if (Utilities.ATLEAST_OREO) {
-            // Load the path from Mask Icon
-            AdaptiveIconDrawable icon = (AdaptiveIconDrawable)
-                    mContext.getDrawable(R.drawable.adaptive_icon_drawable_wrapper);
-            icon.setBounds(0, 0,
-                    PreloadIconDrawable.PATH_SIZE, PreloadIconDrawable.PATH_SIZE);
-            mPreloadProgressPath = icon.getIconMask();
-        } else {
-
-            // Create a circle static from top center and going clockwise.
-            Path p = new Path();
-            p.moveTo(PreloadIconDrawable.PATH_SIZE / 2, 0);
-            p.addArc(0, 0, PreloadIconDrawable.PATH_SIZE, PreloadIconDrawable.PATH_SIZE, -90, 360);
-            mPreloadProgressPath = p;
-        }
-        return mPreloadProgressPath;
+        return new PreloadIconDrawable(info, getShapePath(), context);
     }
 
     /**
diff --git a/src/com/android/launcher3/folder/FolderShape.java b/src/com/android/launcher3/graphics/IconShape.java
similarity index 86%
rename from src/com/android/launcher3/folder/FolderShape.java
rename to src/com/android/launcher3/graphics/IconShape.java
index ec6078e..88e4452 100644
--- a/src/com/android/launcher3/folder/FolderShape.java
+++ b/src/com/android/launcher3/graphics/IconShape.java
@@ -13,7 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.folder;
+package com.android.launcher3.graphics;
+
+import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -31,7 +33,6 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.Region.Op;
-import android.graphics.RegionIterator;
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.os.Build;
@@ -45,6 +46,8 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.icons.GraphicsUtils;
+import com.android.launcher3.icons.IconNormalizer;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ClipPathView;
@@ -59,22 +62,39 @@
 import androidx.annotation.Nullable;
 
 /**
- * Abstract representation of the shape of a folder icon
+ * Abstract representation of the shape of an icon shape
  */
-public abstract class FolderShape {
+public abstract class IconShape {
 
-    private static FolderShape sInstance = new Circle();
+    private static IconShape sInstance = new Circle();
+    private static Path sShapePath;
+    private static float sNormalizationScale = ICON_VISIBLE_AREA_FACTOR;
 
-    public static FolderShape getShape() {
+    public static final int DEFAULT_PATH_SIZE = 100;
+
+    public static IconShape getShape() {
         return sInstance;
     }
 
+    public static Path getShapePath() {
+        if (sShapePath == null) {
+            Path p = new Path();
+            getShape().addToPath(p, 0, 0, DEFAULT_PATH_SIZE * 0.5f);
+            sShapePath = p;
+        }
+        return sShapePath;
+    }
+
+    public static float getNormalizationScale() {
+        return sNormalizationScale;
+    }
+
     private SparseArray<TypedValue> mAttrs;
 
     public abstract void drawShape(Canvas canvas, float offsetX, float offsetY, float radius,
             Paint paint);
 
-    public abstract void addShape(Path path, float offsetX, float offsetY, float radius);
+    public abstract void addToPath(Path path, float offsetX, float offsetY, float radius);
 
     public abstract <T extends View & ClipPathView> Animator createRevealAnimator(T target,
             Rect startRect, Rect endRect, float endRadius, boolean isReversed);
@@ -87,7 +107,7 @@
     /**
      * Abstract shape where the reveal animation is a derivative of a round rect animation
      */
-    private static abstract class SimpleRectShape extends FolderShape {
+    private static abstract class SimpleRectShape extends IconShape {
 
         @Override
         public final <T extends View & ClipPathView> Animator createRevealAnimator(T target,
@@ -107,7 +127,7 @@
     /**
      * Abstract shape which draws using {@link Path}
      */
-    private static abstract class PathShape extends FolderShape {
+    private static abstract class PathShape extends IconShape {
 
         private final Path mTmpPath = new Path();
 
@@ -115,7 +135,7 @@
         public final void drawShape(Canvas canvas, float offsetX, float offsetY, float radius,
                 Paint paint) {
             mTmpPath.reset();
-            addShape(mTmpPath, offsetX, offsetY, radius);
+            addToPath(mTmpPath, offsetX, offsetY, radius);
             canvas.drawPath(mTmpPath, paint);
         }
 
@@ -166,7 +186,7 @@
         }
 
         @Override
-        public void addShape(Path path, float offsetX, float offsetY, float radius) {
+        public void addToPath(Path path, float offsetX, float offsetY, float radius) {
             path.addCircle(radius + offsetX, radius + offsetY, radius, Path.Direction.CW);
         }
 
@@ -196,7 +216,7 @@
         }
 
         @Override
-        public void addShape(Path path, float offsetX, float offsetY, float radius) {
+        public void addToPath(Path path, float offsetX, float offsetY, float radius) {
             float cx = radius + offsetX;
             float cy = radius + offsetY;
             float cr = radius * mRadiusRatio;
@@ -223,7 +243,7 @@
         }
 
         @Override
-        public void addShape(Path p, float offsetX, float offsetY, float r1) {
+        public void addToPath(Path p, float offsetX, float offsetY, float r1) {
             float r2 = r1 * mRadiusRatio;
             float cx = r1 + offsetX;
             float cy = r1 + offsetY;
@@ -274,7 +294,7 @@
         }
 
         @Override
-        public void addShape(Path p, float offsetX, float offsetY, float r) {
+        public void addToPath(Path p, float offsetX, float offsetY, float r) {
             float cx = r + offsetX;
             float cy = r + offsetY;
             float control = r - r * mRadiusRatio;
@@ -358,7 +378,7 @@
         pickBestShape(context);
     }
 
-    private static FolderShape getShapeDefinition(String type, float radius) {
+    private static IconShape getShapeDefinition(String type, float radius) {
         switch (type) {
             case "Circle":
                 return new Circle();
@@ -373,8 +393,8 @@
         }
     }
 
-    private static List<FolderShape> getAllShapes(Context context) {
-        ArrayList<FolderShape> result = new ArrayList<>();
+    private static List<IconShape> getAllShapes(Context context) {
+        ArrayList<IconShape> result = new ArrayList<>();
         try (XmlResourceParser parser = context.getResources().getXml(R.xml.folder_shapes)) {
 
             // Find the root tag
@@ -393,7 +413,7 @@
                 if (type == XmlPullParser.START_TAG) {
                     AttributeSet attrs = Xml.asAttributeSet(parser);
                     TypedArray a = context.obtainStyledAttributes(attrs, radiusAttr);
-                    FolderShape shape = getShapeDefinition(parser.getName(), a.getFloat(0, 1));
+                    IconShape shape = getShapeDefinition(parser.getName(), a.getFloat(0, 1));
                     a.recycle();
 
                     shape.mAttrs = Themes.createValueMap(context, attrs, keysToIgnore);
@@ -409,7 +429,7 @@
     @TargetApi(Build.VERSION_CODES.O)
     protected static void pickBestShape(Context context) {
         // Pick any large size
-        int size = 200;
+        final int size = 200;
 
         Region full = new Region(0, 0, size, size);
         Region iconR = new Region();
@@ -420,23 +440,17 @@
 
         Path shapePath = new Path();
         Region shapeR = new Region();
-        Rect tempRect = new Rect();
 
         // Find the shape with minimum area of divergent region.
         int minArea = Integer.MAX_VALUE;
-        FolderShape closestShape = null;
-        for (FolderShape shape : getAllShapes(context)) {
+        IconShape closestShape = null;
+        for (IconShape shape : getAllShapes(context)) {
             shapePath.reset();
-            shape.addShape(shapePath, 0, 0, size / 2f);
+            shape.addToPath(shapePath, 0, 0, size / 2f);
             shapeR.setPath(shapePath, full);
             shapeR.op(iconR, Op.XOR);
 
-            RegionIterator itr = new RegionIterator(shapeR);
-            int area = 0;
-
-            while (itr.next(tempRect)) {
-                area += tempRect.width() * tempRect.height();
-            }
+            int area = GraphicsUtils.getArea(shapeR);
             if (area < minArea) {
                 minArea = area;
                 closestShape = shape;
@@ -446,5 +460,10 @@
         if (closestShape != null) {
             sInstance = closestShape;
         }
+
+        // Initialize shape properties
+        drawable.setBounds(0, 0, DEFAULT_PATH_SIZE, DEFAULT_PATH_SIZE);
+        sShapePath = new Path(drawable.getIconMask());
+        sNormalizationScale = IconNormalizer.normalizeAdaptiveIcon(drawable, size, null);
     }
 }
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index d3a7955..cc4c2ef 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -17,6 +17,8 @@
 
 package com.android.launcher3.graphics;
 
+import static com.android.launcher3.graphics.IconShape.DEFAULT_PATH_SIZE;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
@@ -55,8 +57,6 @@
                 }
             };
 
-    public static final int PATH_SIZE = 100;
-
     private static final float PROGRESS_WIDTH = 7;
     private static final float PROGRESS_GAP = 2;
     private static final int MAX_PAINT_ALPHA = 255;
@@ -123,14 +123,14 @@
     protected void onBoundsChange(Rect bounds) {
         super.onBoundsChange(bounds);
         mTmpMatrix.setScale(
-                (bounds.width() - 2 * PROGRESS_WIDTH - 2 * PROGRESS_GAP) / PATH_SIZE,
-                (bounds.height() - 2 * PROGRESS_WIDTH - 2 * PROGRESS_GAP) / PATH_SIZE);
+                (bounds.width() - 2 * PROGRESS_WIDTH - 2 * PROGRESS_GAP) / DEFAULT_PATH_SIZE,
+                (bounds.height() - 2 * PROGRESS_WIDTH - 2 * PROGRESS_GAP) / DEFAULT_PATH_SIZE);
         mTmpMatrix.postTranslate(
                 bounds.left + PROGRESS_WIDTH + PROGRESS_GAP,
                 bounds.top + PROGRESS_WIDTH + PROGRESS_GAP);
 
         mProgressPath.transform(mTmpMatrix, mScaledTrackPath);
-        float scale = bounds.width() / PATH_SIZE;
+        float scale = bounds.width() / DEFAULT_PATH_SIZE;
         mProgressPaint.setStrokeWidth(PROGRESS_WIDTH * scale);
 
         mShadowBitmap = getShadowBitmap(bounds.width(), bounds.height(),
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 2574665..787c93c 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -15,8 +15,6 @@
  */
 package com.android.launcher3.views;
 
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
 import static com.android.launcher3.Utilities.mapToRange;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
@@ -27,7 +25,6 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
-import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Outline;
@@ -44,6 +41,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.widget.ImageView;
 
 import com.android.launcher3.BubbleTextView;
@@ -56,7 +54,7 @@
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
 import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.folder.FolderShape;
+import com.android.launcher3.graphics.IconShape;
 import com.android.launcher3.graphics.ShiftedBitmapDrawable;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.popup.SystemShortcut;
@@ -69,15 +67,19 @@
  * A view that is created to look like another view with the purpose of creating fluid animations.
  */
 @TargetApi(Build.VERSION_CODES.Q)
-public class FloatingIconView extends View implements Animator.AnimatorListener, ClipPathView {
+public class FloatingIconView extends View implements
+        Animator.AnimatorListener, ClipPathView, OnGlobalLayoutListener {
 
     public static final float SHAPE_PROGRESS_DURATION = 0.15f;
     private static final int FADE_DURATION_MS = 200;
     private static final Rect sTmpRect = new Rect();
+    private static final RectF sTmpRectF = new RectF();
+    private static final Object[] sTmpObjArray = new Object[1];
 
     private Runnable mEndRunnable;
     private CancellationSignal mLoadIconSignal;
 
+    private final Launcher mLauncher;
     private final int mBlurSizeOutline;
 
     private boolean mIsVerticalBarLayout = false;
@@ -85,12 +87,17 @@
 
     private @Nullable Drawable mForeground;
     private @Nullable Drawable mBackground;
+    private float mRotation;
     private ValueAnimator mRevealAnimator;
     private final Rect mStartRevealRect = new Rect();
     private final Rect mEndRevealRect = new Rect();
     private Path mClipPath;
     private float mTaskCornerRadius;
 
+    private View mOriginalIcon;
+    private RectF mPositionOut;
+    private Runnable mOnTargetChangeRunnable;
+
     private final Rect mFinalDrawableBounds = new Rect();
     private final Rect mBgDrawableBounds = new Rect();
     private float mBgDrawableStartScale = 1f;
@@ -99,12 +106,24 @@
     private AnimatorSet mFadeAnimatorSet;
     private ListenerView mListenerView;
 
-    private FloatingIconView(Context context) {
-        super(context);
-
-        mBlurSizeOutline = context.getResources().getDimensionPixelSize(
+    private FloatingIconView(Launcher launcher) {
+        super(launcher);
+        mLauncher = launcher;
+        mBlurSizeOutline = getResources().getDimensionPixelSize(
                 R.dimen.blur_size_medium_outline);
-        mListenerView = new ListenerView(context, null);
+        mListenerView = new ListenerView(launcher, null);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        getViewTreeObserver().addOnGlobalLayoutListener(this);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        getViewTreeObserver().removeOnGlobalLayoutListener(this);
+        super.onDetachedFromWindow();
     }
 
     /**
@@ -144,7 +163,7 @@
         mTaskCornerRadius = cornerRadius;
         if (mIsAdaptiveIcon && shapeRevealProgress >= 0) {
             if (mRevealAnimator == null) {
-                mRevealAnimator = (ValueAnimator) FolderShape.getShape().createRevealAnimator(this,
+                mRevealAnimator = (ValueAnimator) IconShape.getShape().createRevealAnimator(this,
                         mStartRevealRect, mEndRevealRect, mTaskCornerRadius / scale, !isOpening);
                 mRevealAnimator.addListener(new AnimatorListenerAdapter() {
                     @Override
@@ -188,85 +207,78 @@
      * @param v The view to copy
      * @param positionOut Rect that will hold the size and position of v.
      */
-    private void matchPositionOf(Launcher launcher, View v, RectF positionOut) {
-        getLocationBoundsForView(launcher, v, positionOut);
+    private void matchPositionOf(View v, RectF positionOut) {
+        float rotation = getLocationBoundsForView(v, positionOut);
         final LayoutParams lp = new LayoutParams(
                 Math.round(positionOut.width()),
                 Math.round(positionOut.height()));
-        lp.ignoreInsets = true;
-
-        // Position the floating view exactly on top of the original
-        lp.leftMargin = Math.round(positionOut.left);
-        lp.topMargin = Math.round(positionOut.top);
+        updatePosition(rotation, positionOut, lp);
         setLayoutParams(lp);
+    }
+
+    private void updatePosition(float rotation, RectF position, LayoutParams lp) {
+        mRotation = rotation;
+        mPositionOut.set(position);
+        lp.ignoreInsets = true;
+        // Position the floating view exactly on top of the original
+        lp.leftMargin = Math.round(position.left);
+        lp.topMargin = Math.round(position.top);
+
         // Set the properties here already to make sure they are available when running the first
         // animation frame.
         layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin
                 + lp.height);
+
     }
 
     /**
-     * Returns the location bounds of a view.
+     * Gets the location bounds of a view and returns the overall rotation.
      * - For DeepShortcutView, we return the bounds of the icon view.
      * - For BubbleTextView, we return the icon bounds.
      */
-    private void getLocationBoundsForView(Launcher launcher, View v, RectF outRect) {
-        final boolean isBubbleTextView = v instanceof BubbleTextView;
-        final boolean isFolderIcon = v instanceof FolderIcon;
-
-        // Deep shortcut views have their icon drawn in a separate view.
-        final boolean fromDeepShortcutView = v.getParent() instanceof DeepShortcutView;
-
-        final View targetView;
-        boolean ignoreTransform = false;
-
+    private float getLocationBoundsForView(View v, RectF outRect) {
+        boolean ignoreTransform = true;
         if (v instanceof DeepShortcutView) {
-            targetView = ((DeepShortcutView) v).getIconView();
-        } else if (fromDeepShortcutView) {
-            DeepShortcutView view = (DeepShortcutView) v.getParent();
-            targetView = view.getIconView();
-        } else if ((isBubbleTextView || isFolderIcon) && v.getTag() instanceof ItemInfo
-                && (((ItemInfo) v.getTag()).container == CONTAINER_DESKTOP
-                || ((ItemInfo) v.getTag()).container == CONTAINER_HOTSEAT)) {
-            targetView = v;
-            ignoreTransform = true;
-        } else {
-            targetView = v;
+            v = ((DeepShortcutView) v).getBubbleText();
+            ignoreTransform = false;
+        } else if (v.getParent() instanceof DeepShortcutView) {
+            v = ((DeepShortcutView) v.getParent()).getIconView();
+            ignoreTransform = false;
+        }
+        if (v == null) {
+            return 0;
         }
 
-        float[] points = new float[] {0, 0, targetView.getWidth(), targetView.getHeight()};
-        Utilities.getDescendantCoordRelativeToAncestor(targetView, launcher.getDragLayer(), points,
-                false, ignoreTransform);
-
-        float viewLocationLeft = Math.min(points[0], points[2]);
-        float viewLocationTop = Math.min(points[1], points[3]);
-
-        final Rect iconRect = new Rect();
-        if (isBubbleTextView && !fromDeepShortcutView) {
-            ((BubbleTextView) v).getIconBounds(iconRect);
-        } else if (isFolderIcon) {
-            ((FolderIcon) v).getPreviewBounds(iconRect);
+        Rect iconBounds = new Rect();
+        if (v instanceof BubbleTextView) {
+            ((BubbleTextView) v).getIconBounds(iconBounds);
+        } else if (v instanceof FolderIcon) {
+            ((FolderIcon) v).getPreviewBounds(iconBounds);
         } else {
-            iconRect.set(0, 0, Math.abs(Math.round(points[2] - points[0])),
-                    Math.abs(Math.round(points[3] - points[1])));
+            iconBounds.set(0, 0, v.getWidth(), v.getHeight());
         }
-        viewLocationLeft += iconRect.left;
-        viewLocationTop += iconRect.top;
-        outRect.set(viewLocationLeft, viewLocationTop, viewLocationLeft + iconRect.width(),
-                viewLocationTop + iconRect.height());
+
+        float[] points = new float[] {iconBounds.left, iconBounds.top, iconBounds.right,
+                iconBounds.bottom};
+        float[] rotation = new float[] {0};
+        Utilities.getDescendantCoordRelativeToAncestor(v, mLauncher.getDragLayer(), points,
+                false, ignoreTransform, rotation);
+        outRect.set(
+                Math.min(points[0], points[2]),
+                Math.min(points[1], points[3]),
+                Math.max(points[0], points[2]),
+                Math.max(points[1], points[3]));
+        return rotation[0];
     }
 
     @WorkerThread
-    private void getIcon(Launcher launcher, View v, ItemInfo info, boolean isOpening,
+    private void getIcon(View v, ItemInfo info, boolean isOpening,
             Runnable onIconLoadedRunnable, CancellationSignal loadIconSignal) {
         final LayoutParams lp = (LayoutParams) getLayoutParams();
         Drawable drawable = null;
         boolean supportsAdaptiveIcons = ADAPTIVE_ICON_WINDOW_ANIM.get()
                 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
-        if (!supportsAdaptiveIcons && v instanceof BubbleTextView) {
-            // Similar to DragView, we simply use the BubbleTextView icon here.
-            drawable = ((BubbleTextView) v).getIcon();
-        }
+        Drawable btvIcon = v instanceof BubbleTextView ? ((BubbleTextView) v).getIcon() : null;
         if (info instanceof SystemShortcut) {
             if (v instanceof ImageView) {
                 drawable = ((ImageView) v).getDrawable();
@@ -275,10 +287,24 @@
             } else {
                 drawable = v.getBackground();
             }
-        }
-        if (drawable == null) {
-            drawable = Utilities.getFullDrawable(launcher, info, lp.width, lp.height,
-                    false, new Object[1]);
+        } else {
+            if (supportsAdaptiveIcons) {
+                drawable = Utilities.getFullDrawable(mLauncher, info, lp.width, lp.height,
+                        false, sTmpObjArray);
+                if (!(drawable instanceof AdaptiveIconDrawable)) {
+                    // The drawable we get back is not an adaptive icon, so we need to use the
+                    // BubbleTextView icon that is already legacy treated.
+                    drawable = btvIcon;
+                }
+            } else {
+                if (v instanceof BubbleTextView) {
+                    // Similar to DragView, we simply use the BubbleTextView icon here.
+                    drawable = btvIcon;
+                } else {
+                    drawable = Utilities.getFullDrawable(mLauncher, info, lp.width, lp.height,
+                            false, sTmpObjArray);
+                }
+            }
         }
 
         Drawable finalDrawable = drawable == null ? null
@@ -328,7 +354,7 @@
                     mStartRevealRect.inset(mBlurSizeOutline, mBlurSizeOutline);
                 }
 
-                float aspectRatio = launcher.getDeviceProfile().aspectRatio;
+                float aspectRatio = mLauncher.getDeviceProfile().aspectRatio;
                 if (mIsVerticalBarLayout) {
                     lp.width = (int) Math.max(lp.width, lp.height * aspectRatio);
                 } else {
@@ -395,7 +421,7 @@
         Rect bounds = new Rect(0, 0, lp.width + mBlurSizeOutline, lp.height + mBlurSizeOutline);
         bounds.inset(mBlurSizeOutline / 2, mBlurSizeOutline / 2);
 
-        try (LauncherIcons li = LauncherIcons.obtain(Launcher.fromContext(getContext()))) {
+        try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
             Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(drawable, null));
         }
 
@@ -413,27 +439,22 @@
         invalidate();
     }
 
-    private void drawAdaptiveIconIfExists(Canvas canvas) {
+    @Override
+    public void draw(Canvas canvas) {
+        int count = canvas.save();
+        canvas.rotate(mRotation,
+                mFinalDrawableBounds.exactCenterX(), mFinalDrawableBounds.exactCenterY());
+        if (mClipPath != null) {
+            canvas.clipPath(mClipPath);
+        }
+        super.draw(canvas);
         if (mBackground != null) {
             mBackground.draw(canvas);
         }
         if (mForeground != null) {
             mForeground.draw(canvas);
         }
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        if (mClipPath == null) {
-            super.draw(canvas);
-            drawAdaptiveIconIfExists(canvas);
-        } else {
-            int count = canvas.save();
-            canvas.clipPath(mClipPath);
-            super.draw(canvas);
-            drawAdaptiveIconIfExists(canvas);
-            canvas.restoreToCount(count);
-        }
+        canvas.restoreToCount(count);
     }
 
     public void onListenerViewClosed() {
@@ -457,6 +478,23 @@
     @Override
     public void onAnimationRepeat(Animator animator) {}
 
+    @Override
+    public void onGlobalLayout() {
+        if (mOriginalIcon.isAttachedToWindow() && mPositionOut != null) {
+            float rotation = getLocationBoundsForView(mOriginalIcon, sTmpRectF);
+            if (rotation != mRotation || !sTmpRectF.equals(mPositionOut)) {
+                updatePosition(rotation, sTmpRectF, (LayoutParams) getLayoutParams());
+                if (mOnTargetChangeRunnable != null) {
+                    mOnTargetChangeRunnable.run();
+                }
+            }
+        }
+    }
+
+    public void setOnTargetChangeListener(Runnable onTargetChangeListener) {
+        mOnTargetChangeRunnable = onTargetChangeListener;
+    }
+
     /**
      * Creates a floating icon view for {@param originalView}.
      * @param originalView The view to copy
@@ -473,8 +511,10 @@
         FloatingIconView view = recycle != null ? recycle : new FloatingIconView(launcher);
         view.mIsVerticalBarLayout = launcher.getDeviceProfile().isVerticalBarLayout();
 
+        view.mOriginalIcon = originalView;
+        view.mPositionOut = positionOut;
         // Match the position of the original view.
-        view.matchPositionOf(launcher, originalView, positionOut);
+        view.matchPositionOf(originalView, positionOut);
 
         // Get the drawable on the background thread
         // Must be called after matchPositionOf so that we know what size to load.
@@ -487,7 +527,7 @@
             };
             CancellationSignal loadIconSignal = view.mLoadIconSignal;
             new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(() -> {
-                view.getIcon(launcher, originalView, (ItemInfo) originalView.getTag(), isOpening,
+                view.getIcon(originalView, (ItemInfo) originalView.getTag(), isOpening,
                         onIconLoaded, loadIconSignal);
             });
         }
@@ -585,7 +625,10 @@
         if (mFadeAnimatorSet != null) {
             mFadeAnimatorSet.cancel();
         }
+        mPositionOut = null;
         mFadeAnimatorSet = null;
         mListenerView.setListener(null);
+        mOriginalIcon = null;
+        mOnTargetChangeRunnable = null;
     }
 }
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 43bdb9f..75db2f1 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -18,9 +18,6 @@
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
 
 import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
-import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_2BUTTON_OVERLAY;
-import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_3BUTTON_OVERLAY;
-import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_GESTURAL_OVERLAY;
 
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -35,7 +32,6 @@
 import android.content.IntentFilter;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.PackageManager;
-import android.os.Build;
 import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
@@ -64,7 +60,6 @@
 import com.android.launcher3.util.rule.ShellCommandRule;
 
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.rules.TestRule;
@@ -75,7 +70,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
-import java.lang.reflect.Method;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -111,68 +105,6 @@
         }
         if (TestHelpers.isInLauncherProcess()) Utilities.enableRunningInTestHarnessForTests();
         mLauncher = new LauncherInstrumentation(instrumentation);
-
-        // b/130558787; b/131419978
-        if (TestHelpers.isInLauncherProcess() && !LauncherInstrumentation.needSlowGestures()) {
-            try {
-                Class systemProps = Class.forName("android.os.SystemProperties");
-                Method getInt = systemProps.getMethod("getInt", String.class, int.class);
-                int apiLevel = (int) getInt.invoke(null, "ro.product.first_api_level", 0);
-
-                if (apiLevel >= Build.VERSION_CODES.P) {
-                    setActiveOverlay(NAV_BAR_MODE_2BUTTON_OVERLAY,
-                            LauncherInstrumentation.NavigationModel.TWO_BUTTON);
-                }
-                if (apiLevel >= Build.VERSION_CODES.O && apiLevel < Build.VERSION_CODES.P) {
-                    setActiveOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY,
-                            LauncherInstrumentation.NavigationModel.ZERO_BUTTON);
-                }
-                if (apiLevel < Build.VERSION_CODES.O) {
-                    setActiveOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY,
-                            LauncherInstrumentation.NavigationModel.THREE_BUTTON);
-                }
-            } catch (Throwable e) {
-                e.printStackTrace();
-            }
-        }
-    }
-
-    public void setActiveOverlay(String overlayPackage,
-            LauncherInstrumentation.NavigationModel expectedMode) {
-        setOverlayPackageEnabled(NAV_BAR_MODE_3BUTTON_OVERLAY,
-                overlayPackage == NAV_BAR_MODE_3BUTTON_OVERLAY);
-        setOverlayPackageEnabled(NAV_BAR_MODE_2BUTTON_OVERLAY,
-                overlayPackage == NAV_BAR_MODE_2BUTTON_OVERLAY);
-        setOverlayPackageEnabled(NAV_BAR_MODE_GESTURAL_OVERLAY,
-                overlayPackage == NAV_BAR_MODE_GESTURAL_OVERLAY);
-
-        for (int i = 0; i != 100; ++i) {
-            if (mLauncher.getNavigationModel() == expectedMode) {
-                try {
-                    Thread.sleep(1000);
-                } catch (InterruptedException e) {
-                    e.printStackTrace();
-                }
-                return;
-            }
-            try {
-                Thread.sleep(100);
-            } catch (InterruptedException e) {
-                e.printStackTrace();
-            }
-        }
-        Assert.fail("Couldn't switch to " + overlayPackage);
-    }
-
-    private void setOverlayPackageEnabled(String overlayPackage, boolean enable) {
-        Log.d(TAG, "setOverlayPackageEnabled: " + overlayPackage + " " + enable);
-        final String action = enable ? "enable" : "disable";
-        try {
-            UiDevice.getInstance(getInstrumentation()).executeShellCommand(
-                    "cmd overlay " + action + " " + overlayPackage);
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
     }
 
     @Rule
diff --git a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
index 357e029..48335a6 100644
--- a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
+++ b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
@@ -71,7 +71,7 @@
     }
 
     @Test
-    @Ignore // Convert test to TAPL and enable them; b/131116002
+    // Convert test to TAPL; b/131116002
     public void testCustomProfileLoaded_with_icon_on_hotseat() throws Exception {
         writeLayout(new LauncherLayoutBuilder().atHotseat(0).putApp(SETTINGS_APP, SETTINGS_APP));
 
@@ -86,7 +86,7 @@
     }
 
     @Test
-    @Ignore // Convert test to TAPL and enable them; b/131116002
+    // Convert test to TAPL; b/131116002
     public void testCustomProfileLoaded_with_widget() throws Exception {
         // A non-restored widget with no config screen gets restored automatically.
         LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
@@ -106,7 +106,7 @@
     }
 
     @Test
-    @Ignore // Convert test to TAPL and enable them; b/131116002
+    // Convert test to TAPL; b/131116002
     public void testCustomProfileLoaded_with_folder() throws Exception {
         writeLayout(new LauncherLayoutBuilder().atHotseat(0).putFolder(android.R.string.copy)
                 .addApp(SETTINGS_APP, SETTINGS_APP)
diff --git a/tests/src/com/android/launcher3/ui/TestViewHelpers.java b/tests/src/com/android/launcher3/ui/TestViewHelpers.java
index 6fa28f1..2116f5e 100644
--- a/tests/src/com/android/launcher3/ui/TestViewHelpers.java
+++ b/tests/src/com/android/launcher3/ui/TestViewHelpers.java
@@ -114,7 +114,8 @@
         Point center = icon.getVisibleCenter();
 
         // Action Down
-        sendPointer(MotionEvent.ACTION_DOWN, center);
+        final long downTime = SystemClock.uptimeMillis();
+        sendPointer(downTime, MotionEvent.ACTION_DOWN, center);
 
         UiObject2 dragLayer = findViewById(R.id.drag_layer);
 
@@ -131,7 +132,7 @@
             } else {
                 moveLocation.y += distanceToMove;
             }
-            movePointer(center, moveLocation);
+            movePointer(downTime, center, moveLocation);
 
             assertNull(findViewById(R.id.deep_shortcuts_container));
         }
@@ -142,19 +143,24 @@
         Point moveLocation = dragLayer.getVisibleCenter();
 
         // Move to center
-        movePointer(center, moveLocation);
-        sendPointer(MotionEvent.ACTION_UP, center);
+        movePointer(downTime, center, moveLocation);
+        sendPointer(downTime, MotionEvent.ACTION_UP, moveLocation);
 
         // Wait until remove target is gone.
         getDevice().wait(Until.gone(getSelectorForId(R.id.delete_target_text)),
                 AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT);
     }
 
-    private static void movePointer(Point from, Point to) {
+    private static void movePointer(long downTime, Point from, Point to) {
         while (!from.equals(to)) {
+            try {
+                Thread.sleep(20);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
             from.x = getNextMoveValue(to.x, from.x);
             from.y = getNextMoveValue(to.y, from.y);
-            sendPointer(MotionEvent.ACTION_MOVE, from);
+            sendPointer(downTime, MotionEvent.ACTION_MOVE, from);
         }
     }
 
@@ -168,8 +174,8 @@
         }
     }
 
-    public static void sendPointer(int action, Point point) {
-        MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(),
+    public static void sendPointer(long downTime, int action, Point point) {
+        MotionEvent event = MotionEvent.obtain(downTime,
                 SystemClock.uptimeMillis(), action, point.x, point.y, 0);
         getInstrumentation().sendPointerSync(event);
         event.recycle();
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 84452b4..5eb5f19 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -71,25 +71,25 @@
     }
 
     @Test
-    @Ignore // Convert test to TAPL and enable them; b/131116002
+    // Convert test to TAPL b/131116002
     public void testWidgetConfig() throws Throwable {
         runTest(false, true);
     }
 
     @Test
-    @Ignore // Convert test to TAPL and enable them; b/131116002
+    @Ignore // b/121280703
     public void testWidgetConfig_rotate() throws Throwable {
         runTest(true, true);
     }
 
     @Test
-    @Ignore // Convert test to TAPL and enable them; b/131116002
+    // Convert test to TAPL b/131116002
     public void testConfigCancelled() throws Throwable {
         runTest(false, false);
     }
 
     @Test
-    @Ignore // Convert test to TAPL and enable them; b/131116002
+    @Ignore // b/121280703
     public void testConfigCancelled_rotate() throws Throwable {
         runTest(true, false);
     }
@@ -156,7 +156,8 @@
         @Override
         public boolean evaluate(ItemInfo info, View view) {
             return info instanceof LauncherAppWidgetInfo &&
-                    ((LauncherAppWidgetInfo) info).providerName.equals(mWidgetInfo.provider) &&
+                    ((LauncherAppWidgetInfo) info).providerName.getClassName().equals(
+                            mWidgetInfo.provider.getClassName()) &&
                     ((LauncherAppWidgetInfo) info).appWidgetId == mWidgetId;
         }
     }
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 90c339f..0061568 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -49,19 +49,19 @@
     @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
 
     @Test
-    @Ignore // Convert test to TAPL and enable them; b/131116002
     public void testDragIcon_portrait() throws Throwable {
         lockRotation(true);
         performTest();
     }
 
     @Test
-    @Ignore // Convert test to TAPL and enable them; b/131116002
+    @Ignore // b/121280703
     public void testDragIcon_landscape() throws Throwable {
         lockRotation(false);
         performTest();
     }
 
+    // Convert to TAPL b/131116002
     private void performTest() throws Throwable {
         clearHomescreen();
         mActivityMonitor.startLauncher();
@@ -82,7 +82,8 @@
             @Override
             public boolean evaluate(ItemInfo info, View view) {
                 return info instanceof LauncherAppWidgetInfo &&
-                        ((LauncherAppWidgetInfo) info).providerName.equals(widgetInfo.provider);
+                        ((LauncherAppWidgetInfo) info).providerName.getClassName().equals(
+                                widgetInfo.provider.getClassName());
             }
         }).call());
     }
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index af50190..874ff19 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -123,7 +123,7 @@
         verifyWidgetPresent(info);
     }
 
-    @Test @Ignore // b/131116593
+    @Test
     public void testUnboundWidget_removed() {
         LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
         LauncherAppWidgetInfo item = createWidgetInfo(info, false);
@@ -143,7 +143,7 @@
         assertFalse(mDevice.findObject(new UiSelector().description(info.label)).exists());
     }
 
-    @Test @Ignore // b/131116593
+    @Test
     public void testPendingWidget_autoRestored() {
         // A non-restored widget with no config screen gets restored automatically.
         LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
@@ -181,7 +181,7 @@
                         LauncherSettings.Favorites.APPWIDGET_ID))));
     }
 
-    @Test @Ignore // b/131116593
+    @Test
     public void testPendingWidget_notRestored_removed() {
         LauncherAppWidgetInfo item = getInvalidWidgetInfo();
         item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 65d8a82..b66fa8a 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -80,7 +80,7 @@
     @Test
     public void testEmpty() throws Throwable { /* needed while the broken tests are being fixed */ }
 
-    @Test @Ignore // b/131116593
+    @Test
     public void testPinWidgetNoConfig() throws Throwable {
         runTest("pinWidgetNoConfig", true, new ItemOperator() {
             @Override
@@ -93,7 +93,7 @@
         });
     }
 
-        @Test @Ignore // b/131116593
+        @Test
     public void testPinWidgetNoConfig_customPreview() throws Throwable {
         // Command to set custom preview
         Intent command =  RequestPinItemActivity.getCommandIntent(
@@ -111,7 +111,7 @@
         }, command);
     }
 
-    @Test @Ignore // b/131116593
+    @Test
     public void testPinWidgetWithConfig() throws Throwable {
         runTest("pinWidgetWithConfig", true, new ItemOperator() {
             @Override
@@ -124,7 +124,7 @@
         });
     }
 
-    @Test @Ignore // b/131116593
+    @Test
     public void testPinShortcut() throws Throwable {
         // Command to set the shortcut id
         Intent command = RequestPinItemActivity.getCommandIntent(
@@ -181,8 +181,7 @@
 
         // Accept confirmation:
         BlockingBroadcastReceiver resultReceiver = new BlockingBroadcastReceiver(mCallbackAction);
-        mDevice.wait(Until.findObject(By.text(mTargetContext.getString(
-                R.string.place_automatically).toUpperCase())), DEFAULT_UI_TIMEOUT).click();
+        mDevice.wait(Until.findObject(By.text("Add automatically")), DEFAULT_UI_TIMEOUT).click();
         Intent result = resultReceiver.blockingGetIntent();
         assertNotNull(result);
         mAppWidgetId = result.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 4685c7d..1ad0037 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -69,6 +69,8 @@
             if (mLauncher.getNavigationModel() != ZERO_BUTTON) {
                 final UiObject2 navBar = mLauncher.waitForSystemUiObject("navigation_bar_frame");
                 allAppsContainer.setGestureMargins(0, 0, 0, navBar.getVisibleBounds().height() + 1);
+            } else {
+                allAppsContainer.setGestureMargins(0, 0, 0, 200);
             }
             final BySelector appIconSelector = AppIcon.getAppIconSelector(appName, mLauncher);
             if (!hasClickableIcon(allAppsContainer, appIconSelector)) {
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index f5c5a8d..87ef044 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -374,16 +374,6 @@
                     event -> true,
                     "Pressing Home didn't produce any events");
             mDevice.waitForIdle();
-
-            // Temporarily press home twice as the first click sometimes gets ignored  (b/124239413)
-            executeAndWaitForEvent(
-                    () -> {
-                        log("LauncherInstrumentation.pressHome before clicking");
-                        waitForSystemUiObject("home").click();
-                    },
-                    event -> true,
-                    "Pressing Home didn't produce any events");
-            mDevice.waitForIdle();
         }
         try (LauncherInstrumentation.Closable c = addContextLayer(
                 "performed action to switch to Home - " + action)) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 2de53c3..100a11c 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -38,7 +38,7 @@
                 "want to fling forward in widgets")) {
             LauncherInstrumentation.log("Widgets.flingForward enter");
             final UiObject2 widgetsContainer = verifyActiveContainer();
-            widgetsContainer.setGestureMargin(100);
+            widgetsContainer.setGestureMargins(0, 0, 0, 200);
             widgetsContainer.fling(Direction.DOWN,
                     (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung forward")) {