Merge "Fixing some dimentions in AllSet activity" into sc-dev
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 524cd53..ea4b08d 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -61,6 +61,7 @@
 import android.graphics.Matrix;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.os.Build;
 import android.os.IBinder;
 import android.os.SystemClock;
@@ -383,10 +384,10 @@
         // Set up a entire animation lifecycle callback to notify the current recents view when
         // the animation is canceled
         mGestureState.runOnceAtState(STATE_RECENTS_ANIMATION_CANCELED, () -> {
-                ThumbnailData snapshot = mGestureState.getRecentsAnimationCanceledSnapshot();
+                ThumbnailData snapshot = mGestureState.consumeRecentsAnimationCanceledSnapshot();
                 if (snapshot != null) {
-                    RecentsModel.INSTANCE.get(mContext).onTaskSnapshotChanged(
-                            mRecentsView.getRunningTaskId(), snapshot);
+                    mRecentsView.switchToScreenshot(snapshot,
+                            () -> mRecentsAnimationController.cleanupScreenshot());
                     mRecentsView.onRecentsAnimationComplete();
                 }
             });
@@ -1233,30 +1234,40 @@
         final RecentsOrientedState orientationState = mTaskViewSimulator.getOrientationState();
         final int windowRotation = orientationState.getDisplayRotation();
         final int homeRotation = orientationState.getRecentsActivityRotation();
+
+        final Matrix homeToWindowPositionMap = new Matrix();
+        final RectF startRect = updateProgressForStartRect(homeToWindowPositionMap, startProgress);
+        // Move the startRect to Launcher space as floatingIconView runs in Launcher
+        final Matrix windowToHomePositionMap = new Matrix();
+        homeToWindowPositionMap.invert(windowToHomePositionMap);
+        windowToHomePositionMap.mapRect(startRect);
+
         final Rect destinationBounds = SystemUiProxy.INSTANCE.get(mContext)
                 .startSwipePipToHome(taskInfo.topActivity,
                         TaskInfoCompat.getTopActivityInfo(taskInfo),
                         runningTaskTarget.taskInfo.pictureInPictureParams,
                         homeRotation,
                         mDp.hotseatBarSizePx);
-        final SwipePipToHomeAnimator swipePipToHomeAnimator = new SwipePipToHomeAnimator(
-                mContext,
-                runningTaskTarget.taskId,
-                taskInfo.topActivity,
-                runningTaskTarget.leash.getSurfaceControl(),
-                TaskInfoCompat.getPipSourceRectHint(
-                        runningTaskTarget.taskInfo.pictureInPictureParams),
-                TaskInfoCompat.getWindowConfigurationBounds(taskInfo),
-                updateProgressForStartRect(new Matrix(), startProgress),
-                destinationBounds,
-                mRecentsView.getPipCornerRadius(),
-                mRecentsView);
+        final SwipePipToHomeAnimator.Builder builder = new SwipePipToHomeAnimator.Builder()
+                .setContext(mContext)
+                .setTaskId(runningTaskTarget.taskId)
+                .setComponentName(taskInfo.topActivity)
+                .setLeash(runningTaskTarget.leash.getSurfaceControl())
+                .setSourceRectHint(TaskInfoCompat.getPipSourceRectHint(
+                        runningTaskTarget.taskInfo.pictureInPictureParams))
+                .setAppBounds(TaskInfoCompat.getWindowConfigurationBounds(taskInfo))
+                .setHomeToWindowPositionMap(homeToWindowPositionMap)
+                .setStartBounds(startRect)
+                .setDestinationBounds(destinationBounds)
+                .setCornerRadius(mRecentsView.getPipCornerRadius())
+                .setAttachedView(mRecentsView);
         // We would assume home and app window always in the same rotation While homeRotation
         // is not ROTATION_0 (which implies the rotation is turned on in launcher settings).
         if (homeRotation == ROTATION_0
                 && (windowRotation == ROTATION_90 || windowRotation == ROTATION_270)) {
-            swipePipToHomeAnimator.setFromRotation(mTaskViewSimulator, windowRotation);
+            builder.setFromRotation(mTaskViewSimulator, windowRotation);
         }
+        final SwipePipToHomeAnimator swipePipToHomeAnimator = builder.build();
         AnimatorPlaybackController activityAnimationToHome =
                 homeAnimFactory.createActivityAnimationToHome();
         swipePipToHomeAnimator.addAnimatorListener(new AnimatorListenerAdapter() {
@@ -1283,6 +1294,7 @@
                 mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
             }
         });
+        setupWindowAnimation(swipePipToHomeAnimator);
         return swipePipToHomeAnimator;
     }
 
@@ -1313,6 +1325,11 @@
             HomeAnimationFactory homeAnimationFactory) {
         RectFSpringAnim anim =
                 super.createWindowAnimationToHome(startProgress, homeAnimationFactory);
+        setupWindowAnimation(anim);
+        return anim;
+    }
+
+    private void setupWindowAnimation(RectFSpringAnim anim) {
         anim.addOnUpdateListener((v, r, p) -> {
             updateSysUiFlags(Math.max(p, mCurrentShift.value));
         });
@@ -1330,7 +1347,6 @@
         if (mRecentsAnimationTargets != null) {
             mRecentsAnimationTargets.addReleaseCheck(anim);
         }
-        return anim;
     }
 
     public void onConsumerAboutToBeSwitched() {
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index a302a07..015002f 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -376,11 +376,14 @@
     }
 
     /**
-     * Returns the canceled animation thumbnail data. This call only returns a value while
-     * STATE_RECENTS_ANIMATION_CANCELED state is being set.
+     * Returns and clears the canceled animation thumbnail data. This call only returns a value
+     * while STATE_RECENTS_ANIMATION_CANCELED state is being set, and the caller is responsible for
+     * calling {@link RecentsAnimationController#cleanupScreenshot()}.
      */
-    ThumbnailData getRecentsAnimationCanceledSnapshot() {
-        return mRecentsAnimationCanceledSnapshot;
+    ThumbnailData consumeRecentsAnimationCanceledSnapshot() {
+        ThumbnailData data = mRecentsAnimationCanceledSnapshot;
+        mRecentsAnimationCanceledSnapshot = null;
+        return data;
     }
 
     void setSwipeUpStartTimeMs(long uptimeMs) {
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index 67a635b..7488649 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -56,7 +56,9 @@
     private final ComponentName mComponentName;
     private final SurfaceControl mLeash;
     private final Rect mAppBounds = new Rect();
+    private final Matrix mHomeToWindowPositionMap = new Matrix();
     private final Rect mStartBounds = new Rect();
+    private final RectF mCurrentBoundsF = new RectF();
     private final Rect mCurrentBounds = new Rect();
     private final Rect mDestinationBounds = new Rect();
     private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
@@ -66,10 +68,9 @@
     private final Rect mSourceHintRectInsets;
     private final Rect mSourceInsets = new Rect();
 
-    /** for rotation via {@link #setFromRotation(TaskViewSimulator, int)} */
-    private @RecentsOrientedState.SurfaceRotation int mFromRotation = Surface.ROTATION_0;
+    /** for rotation calculations */
+    private final @RecentsOrientedState.SurfaceRotation int mFromRotation;
     private final Rect mDestinationBoundsTransformed = new Rect();
-    private final Rect mDestinationBoundsAnimation = new Rect();
 
     /**
      * Flag to avoid the double-end problem since the leash would have been released
@@ -91,31 +92,39 @@
      * @param leash {@link SurfaceControl} this animator operates on
      * @param sourceRectHint See the definition in {@link android.app.PictureInPictureParams}
      * @param appBounds Bounds of the application, sourceRectHint is based on this bounds
+     * @param homeToWindowPositionMap {@link Matrix} to map a Rect from home to window space
      * @param startBounds Bounds of the application when this animator starts. This can be
      *                    different from the appBounds if user has swiped a certain distance and
      *                    Launcher has performed transform on the leash.
      * @param destinationBounds Bounds of the destination this animator ends to
+     * @param fromRotation From rotation if different from final rotation, ROTATION_0 otherwise
+     * @param destinationBoundsTransformed Destination bounds in window space
      * @param cornerRadius Corner radius in pixel value for PiP window
+     * @param view Attached view for logging purpose
      */
-    public SwipePipToHomeAnimator(@NonNull Context context,
+    private SwipePipToHomeAnimator(@NonNull Context context,
             int taskId,
             @NonNull ComponentName componentName,
             @NonNull SurfaceControl leash,
             @Nullable Rect sourceRectHint,
             @NonNull Rect appBounds,
+            @NonNull Matrix homeToWindowPositionMap,
             @NonNull RectF startBounds,
             @NonNull Rect destinationBounds,
+            @RecentsOrientedState.SurfaceRotation int fromRotation,
+            @NonNull Rect destinationBoundsTransformed,
             int cornerRadius,
             @NonNull View view) {
-        super(startBounds, new RectF(destinationBounds), context);
+        super(startBounds, new RectF(destinationBoundsTransformed), context);
         mTaskId = taskId;
         mComponentName = componentName;
         mLeash = leash;
         mAppBounds.set(appBounds);
+        mHomeToWindowPositionMap.set(homeToWindowPositionMap);
         startBounds.round(mStartBounds);
         mDestinationBounds.set(destinationBounds);
-        mDestinationBoundsTransformed.set(mDestinationBounds);
-        mDestinationBoundsAnimation.set(mDestinationBounds);
+        mFromRotation = fromRotation;
+        mDestinationBoundsTransformed.set(destinationBoundsTransformed);
         mSurfaceTransactionHelper = new PipSurfaceTransactionHelper(cornerRadius);
 
         if (sourceRectHint != null && (sourceRectHint.width() < destinationBounds.width()
@@ -191,37 +200,13 @@
         addOnUpdateListener(this::onAnimationUpdate);
     }
 
-    /** sets the from rotation if it's different from the target rotation. */
-    public void setFromRotation(TaskViewSimulator taskViewSimulator,
-            @RecentsOrientedState.SurfaceRotation int fromRotation) {
-        if (fromRotation != Surface.ROTATION_90 && fromRotation != Surface.ROTATION_270) {
-            Log.wtf(TAG, "Not a supported rotation, rotation=" + fromRotation);
-            return;
-        }
-        mFromRotation = fromRotation;
-        final Matrix matrix = new Matrix();
-        taskViewSimulator.applyWindowToHomeRotation(matrix);
-
-        // map the destination bounds into window space. mDestinationBounds is always calculated
-        // in the final home space and the animation runs in original window space.
-        final RectF transformed = new RectF(mDestinationBounds);
-        matrix.mapRect(transformed, new RectF(mDestinationBounds));
-        transformed.round(mDestinationBoundsTransformed);
-
-        // set the animation destination bounds for RectEvaluator calculation.
-        // bounds and insets are calculated as if the transition is from mAppBounds to
-        // mDestinationBoundsAnimation, separated from rotate / scale / position.
-        mDestinationBoundsAnimation.set(mAppBounds.left, mAppBounds.top,
-                mAppBounds.left + mDestinationBounds.width(),
-                mAppBounds.top + mDestinationBounds.height());
-    }
-
     private void onAnimationUpdate(@Nullable AppCloseConfig values, RectF currentRect,
             float progress) {
         if (mHasAnimationEnded) return;
         final SurfaceControl.Transaction tx =
                 PipSurfaceTransactionHelper.newSurfaceControlTransaction();
-        onAnimationUpdate(tx, currentRect, progress);
+        mHomeToWindowPositionMap.mapRect(mCurrentBoundsF, currentRect);
+        onAnimationUpdate(tx, mCurrentBoundsF, progress);
         tx.apply();
     }
 
@@ -309,6 +294,108 @@
         return new RotatedPosition(degree, positionX, positionY);
     }
 
+    /** Builder class for {@link SwipePipToHomeAnimator} */
+    public static class Builder {
+        private Context mContext;
+        private int mTaskId;
+        private ComponentName mComponentName;
+        private SurfaceControl mLeash;
+        private Rect mSourceRectHint;
+        private Rect mAppBounds;
+        private Matrix mHomeToWindowPositionMap;
+        private RectF mStartBounds;
+        private Rect mDestinationBounds;
+        private int mCornerRadius;
+        private View mAttachedView;
+        private @RecentsOrientedState.SurfaceRotation int mFromRotation = Surface.ROTATION_0;
+        private final Rect mDestinationBoundsTransformed = new Rect();
+
+        public Builder setContext(Context context) {
+            mContext = context;
+            return this;
+        }
+
+        public Builder setTaskId(int taskId) {
+            mTaskId = taskId;
+            return this;
+        }
+
+        public Builder setComponentName(ComponentName componentName) {
+            mComponentName = componentName;
+            return this;
+        }
+
+        public Builder setLeash(SurfaceControl leash) {
+            mLeash = leash;
+            return this;
+        }
+
+        public Builder setSourceRectHint(Rect sourceRectHint) {
+            mSourceRectHint = new Rect(sourceRectHint);
+            return this;
+        }
+
+        public Builder setAppBounds(Rect appBounds) {
+            mAppBounds = new Rect(appBounds);
+            return this;
+        }
+
+        public Builder setHomeToWindowPositionMap(Matrix homeToWindowPositionMap) {
+            mHomeToWindowPositionMap = new Matrix(homeToWindowPositionMap);
+            return this;
+        }
+
+        public Builder setStartBounds(RectF startBounds) {
+            mStartBounds = new RectF(startBounds);
+            return this;
+        }
+
+        public Builder setDestinationBounds(Rect destinationBounds) {
+            mDestinationBounds = new Rect(destinationBounds);
+            return this;
+        }
+
+        public Builder setCornerRadius(int cornerRadius) {
+            mCornerRadius = cornerRadius;
+            return this;
+        }
+
+        public Builder setAttachedView(View attachedView) {
+            mAttachedView = attachedView;
+            return this;
+        }
+
+        public Builder setFromRotation(TaskViewSimulator taskViewSimulator,
+                @RecentsOrientedState.SurfaceRotation int fromRotation) {
+            if (fromRotation != Surface.ROTATION_90 && fromRotation != Surface.ROTATION_270) {
+                Log.wtf(TAG, "Not a supported rotation, rotation=" + fromRotation);
+                return this;
+            }
+            final Matrix matrix = new Matrix();
+            taskViewSimulator.applyWindowToHomeRotation(matrix);
+
+            // map the destination bounds into window space. mDestinationBounds is always calculated
+            // in the final home space and the animation runs in original window space.
+            final RectF transformed = new RectF(mDestinationBounds);
+            matrix.mapRect(transformed, new RectF(mDestinationBounds));
+            transformed.round(mDestinationBoundsTransformed);
+
+            mFromRotation = fromRotation;
+            return this;
+        }
+
+        public SwipePipToHomeAnimator build() {
+            if (mDestinationBoundsTransformed.isEmpty()) {
+                mDestinationBoundsTransformed.set(mDestinationBounds);
+            }
+            return new SwipePipToHomeAnimator(mContext, mTaskId, mComponentName, mLeash,
+                    mSourceRectHint, mAppBounds,
+                    mHomeToWindowPositionMap, mStartBounds, mDestinationBounds,
+                    mFromRotation, mDestinationBoundsTransformed,
+                    mCornerRadius, mAttachedView);
+        }
+    }
+
     private static class RotatedPosition {
         private final float degree;
         private final float positionX;
diff --git a/res/drawable/work_card.xml b/res/drawable/work_card.xml
index 0e4b054..7048955 100644
--- a/res/drawable/work_card.xml
+++ b/res/drawable/work_card.xml
@@ -20,8 +20,5 @@
     android:shape="rectangle">
     <solid android:color="?androidprv:attr/colorSurface" />
     <corners android:radius="@dimen/work_edu_card_margin" />
-    <padding
-        android:left="@dimen/work_fab_radius"
-        android:right="@dimen/work_fab_radius" />
 </shape>
 
diff --git a/res/layout/launcher_preview_layout.xml b/res/layout/launcher_preview_layout.xml
index 1691680..cf2f2c7 100644
--- a/res/layout/launcher_preview_layout.xml
+++ b/res/layout/launcher_preview_layout.xml
@@ -13,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.InsettableFrameLayout
+<view class="com.android.launcher3.graphics.LauncherPreviewRenderer$LauncherPreviewLayout"
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
@@ -33,4 +33,4 @@
         android:id="@+id/hotseat"
         layout="@layout/hotseat" />
 
-</com.android.launcher3.InsettableFrameLayout>
\ No newline at end of file
+</view>
\ No newline at end of file
diff --git a/res/layout/work_apps_edu.xml b/res/layout/work_apps_edu.xml
index 919f1b2..84fdfdf 100644
--- a/res/layout/work_apps_edu.xml
+++ b/res/layout/work_apps_edu.xml
@@ -22,7 +22,8 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="vertical"
-        android:padding="@dimen/work_edu_card_margin"
+        android:paddingHorizontal="@dimen/work_card_padding_horizontal"
+        android:paddingVertical="@dimen/work_card_padding_vertical"
         android:background="@drawable/work_card"
         android:layout_gravity="center_horizontal"
         android:gravity="center"
@@ -34,8 +35,7 @@
             android:id="@+id/work_apps_paused_title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginTop="8dp"
-            android:layout_marginBottom="8dp"
+            android:layout_marginBottom="@dimen/work_card_padding_vertical"
             android:text="@string/work_profile_edu_work_apps"
             android:textAlignment="center"
             android:textSize="20sp" />
diff --git a/res/layout/work_apps_paused.xml b/res/layout/work_apps_paused.xml
index 02a50ca..841734c 100644
--- a/res/layout/work_apps_paused.xml
+++ b/res/layout/work_apps_paused.xml
@@ -28,18 +28,18 @@
         android:layout_marginTop="40dp"
         android:text="@string/work_apps_paused_title"
         android:textAlignment="center"
-        android:textSize="20sp" />
+        android:textSize="22sp" />
 
     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:id="@+id/work_apps_paused_content"
-        android:textColor="?attr/workProfileOverlayTextColor"
+        android:textColor="?android:attr/textColorSecondary"
         android:text="@string/work_apps_paused_body"
         android:textAlignment="center"
         android:layout_marginTop="16dp"
         android:layout_marginBottom="24dp"
-        android:textSize="16sp" />
+        android:textSize="14sp" />
 
     <Button
         android:layout_width="wrap_content"
@@ -49,5 +49,7 @@
         android:text="@string/work_apps_enable_btn_text"
         android:textAlignment="center"
         android:background="@drawable/rounded_action_button"
+        android:paddingStart="8dp"
+        android:paddingEnd="8dp"
         android:textSize="14sp" />
 </com.android.launcher3.allapps.WorkPausedCard>
\ No newline at end of file
diff --git a/res/layout/work_mode_fab.xml b/res/layout/work_mode_fab.xml
index 7183817..04faa15 100644
--- a/res/layout/work_mode_fab.xml
+++ b/res/layout/work_mode_fab.xml
@@ -25,7 +25,7 @@
     android:textColor="@color/all_apps_tab_text"
     android:textSize="14sp"
     android:background="@drawable/work_apps_toggle_background"
-    android:drawablePadding="16dp"
+    android:drawablePadding="8dp"
     android:drawableStart="@drawable/ic_corp_off"
     android:layout_marginBottom="@dimen/work_fab_margin"
     android:layout_marginEnd="@dimen/work_fab_margin"
diff --git a/res/values-sw340dp/dimens.xml b/res/values-sw340dp/dimens.xml
index c9f2981..33b06f5 100644
--- a/res/values-sw340dp/dimens.xml
+++ b/res/values-sw340dp/dimens.xml
@@ -19,6 +19,5 @@
 
     <!-- Drag padding to add to the bottom of drop targets -->
     <dimen name="drop_target_drag_padding">20dp</dimen>
-    <dimen name="drop_target_text_size">16sp</dimen>
 
 </resources>
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 270c92e..ee2304c 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -121,6 +121,8 @@
 <!-- Floating action button inside work tab to toggle work profile -->
     <dimen name="work_fab_height">48dp</dimen>
     <dimen name="work_fab_radius">24dp</dimen>
+    <dimen name="work_card_padding_horizontal">24dp</dimen>
+    <dimen name="work_card_padding_vertical">32dp</dimen>
     <dimen name="work_fab_margin">16dp</dimen>
     <dimen name="work_profile_footer_padding">20dp</dimen>
     <dimen name="work_profile_footer_text_size">16sp</dimen>
@@ -189,8 +191,9 @@
 <!-- Dragging -->
     <!-- Drag padding to add to the bottom of drop targets -->
     <dimen name="drop_target_drag_padding">14dp</dimen>
-    <dimen name="drop_target_text_size">20sp</dimen>
+    <dimen name="drop_target_text_size">16sp</dimen>
     <dimen name="drop_target_shadow_elevation">2dp</dimen>
+    <dimen name="drop_target_bar_margin_horizontal">4dp</dimen>
 
     <!-- the distance an icon must be dragged before button drop targets accept it -->
     <dimen name="drag_distanceThreshold">30dp</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 416c711..24851f2 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -258,7 +258,7 @@
     <!-- Drop targets -->
     <style name="DropTargetButtonBase" parent="@android:style/TextAppearance.DeviceDefault">
         <item name="android:drawablePadding">8dp</item>
-        <item name="android:padding">16dp</item>
+        <item name="android:padding">14dp</item>
         <item name="android:textColor">@color/drop_target_text</item>
         <item name="android:textSize">@dimen/drop_target_text_size</item>
         <item name="android:singleLine">true</item>
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 23dd3bb..61b5564 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -20,15 +20,12 @@
 
 import static com.android.launcher3.LauncherState.NORMAL;
 
-import android.animation.AnimatorSet;
 import android.content.Context;
-import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.util.Property;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -40,6 +37,7 @@
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.model.data.ItemInfo;
 
 /**
@@ -212,18 +210,23 @@
             return;
         }
         final DragLayer dragLayer = mLauncher.getDragLayer();
+        final DragView dragView = d.dragView;
         final Rect from = new Rect();
         dragLayer.getViewRectRelativeToSelf(d.dragView, from);
 
         final Rect to = getIconRect(d);
         final float scale = (float) to.width() / from.width();
-        d.dragView.detachContentView(/* reattachToPreviousParent= */ true);
+        dragView.disableColorExtraction();
+        dragView.detachContentView(/* reattachToPreviousParent= */ true);
         mDropTargetBar.deferOnDragEnd();
 
         Runnable onAnimationEndRunnable = () -> {
             completeDrop(d);
             mDropTargetBar.onDragEnd();
             mLauncher.getStateManager().goToState(NORMAL);
+            // Only re-enable updates once the workspace is back to normal, which will be after the
+            // current frame.
+            post(dragView::resumeColorExtraction);
         };
 
         dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f,
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index 4a1b084..88f6c49 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -105,7 +105,8 @@
                         / (2 * (grid.inv.numColumns + 1)))
                         + grid.edgeMarginPx;
             } else {
-                gap = grid.desiredWorkspaceLeftRightMarginPx - grid.inv.defaultWidgetPadding.right;
+                gap = getContext().getResources()
+                        .getDimensionPixelSize(R.dimen.drop_target_bar_margin_horizontal);
             }
             lp.width = grid.availableWidthPx - 2 * gap;
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index f28f54a..5bdc402 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1786,8 +1786,11 @@
             onDropExternal(touchXY, dropTargetLayout, d);
         } else {
             final View cell = mDragInfo.cell;
+            final DragView dragView = d.dragView;
             boolean droppedOnOriginalCellDuringTransition = false;
-            Runnable onCompleteRunnable = null;
+            Runnable onCompleteRunnable = dragView::resumeColorExtraction;
+
+            dragView.disableColorExtraction();
 
             if (dropTargetLayout != null && !d.cancelled) {
                 // Move internally
@@ -1898,7 +1901,9 @@
                         AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo();
                         if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE
                                 && !options.isAccessibleDrag) {
+                            final Runnable previousRunnable = onCompleteRunnable;
                             onCompleteRunnable = () -> {
+                                previousRunnable.run();
                                 if (!isPageInTransition()) {
                                     AppWidgetResizeFrame.showForWidget(hostView, cellLayout);
                                 }
@@ -1967,7 +1972,7 @@
             parent.onDropChild(cell);
 
             mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY,
-                    onCompleteRunnable == null ? null : forSuccessCallback(onCompleteRunnable));
+                    forSuccessCallback(onCompleteRunnable));
             mStatsLogManager.logger().withItemInfo(d.dragInfo).withInstanceId(d.logInstanceId)
                     .log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
         }
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 1664980..3fdb256 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -66,6 +66,7 @@
 import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.views.BaseDragLayer;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
 /** A custom view for rendering an icon, folder, shortcut or widget during drag-n-drop. */
 public class DragView extends FrameLayout implements StateListener<LauncherState> {
@@ -471,12 +472,12 @@
             mContent.draw(picture.beginRecording(mWidth, mHeight));
             picture.endRecording();
             View view = new View(mLauncher);
-            view.setClipToOutline(mContent.getClipToOutline());
-            view.setOutlineProvider(mContent.getOutlineProvider());
             view.setBackground(new PictureDrawable(picture));
             view.measure(makeMeasureSpec(mWidth, EXACTLY), makeMeasureSpec(mHeight, EXACTLY));
             view.layout(mContent.getLeft(), mContent.getTop(),
                     mContent.getRight(), mContent.getBottom());
+            setClipToOutline(mContent.getClipToOutline());
+            setOutlineProvider(mContent.getOutlineProvider());
             addViewInLayout(view, indexOfChild(mContent), mContent.getLayoutParams(), true);
 
             removeViewInLayout(mContent);
@@ -491,6 +492,24 @@
     }
 
     /**
+     * If the drag view uses color extraction, block it.
+     */
+    public void disableColorExtraction() {
+        if (mContent instanceof LauncherAppWidgetHostView) {
+            ((LauncherAppWidgetHostView) mContent).disableColorExtraction();
+        }
+    }
+
+    /**
+     * If the drag view uses color extraction, restores it.
+     */
+    public void resumeColorExtraction() {
+        if (mContent instanceof LauncherAppWidgetHostView) {
+            ((LauncherAppWidgetHostView) mContent).enableColorExtraction(/* updateColors= */ false);
+        }
+    }
+
+    /**
      * Removes this view from the {@link DragLayer}.
      *
      * <p>If the drag content is a {@link #mContent}, this call doesn't reattach the
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 2a1aec8..cf3da4b 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -43,6 +43,7 @@
 import android.util.AttributeSet;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowInsets;
@@ -467,4 +468,16 @@
         view.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
         view.layout(0, 0, width, height);
     }
+
+    /** Root layout for launcher preview that intercepts all touch events. */
+    public static class LauncherPreviewLayout extends InsettableFrameLayout {
+        public LauncherPreviewLayout(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+
+        @Override
+        public boolean onInterceptTouchEvent(MotionEvent ev) {
+            return true;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index ea08a25..63bc416 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -57,7 +57,6 @@
 import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
 
 import java.util.List;
-import java.util.Optional;
 
 /**
  * {@inheritDoc}
@@ -118,7 +117,9 @@
     private final ViewGroupFocusHelper mDragLayerRelativeCoordinateHelper;
     private long mDeferUpdatesUntilMillis = 0;
     private RemoteViews mDeferredRemoteViews;
-    private Optional<SparseIntArray> mDeferredColorChange = Optional.empty();
+    private boolean mHasDeferredColorChange = false;
+    private @Nullable SparseIntArray mDeferredColorChange = null;
+    private boolean mEnableColorExtraction = true;
 
     public LauncherAppWidgetHostView(Context context) {
         super(context);
@@ -243,18 +244,23 @@
      */
     public void endDeferringUpdates() {
         RemoteViews remoteViews;
-        Optional<SparseIntArray> deferredColors;
+        SparseIntArray deferredColors;
+        boolean hasDeferredColors;
         synchronized (mUpdateLock) {
             mDeferUpdatesUntilMillis = 0;
             remoteViews = mDeferredRemoteViews;
             mDeferredRemoteViews = null;
             deferredColors = mDeferredColorChange;
-            mDeferredColorChange = Optional.empty();
+            hasDeferredColors = mHasDeferredColorChange;
+            mDeferredColorChange = null;
+            mHasDeferredColorChange = false;
         }
         if (remoteViews != null) {
             updateAppWidget(remoteViews);
         }
-        deferredColors.ifPresent(colors -> onColorsChanged(null /* rectF */, colors));
+        if (hasDeferredColors) {
+            onColorsChanged(null /* rectF */, deferredColors);
+        }
     }
 
     public boolean onInterceptTouchEvent(MotionEvent ev) {
@@ -342,13 +348,7 @@
         }
 
         mIsScrollable = checkScrollableRecursively(this);
-        if (!mIsInDragMode && getTag() instanceof LauncherAppWidgetInfo) {
-
-            LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
-            mDragLayerRelativeCoordinateHelper.viewToRect(this, mCurrentWidgetSize);
-            updateColorExtraction(mCurrentWidgetSize,
-                    mWorkspace.getPageIndexForScreenId(info.screenId));
-        }
+        updateColorExtraction();
 
         enforceRoundedCorners();
     }
@@ -377,6 +377,7 @@
      * @param pageId The workspace page the widget is on.
      */
     private void updateColorExtraction(Rect rectInDragLayer, int pageId) {
+        if (!mEnableColorExtraction) return;
         mColorExtractor.getExtractedRectForViewRect(mLauncher, pageId, rectInDragLayer, mTempRectF);
 
         if (mTempRectF.isEmpty()) {
@@ -391,6 +392,38 @@
         }
     }
 
+    /**
+     * Update the color extraction, using the current position of the app widget.
+     */
+    private void updateColorExtraction() {
+        if (!mIsInDragMode && getTag() instanceof LauncherAppWidgetInfo) {
+            LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
+            mDragLayerRelativeCoordinateHelper.viewToRect(this, mCurrentWidgetSize);
+            updateColorExtraction(mCurrentWidgetSize,
+                    mWorkspace.getPageIndexForScreenId(info.screenId));
+        }
+    }
+
+    /**
+     * Enables the local color extraction.
+     *
+     * @param updateColors If true, this will update the color extraction using the current location
+     *                    of the App Widget.
+     */
+    public void enableColorExtraction(boolean updateColors) {
+        mEnableColorExtraction = true;
+        if (updateColors) {
+            updateColorExtraction();
+        }
+    }
+
+    /**
+     * Disables the local color extraction.
+     */
+    public void disableColorExtraction() {
+        mEnableColorExtraction = false;
+    }
+
     // Compare two location rectangles. Locations are always in the [0;1] range.
     private static boolean isSameLocation(@NonNull RectF rect1, @Nullable RectF rect2,
             float epsilon) {
@@ -409,10 +442,12 @@
     public void onColorsChanged(RectF rectF, SparseIntArray colors) {
         synchronized (mUpdateLock) {
             if (isDeferringUpdates()) {
-                mDeferredColorChange = Optional.ofNullable(colors);
+                mDeferredColorChange = colors;
+                mHasDeferredColorChange = true;
                 return;
             }
-            mDeferredColorChange = Optional.empty();
+            mDeferredColorChange = null;
+            mHasDeferredColorChange = false;
         }
 
         // setColorResources will reapply the view, which must happen in the UI thread.
diff --git a/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java b/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java
index 0ec0f02..2b2fef4 100644
--- a/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java
+++ b/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java
@@ -15,21 +15,27 @@
  */
 package com.android.launcher3.util.rule;
 
-import static com.android.launcher3.tapl.TestHelpers.getLauncherInMyProcess;
-
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.launcher3.tapl.TestHelpers.getLauncherInMyProcess;
+
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
+import android.util.Log;
 
 import androidx.annotation.Nullable;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.uiautomator.UiDevice;
 
+import com.android.systemui.shared.system.PackageManagerWrapper;
+
+import org.junit.Assert;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.ArrayList;
+
 /**
  * Test rule which executes a shell command at the start of the test.
  */
@@ -37,10 +43,19 @@
 
     private final String mCmd;
     private final String mRevertCommand;
+    private final boolean mCheckSuccess;
+    private final Runnable mAdditionalChecks;
 
-    public ShellCommandRule(String cmd, @Nullable String revertCommand) {
+    public ShellCommandRule(String cmd, @Nullable String revertCommand, boolean checkSuccess,
+            Runnable additionalChecks) {
         mCmd = cmd;
         mRevertCommand = revertCommand;
+        mCheckSuccess = checkSuccess;
+        mAdditionalChecks = additionalChecks;
+    }
+
+    public ShellCommandRule(String cmd, @Nullable String revertCommand) {
+        this(cmd, revertCommand, false, null);
     }
 
     @Override
@@ -48,12 +63,27 @@
         return new Statement() {
             @Override
             public void evaluate() throws Throwable {
-                UiDevice.getInstance(getInstrumentation()).executeShellCommand(mCmd);
+                final String result =
+                        UiDevice.getInstance(getInstrumentation()).executeShellCommand(mCmd);
+                if (mCheckSuccess) {
+                    Assert.assertTrue(
+                            "Failed command: " + mCmd + ", result: " + result,
+                            "Success".equals(result.replaceAll("\\s", "")));
+                }
+                if (mAdditionalChecks != null) mAdditionalChecks.run();
                 try {
                     base.evaluate();
                 } finally {
                     if (mRevertCommand != null) {
-                        UiDevice.getInstance(getInstrumentation()).executeShellCommand(mRevertCommand);
+                        final String revertResult = UiDevice.getInstance(
+                                getInstrumentation()).executeShellCommand(
+                                mRevertCommand);
+                        if (mCheckSuccess) {
+                            Assert.assertTrue(
+                                    "Failed command: " + mRevertCommand
+                                            + ", result: " + revertResult,
+                                    "Success".equals(result.replaceAll("\\s", "")));
+                        }
                     }
                 }
             }
@@ -72,7 +102,15 @@
      * Sets the target launcher as default launcher.
      */
     public static ShellCommandRule setDefaultLauncher() {
-        return new ShellCommandRule(getLauncherCommand(getLauncherInMyProcess()), null);
+        final ActivityInfo launcher = getLauncherInMyProcess();
+        Log.d("b/187080582", "Launcher: " + new ComponentName(launcher.packageName, launcher.name)
+                .flattenToString());
+        return new ShellCommandRule(getLauncherCommand(launcher), null, true, () ->
+                Assert.assertEquals("Setting default launcher failed",
+                        new ComponentName(launcher.packageName, launcher.name)
+                                .flattenToString(),
+                        PackageManagerWrapper.getInstance().getHomeActivities(new ArrayList<>())
+                                .flattenToString()));
     }
 
     public static String getLauncherCommand(ActivityInfo launcher) {
diff --git a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
index a809e2e..710e3cd 100644
--- a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
+++ b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
@@ -15,6 +15,10 @@
  */
 package com.android.launcher3.tapl;
 
+import static com.android.launcher3.testing.TestProtocol.SEQUENCE_MAIN;
+import static com.android.launcher3.testing.TestProtocol.SEQUENCE_PILFER;
+import static com.android.launcher3.testing.TestProtocol.SEQUENCE_TIS;
+
 import android.os.SystemClock;
 
 import com.android.launcher3.testing.TestProtocol;
@@ -87,6 +91,24 @@
         final ListMap<String> actualEvents = finishSync(waitForExpectedCountMs);
         if (actualEvents == null) return "null event sequences because launcher likely died";
 
+        final String lowLevelDiags = lowLevelMismatchDiagnostics(actualEvents);
+        // If we have a sequence mismatch for a successful gesture, we want to provide all low-level
+        // details.
+        if (successfulGesture) {
+            return lowLevelDiags;
+        }
+
+        final String sequenceMismatchInEnglish = highLevelMismatchDiagnostics(actualEvents);
+
+        if (sequenceMismatchInEnglish != null) {
+            LauncherInstrumentation.log(lowLevelDiags);
+            return "Hint: " + sequenceMismatchInEnglish;
+        } else {
+            return lowLevelDiags;
+        }
+    }
+
+    private String lowLevelMismatchDiagnostics(ListMap<String> actualEvents) {
         final StringBuilder sb = new StringBuilder();
         boolean hasMismatches = false;
         for (Map.Entry<String, List<Pattern>> expectedEvents : mExpectedEvents.entrySet()) {
@@ -118,6 +140,42 @@
         return hasMismatches ? "Mismatching events: " + sb.toString() : null;
     }
 
+    private String highLevelMismatchDiagnostics(ListMap<String> actualEvents) {
+        if (!mExpectedEvents.getNonNull(SEQUENCE_TIS).isEmpty()
+                && actualEvents.getNonNull(SEQUENCE_TIS).isEmpty()) {
+            return "TouchInteractionService didn't receive any of the touch events sent by the "
+                    + "test";
+        }
+        if (getMismatchPosition(mExpectedEvents.getNonNull(SEQUENCE_TIS),
+                actualEvents.getNonNull(SEQUENCE_TIS)) != -1) {
+            // If TIS has a mismatch that we can't convert to high-level diags, don't convert
+            // other sequences either.
+            return null;
+        }
+
+        if (mExpectedEvents.getNonNull(SEQUENCE_PILFER).size() == 1
+                && actualEvents.getNonNull(SEQUENCE_PILFER).isEmpty()) {
+            return "Launcher didn't detect the navigation gesture sent by the test";
+        }
+        if (mExpectedEvents.getNonNull(SEQUENCE_PILFER).isEmpty()
+                && actualEvents.getNonNull(SEQUENCE_PILFER).size() == 1) {
+            return "Launcher detected a navigation gesture, but the test didn't send one";
+        }
+        if (getMismatchPosition(mExpectedEvents.getNonNull(SEQUENCE_PILFER),
+                actualEvents.getNonNull(SEQUENCE_PILFER)) != -1) {
+            // If Pilfer has a mismatch that we can't convert to high-level diags, don't analyze
+            // other sequences.
+            return null;
+        }
+
+        if (!mExpectedEvents.getNonNull(SEQUENCE_MAIN).isEmpty()
+                && actualEvents.getNonNull(SEQUENCE_MAIN).isEmpty()) {
+            return "None of the touch or keyboard events sent by the test was received by "
+                    + "Launcher's main thread";
+        }
+        return null;
+    }
+
     // If the list of actual events matches the list of expected events, returns -1, otherwise
     // the position of the mismatch.
     private static int getMismatchPosition(List<Pattern> expected, List<String> actual) {