Snap for 7552724 from a1ef7b3c1bd2d08ef624e7749d40bf40bc6fa108 to sc-v2-release

Change-Id: I0e2a6443787fca82fe61a0f05c18ae344dff9912
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 096ac6c..bda2b77 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -115,7 +115,6 @@
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 import com.android.systemui.shared.system.LatencyTrackerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.TaskInfoCompat;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 
@@ -540,7 +539,7 @@
             @Override
             public void onMotionPauseDetected() {
                 mHasMotionEverBeenPaused = true;
-                maybeUpdateRecentsAttachedState();
+                maybeUpdateRecentsAttachedState(true/* animate */, true/* moveFocusedTask */);
                 performHapticFeedback();
             }
 
@@ -551,18 +550,24 @@
         };
     }
 
-    public void maybeUpdateRecentsAttachedState() {
+    private void maybeUpdateRecentsAttachedState() {
         maybeUpdateRecentsAttachedState(true /* animate */);
     }
 
+    private void maybeUpdateRecentsAttachedState(boolean animate) {
+        maybeUpdateRecentsAttachedState(animate, false /* moveFocusedTask */);
+    }
+
     /**
      * Determines whether to show or hide RecentsView. The window is always
      * synchronized with its corresponding TaskView in RecentsView, so if
      * RecentsView is shown, it will appear to be attached to the window.
      *
      * Note this method has no effect unless the navigation mode is NO_BUTTON.
+     * @param animate whether to animate when attaching RecentsView
+     * @param moveFocusedTask whether to move focused task to front when attaching
      */
-    private void maybeUpdateRecentsAttachedState(boolean animate) {
+    private void maybeUpdateRecentsAttachedState(boolean animate, boolean moveFocusedTask) {
         if (!mDeviceState.isFullyGesturalNavMode() || mRecentsView == null) {
             return;
         }
@@ -581,6 +586,12 @@
         } else {
             recentsAttachedToAppWindow = mHasMotionEverBeenPaused || mIsLikelyToStartNewTask;
         }
+        if (moveFocusedTask && !mAnimationFactory.hasRecentsEverAttachedToAppWindow()
+                && recentsAttachedToAppWindow) {
+            // Only move focused task if RecentsView has never been attached before, to avoid
+            // TaskView jumping to new position as we move the tasks.
+            mRecentsView.moveFocusedTaskToFront();
+        }
         mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate);
 
         // Reapply window transform throughout the attach animation, as the animation affects how
@@ -1026,9 +1037,6 @@
             if (mRecentsView != null) {
                 int nearestPage = mRecentsView.getDestinationPage();
                 boolean isScrolling = false;
-                // Update page scroll before snapping to page to make sure we snapped to the
-                // position calculated with target gesture in mind.
-                mRecentsView.updateScrollSynchronously();
                 if (mRecentsView.getNextPage() != nearestPage) {
                     // We shouldn't really scroll to the next page when swiping up to recents.
                     // Only allow settling on the next page if it's nearest to the center.
@@ -1110,7 +1118,8 @@
         public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
                 boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
             if (task.taskId == mGestureState.getRunningTaskId()
-                    && TaskInfoCompat.getActivityType(task) != ACTIVITY_TYPE_HOME) {
+                    && task.configuration.windowConfiguration.getActivityType()
+                    != ACTIVITY_TYPE_HOME) {
                 // Since this is an edge case, just cancel and relaunch with default activity
                 // options (since we don't know if there's an associated app icon to launch from)
                 endRunningWindowAnim(true /* cancel */);
@@ -1152,8 +1161,7 @@
             boolean appCanEnterPip = !mDeviceState.isPipActive()
                     && runningTaskTarget != null
                     && runningTaskTarget.taskInfo.pictureInPictureParams != null
-                    && TaskInfoCompat.isAutoEnterPipEnabled(
-                            runningTaskTarget.taskInfo.pictureInPictureParams);
+                    && runningTaskTarget.taskInfo.pictureInPictureParams.isAutoEnterEnabled();
             HomeAnimationFactory homeAnimFactory =
                     createHomeAnimationFactory(cookies, duration, isTranslucent, appCanEnterPip,
                             runningTaskTarget);
@@ -1187,7 +1195,8 @@
             mLauncherTransitionController = null;
 
             if (mRecentsView != null) {
-                mRecentsView.onPrepareGestureEndAnimation(null, mGestureState.getEndTarget());
+                mRecentsView.onPrepareGestureEndAnimation(null, mGestureState.getEndTarget(),
+                        mTaskViewSimulator);
             }
         } else {
             AnimatorSet animatorSet = new AnimatorSet();
@@ -1229,7 +1238,7 @@
             animatorSet.play(windowAnim);
             if (mRecentsView != null) {
                 mRecentsView.onPrepareGestureEndAnimation(
-                        animatorSet, mGestureState.getEndTarget());
+                        animatorSet, mGestureState.getEndTarget(), mTaskViewSimulator);
             }
             animatorSet.setDuration(duration).setInterpolator(interpolator);
             animatorSet.start();
@@ -1254,7 +1263,7 @@
 
         final Rect destinationBounds = SystemUiProxy.INSTANCE.get(mContext)
                 .startSwipePipToHome(taskInfo.topActivity,
-                        TaskInfoCompat.getTopActivityInfo(taskInfo),
+                        taskInfo.topActivityInfo,
                         runningTaskTarget.taskInfo.pictureInPictureParams,
                         homeRotation,
                         mDp.hotseatBarSizePx);
@@ -1263,9 +1272,9 @@
                 .setTaskId(runningTaskTarget.taskId)
                 .setComponentName(taskInfo.topActivity)
                 .setLeash(runningTaskTarget.leash.getSurfaceControl())
-                .setSourceRectHint(TaskInfoCompat.getPipSourceRectHint(
-                        runningTaskTarget.taskInfo.pictureInPictureParams))
-                .setAppBounds(TaskInfoCompat.getWindowConfigurationBounds(taskInfo))
+                .setSourceRectHint(
+                        runningTaskTarget.taskInfo.pictureInPictureParams.getSourceRectHint())
+                .setAppBounds(taskInfo.configuration.windowConfiguration.getBounds())
                 .setHomeToWindowPositionMap(homeToWindowPositionMap)
                 .setStartBounds(startRect)
                 .setDestinationBounds(destinationBounds)
@@ -1275,7 +1284,8 @@
         // is not ROTATION_0 (which implies the rotation is turned on in launcher settings).
         if (homeRotation == ROTATION_0
                 && (windowRotation == ROTATION_90 || windowRotation == ROTATION_270)) {
-            builder.setFromRotation(mTaskViewSimulator, windowRotation);
+            builder.setFromRotation(mTaskViewSimulator, windowRotation,
+                    taskInfo.displayCutoutInsets);
         }
         final SwipePipToHomeAnimator swipePipToHomeAnimator = builder.build();
         AnimatorPlaybackController activityAnimationToHome =
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 1412b1a..389509f 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -396,6 +396,10 @@
         default boolean isRecentsAttachedToAppWindow() {
             return false;
         }
+
+        default boolean hasRecentsEverAttachedToAppWindow() {
+            return false;
+        }
     }
 
     class DefaultAnimationFactory implements AnimationFactory {
@@ -405,6 +409,7 @@
         private final Consumer<AnimatorControllerWithResistance> mCallback;
 
         private boolean mIsAttachedToWindow;
+        private boolean mHasEverAttachedToWindow;
 
         DefaultAnimationFactory(Consumer<AnimatorControllerWithResistance> callback) {
             mCallback = callback;
@@ -458,6 +463,9 @@
             }
             mIsAttachedToWindow = attached;
             RecentsView recentsView = mActivity.getOverviewPanel();
+            if (attached) {
+                mHasEverAttachedToWindow = true;
+            }
             Animator fadeAnim = mActivity.getStateManager()
                     .createStateElementAnimation(INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0);
 
@@ -487,6 +495,11 @@
             return mIsAttachedToWindow;
         }
 
+        @Override
+        public boolean hasRecentsEverAttachedToAppWindow() {
+            return mHasEverAttachedToWindow;
+        }
+
         protected void createBackgroundToOverviewAnim(ACTIVITY_TYPE activity, PendingAnimation pa) {
             //  Scale down recents from being full screen to being in overview.
             RecentsView recentsView = activity.getOverviewPanel();
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index d963188..3bf79f1 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -36,6 +36,7 @@
 import com.android.quickstep.FallbackActivityInterface;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.RecentsActivity;
+import com.android.quickstep.util.TaskViewSimulator;
 import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.SplitPlaceholderView;
@@ -89,8 +90,9 @@
      */
     @Override
     public void onPrepareGestureEndAnimation(
-            @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget) {
-        super.onPrepareGestureEndAnimation(animatorSet, endTarget);
+            @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget,
+            TaskViewSimulator taskViewSimulator) {
+        super.onPrepareGestureEndAnimation(animatorSet, endTarget, taskViewSimulator);
         if (mHomeTaskInfo != null && endTarget == RECENTS && animatorSet != null) {
             TaskView tv = getTaskView(mHomeTaskInfo.taskId);
             if (tv != null) {
diff --git a/quickstep/src/com/android/quickstep/util/AssistantUtilities.java b/quickstep/src/com/android/quickstep/util/AssistantUtilities.java
index 552db1f..336f7d1 100644
--- a/quickstep/src/com/android/quickstep/util/AssistantUtilities.java
+++ b/quickstep/src/com/android/quickstep/util/AssistantUtilities.java
@@ -23,7 +23,6 @@
 import android.os.Build;
 
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.TaskInfoCompat;
 
 /**
  * Utility class for interacting with the Assistant.
@@ -39,7 +38,7 @@
     /** Returns true if the given task holds an Assistant activity that is excluded from recents. */
     public static boolean isExcludedAssistant(TaskInfo info) {
         return info != null
-            && TaskInfoCompat.getActivityType(info) == ACTIVITY_TYPE_ASSISTANT
+            && info.configuration.windowConfiguration.getActivityType() == ACTIVITY_TYPE_ASSISTANT
             && (info.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index 7488649..c0f5c14 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -301,6 +301,7 @@
         private ComponentName mComponentName;
         private SurfaceControl mLeash;
         private Rect mSourceRectHint;
+        private Rect mDisplayCutoutInsets;
         private Rect mAppBounds;
         private Matrix mHomeToWindowPositionMap;
         private RectF mStartBounds;
@@ -366,7 +367,8 @@
         }
 
         public Builder setFromRotation(TaskViewSimulator taskViewSimulator,
-                @RecentsOrientedState.SurfaceRotation int fromRotation) {
+                @RecentsOrientedState.SurfaceRotation int fromRotation,
+                Rect displayCutoutInsets) {
             if (fromRotation != Surface.ROTATION_90 && fromRotation != Surface.ROTATION_270) {
                 Log.wtf(TAG, "Not a supported rotation, rotation=" + fromRotation);
                 return this;
@@ -381,6 +383,9 @@
             transformed.round(mDestinationBoundsTransformed);
 
             mFromRotation = fromRotation;
+            if (displayCutoutInsets != null) {
+                mDisplayCutoutInsets = new Rect(displayCutoutInsets);
+            }
             return this;
         }
 
@@ -388,6 +393,14 @@
             if (mDestinationBoundsTransformed.isEmpty()) {
                 mDestinationBoundsTransformed.set(mDestinationBounds);
             }
+            // adjust the mSourceRectHint / mAppBounds by display cutout if applicable.
+            if (mSourceRectHint != null && mDisplayCutoutInsets != null) {
+                if (mFromRotation == Surface.ROTATION_90) {
+                    mSourceRectHint.offset(mDisplayCutoutInsets.left, mDisplayCutoutInsets.top);
+                } else if (mFromRotation == Surface.ROTATION_270) {
+                    mAppBounds.inset(mDisplayCutoutInsets);
+                }
+            }
             return new SwipePipToHomeAnimator(mContext, mTaskId, mComponentName, mLeash,
                     mSourceRectHint, mAppBounds,
                     mHomeToWindowPositionMap, mStartBounds, mDestinationBounds,
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index efac775..9d10b54 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1111,6 +1111,35 @@
         }
     }
 
+    /**
+     * Moves the focused task to the front of the carousel in tablets, to minimize animation
+     * required to focus the task in grid.
+     */
+    public void moveFocusedTaskToFront() {
+        if (!(mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get())) {
+            return;
+        }
+
+        TaskView focusedTaskView = getFocusedTaskView();
+        if (focusedTaskView == null) {
+            return;
+        }
+
+        if (indexOfChild(focusedTaskView) != mCurrentPage) {
+            return;
+        }
+
+        int primaryScroll = mOrientationHandler.getPrimaryScroll(this);
+        int currentPageScroll = getScrollForPage(mCurrentPage);
+        mCurrentPageScrollDiff = primaryScroll - currentPageScroll;
+
+        removeView(focusedTaskView);
+        addView(focusedTaskView, mTaskViewStartIndex);
+        setCurrentPage(0);
+
+        updateGridProperties();
+    }
+
     protected void applyLoadPlan(ArrayList<Task> tasks) {
         if (mPendingAnimation != null) {
             mPendingAnimation.addEndListener(success -> applyLoadPlan(tasks));
@@ -1769,24 +1798,33 @@
      * Called when a gesture from an app has finished, and an end target has been determined.
      */
     public void onPrepareGestureEndAnimation(
-            @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget) {
+            @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget,
+            TaskViewSimulator taskViewSimulator) {
+        mCurrentGestureEndTarget = endTarget;
+        if (endTarget == GestureState.GestureEndTarget.RECENTS) {
+            setEnableFreeScroll(true);
+            updateGridProperties();
+        }
+
         if (mSizeStrategy.stateFromGestureEndTarget(endTarget)
                 .displayOverviewTasksAsGrid(mActivity.getDeviceProfile())) {
+            TaskView runningTaskView = getRunningTaskView();
+            float runningTaskPrimaryGridTranslation = 0;
+            if (indexOfChild(runningTaskView) != getNextPage()) {
+                // Apply the gird translation to running task unless it's being snapped to.
+                runningTaskPrimaryGridTranslation = mOrientationHandler.getPrimaryValue(
+                        runningTaskView.getGridTranslationX(),
+                        runningTaskView.getGridTranslationY());
+            }
             if (animatorSet == null) {
                 setGridProgress(1);
+                taskViewSimulator.taskPrimaryTranslation.value = runningTaskPrimaryGridTranslation;
             } else {
                 animatorSet.play(ObjectAnimator.ofFloat(this, RECENTS_GRID_PROGRESS, 1));
+                animatorSet.play(taskViewSimulator.taskPrimaryTranslation.animateToValue(
+                        runningTaskPrimaryGridTranslation));
             }
         }
-        mCurrentGestureEndTarget = endTarget;
-        if (endTarget == GestureState.GestureEndTarget.NEW_TASK
-                || endTarget == GestureState.GestureEndTarget.LAST_TASK) {
-            // When switching to tasks in quick switch, ensures the snapped page's scroll maintain
-            // invariant between quick switch and overview, to ensure a smooth animation transition.
-            updateGridProperties();
-        } else if (endTarget == GestureState.GestureEndTarget.RECENTS) {
-            setEnableFreeScroll(true);
-        }
     }
 
     /**
@@ -2073,12 +2111,6 @@
             snappedTaskGridTranslationX = gridTranslations[snappedPage - mTaskViewStartIndex];
         }
 
-        for (int i = 0; i < taskCount; i++) {
-            TaskView taskView = getTaskViewAt(i);
-            taskView.setGridTranslationX(gridTranslations[i] - snappedTaskGridTranslationX
-                    + snappedTaskNonGridScrollAdjustment);
-        }
-
         // Use the accumulated translation of the row containing the last task.
         float clearAllAccumulatedTranslation = topSet.contains(taskCount - 1)
                 ? topAccumulatedTranslationX : bottomAccumulatedTranslationX;
@@ -2121,17 +2153,23 @@
         // Make sure there are enough space between snapped page and ClearAllButton, for the case
         // of swiping up after quick switch.
         if (snappedTaskView != null) {
-            int distanceFromClearAll = longRowWidth - snappedTaskRowWidth;
+            int distanceFromClearAll = longRowWidth - snappedTaskRowWidth + mPageSpacing;
             // ClearAllButton should be off screen when snapped task is in its snapped position.
             int minimumDistance =
                     mTaskWidth - snappedTaskView.getLayoutParams().width
                             + (mLastComputedGridSize.width() - mTaskWidth) / 2;
             if (distanceFromClearAll < minimumDistance) {
                 int distanceDifference = minimumDistance - distanceFromClearAll;
-                clearAllTotalTranslationX += mIsRtl ? -distanceDifference : distanceDifference;
+                snappedTaskGridTranslationX += mIsRtl ? distanceDifference : -distanceDifference;
             }
         }
 
+        for (int i = 0; i < taskCount; i++) {
+            TaskView taskView = getTaskViewAt(i);
+            taskView.setGridTranslationX(gridTranslations[i] - snappedTaskGridTranslationX
+                    + snappedTaskNonGridScrollAdjustment);
+        }
+
         mClearAllButton.setGridTranslationPrimary(
                 clearAllTotalTranslationX - snappedTaskGridTranslationX);
         mClearAllButton.setGridScrollOffset(
@@ -2495,12 +2533,15 @@
                     resetTaskVisuals();
 
                     int pageToSnapTo = mCurrentPage;
-                    if (finalNextFocusedTaskView != null) {
-                        pageToSnapTo = indexOfChild(finalNextFocusedTaskView);
-                    }
-                    if (dismissedIndex < pageToSnapTo || pageToSnapTo == (taskCount - 1)) {
+                    if ((dismissedIndex < pageToSnapTo && !showAsGrid)
+                            || pageToSnapTo == taskCount - 1) {
                         pageToSnapTo -= 1;
                     }
+                    if (showAsGrid) {
+                        int primaryScroll = mOrientationHandler.getPrimaryScroll(RecentsView.this);
+                        int currentPageScroll = getScrollForPage(pageToSnapTo);
+                        mCurrentPageScrollDiff = primaryScroll - currentPageScroll;
+                    }
                     removeViewInLayout(dismissedTaskView);
                     mTopRowIdSet.remove(dismissedTaskId);
 
@@ -2513,12 +2554,11 @@
                             mFocusedTaskId = finalNextFocusedTaskView.getTaskId();
                             mTopRowIdSet.remove(mFocusedTaskId);
                             finalNextFocusedTaskView.animateIconScaleAndDimIntoView();
-                            setCurrentPage(pageToSnapTo);
                         }
                         updateTaskSize(true);
                         // Update scroll and snap to page.
                         updateScrollSynchronously();
-                        snapToPageImmediately(pageToSnapTo);
+                        setCurrentPage(pageToSnapTo);
                         dispatchScrollChanged();
                     }
                 }
@@ -2860,6 +2900,7 @@
         mLastComputedTaskStartPushOutDistance = null;
         mLastComputedTaskEndPushOutDistance = null;
         updatePageOffsets();
+        mLiveTileTaskViewSimulator.setScroll(getScrollOffset());
         setImportantForAccessibility(isModal() ? IMPORTANT_FOR_ACCESSIBILITY_NO
                 : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
     }
@@ -3622,18 +3663,28 @@
         }
 
         boolean pageScrollChanged = false;
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            float scrollDiff = 0;
-            if (child instanceof TaskView) {
-                scrollDiff = ((TaskView) child).getScrollAdjustment(showAsFullscreen, showAsGrid);
-            } else if (child instanceof ClearAllButton) {
-                scrollDiff = ((ClearAllButton) child).getScrollAdjustment(showAsFullscreen,
-                        showAsGrid);
-            }
 
-            final int pageScroll = newPageScrolls[i] + (int) scrollDiff;
+        int clearAllIndex = indexOfChild(mClearAllButton);
+        int clearAllScroll = 0;
+        int clearAllWidth = mOrientationHandler.getPrimarySize(mClearAllButton);
+        if (clearAllIndex != -1 && clearAllIndex < outPageScrolls.length) {
+            float scrollDiff = mClearAllButton.getScrollAdjustment(showAsFullscreen, showAsGrid);
+            clearAllScroll = newPageScrolls[clearAllIndex] + (int) scrollDiff;
+            if (outPageScrolls[clearAllIndex] != clearAllScroll) {
+                pageScrollChanged = true;
+                outPageScrolls[clearAllIndex] = clearAllScroll;
+            }
+        }
+
+        final int taskCount = getTaskViewCount();
+        for (int i = 0; i < taskCount; i++) {
+            TaskView taskView = getTaskViewAt(i);
+            float scrollDiff = taskView.getScrollAdjustment(showAsFullscreen, showAsGrid);
+            int pageScroll = newPageScrolls[i + mTaskViewStartIndex] + (int) scrollDiff;
+            if ((mIsRtl && pageScroll < clearAllScroll)
+                    || (!mIsRtl && pageScroll > clearAllScroll)) {
+                pageScroll = clearAllScroll + (mIsRtl ? clearAllWidth : -clearAllWidth);
+            }
             if (outPageScrolls[i] != pageScroll) {
                 pageScrollChanged = true;
                 outPageScrolls[i] = pageScroll;
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 204913a..1555e98 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -104,6 +104,10 @@
 
     @ViewDebug.ExportedProperty(category = "launcher")
     protected int mCurrentPage;
+    // Difference between current scroll position and mCurrentPage's page scroll. Used to maintain
+    // relative scroll position unchanged in updateCurrentPageScroll. Cleared when snapping to a
+    // page.
+    protected int mCurrentPageScrollDiff;
 
     @ViewDebug.ExportedProperty(category = "launcher")
     protected int mNextPage = INVALID_PAGE;
@@ -247,7 +251,7 @@
         // If the current page is invalid, just reset the scroll position to zero
         int newPosition = 0;
         if (0 <= mCurrentPage && mCurrentPage < getPageCount()) {
-            newPosition = getScrollForPage(mCurrentPage);
+            newPosition = getScrollForPage(mCurrentPage) + mCurrentPageScrollDiff;
         }
         mOrientationHandler.set(this, VIEW_SCROLL_TO, newPosition);
         mScroller.startScroll(mScroller.getCurrX(), 0, newPosition - mScroller.getCurrX(), 0);
@@ -452,6 +456,7 @@
      * to provide custom behavior during animation.
      */
     protected void onPageEndTransition() {
+        mCurrentPageScrollDiff = 0;
         AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
         AccessibilityManagerCompat.sendCustomAccessibilityEvent(getPageAt(mCurrentPage),
                 AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 1f93730..f2ab96c 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -148,6 +148,12 @@
 
         addView(content, new LayoutParams(width, height));
 
+        // If there is already a scale set on the content, we don't want to clip the children.
+        if (content.getScaleX() != 1 || content.getScaleY() != 1) {
+            setClipChildren(false);
+            setClipToPadding(false);
+        }
+
         final float scale = (width + finalScaleDps) / width;
 
         // Set the initial scale to avoid any jumps
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 5759f75..edcab4e 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -16,12 +16,14 @@
 
 package com.android.launcher3.widget;
 
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
 import static com.android.launcher3.Utilities.ATLEAST_S;
 
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.CancellationSignal;
 import android.util.AttributeSet;
@@ -31,6 +33,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnLayoutChangeListener;
+import android.view.ViewGroup;
 import android.view.ViewPropertyAnimator;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
@@ -71,11 +74,36 @@
     /** Widget preview width is calculated by multiplying this factor to the widget cell width. */
     private static final float PREVIEW_SCALE = 0.8f;
 
-    protected int mPreviewWidth;
-    protected int mPreviewHeight;
+    /**
+     * The maximum dimension that can be used as the size in
+     * {@link android.view.View.MeasureSpec#makeMeasureSpec(int, int)}.
+     *
+     * <p>This is equal to (1 << MeasureSpec.MODE_SHIFT) - 1.
+     */
+    private static final int MAX_MEASURE_SPEC_DIMENSION = (1 << 30) - 1;
+
+    /**
+     * The target preview width, in pixels, of a widget or a shortcut.
+     *
+     * <p>The actual preview width may be smaller than or equal to this value subjected to scaling.
+     */
+    protected int mTargetPreviewWidth;
+
+    /**
+     * The target preview height, in pixels, of a widget or a shortcut.
+     *
+     * <p>The actual preview height may be smaller than or equal to this value subjected to scaling.
+     */
+    protected int mTargetPreviewHeight;
+
     protected int mPresetPreviewSize;
+
     private int mCellSize;
-    private float mPreviewScale = 1f;
+
+    /**
+     * The scale of the preview container.
+     */
+    private float mPreviewContainerScale = 1f;
 
     private FrameLayout mWidgetImageContainer;
     private WidgetImageView mWidgetImage;
@@ -96,7 +124,6 @@
     protected final BaseActivity mActivity;
     private final CheckLongPressHelper mLongPressHelper;
     private final float mEnforcedCornerRadius;
-    private final int mShortcutPreviewPadding;
 
     private RemoteViews mRemoteViewsPreview;
     private NavigableAppWidgetHostView mAppWidgetHostViewPreview;
@@ -122,14 +149,12 @@
         setClipToPadding(false);
         setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
         mEnforcedCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(context);
-        mShortcutPreviewPadding =
-                2 * getResources().getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
     }
 
     private void setContainerWidth() {
         mCellSize = (int) (mActivity.getDeviceProfile().allAppsIconSizePx * WIDTH_SCALE);
         mPresetPreviewSize = (int) (mCellSize * PREVIEW_SCALE);
-        mPreviewWidth = mPreviewHeight = mPresetPreviewSize;
+        mTargetPreviewWidth = mTargetPreviewHeight = mPresetPreviewSize;
     }
 
     @Override
@@ -166,7 +191,7 @@
         mWidgetDims.setText(null);
         mWidgetDescription.setText(null);
         mWidgetDescription.setVisibility(GONE);
-        mPreviewWidth = mPreviewHeight = mPresetPreviewSize;
+        mTargetPreviewWidth = mTargetPreviewHeight = mPresetPreviewSize;
 
         if (mActiveRequest != null) {
             mActiveRequest.cancel();
@@ -248,16 +273,6 @@
             @Nullable RemoteViews remoteViews) {
         appWidgetHostViewPreview.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
         appWidgetHostViewPreview.setAppWidget(/* appWidgetId= */ -1, providerInfo);
-        Rect padding;
-        DeviceProfile deviceProfile = mActivity.getDeviceProfile();
-        if (deviceProfile.shouldInsetWidgets()) {
-            padding = new Rect();
-            appWidgetHostViewPreview.getWidgetInset(deviceProfile, padding);
-        } else {
-            padding = deviceProfile.inv.defaultWidgetPadding;
-        }
-        appWidgetHostViewPreview.setPadding(padding.left, padding.top, padding.right,
-                padding.bottom);
         appWidgetHostViewPreview.updateAppWidget(remoteViews);
     }
 
@@ -305,7 +320,7 @@
             if (getWidth() > 0 && getHeight() > 0) {
                 // Scale down the preview size if it's wider than the cell.
                 float maxWidth = getWidth();
-                float previewWidth = drawable.getIntrinsicWidth() * mPreviewScale;
+                float previewWidth = drawable.getIntrinsicWidth() * mPreviewContainerScale;
                 scale = Math.min(maxWidth / previewWidth, 1);
             }
             setContainerSize(
@@ -329,16 +344,32 @@
 
     private void setContainerSize(int width, int height) {
         LayoutParams layoutParams = (LayoutParams) mWidgetImageContainer.getLayoutParams();
-        layoutParams.width = (int) (width * mPreviewScale);
-        layoutParams.height = (int) (height * mPreviewScale);
+        layoutParams.width = width;
+        layoutParams.height = height;
         mWidgetImageContainer.setLayoutParams(layoutParams);
     }
 
     public void ensurePreview() {
         if (mAppWidgetHostViewPreview != null) {
-            setContainerSize(mPreviewWidth, mPreviewHeight);
+            int containerWidth = (int) (mTargetPreviewWidth * mPreviewContainerScale);
+            int containerHeight = (int) (mTargetPreviewHeight * mPreviewContainerScale);
+            setContainerSize(containerWidth, containerHeight);
+            if (mAppWidgetHostViewPreview.getChildCount() == 1) {
+                View widgetContent = mAppWidgetHostViewPreview.getChildAt(0);
+                ViewGroup.LayoutParams layoutParams = widgetContent.getLayoutParams();
+                // We only scale preview if both the width & height of the outermost view group are
+                // not set to MATCH_PARENT.
+                boolean shouldScale =
+                        layoutParams.width != MATCH_PARENT && layoutParams.height != MATCH_PARENT;
+                if (shouldScale) {
+                    setNoClip(mWidgetImageContainer);
+                    setNoClip(mAppWidgetHostViewPreview);
+                    float previewLayoutScale = computeWidgetPreviewScale();
+                    mAppWidgetHostViewPreview.setScaleToFit(previewLayoutScale);
+                }
+            }
             FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
-                    mPreviewWidth, mPreviewHeight, Gravity.FILL);
+                    containerWidth, containerHeight, Gravity.FILL);
             mAppWidgetHostViewPreview.setLayoutParams(params);
             mWidgetImageContainer.addView(mAppWidgetHostViewPreview, /* index= */ 0);
             mWidgetImage.setVisibility(View.GONE);
@@ -350,7 +381,7 @@
         }
         mActiveRequest = mWidgetPreviewLoader.loadPreview(
                 BaseActivity.fromContext(getContext()), mItem,
-                new Size(mPreviewWidth, mPreviewHeight),
+                new Size(mTargetPreviewWidth, mTargetPreviewHeight),
                 this::applyPreview);
     }
 
@@ -363,9 +394,9 @@
     public Size setPreviewSize(WidgetItem widgetItem, float previewScale) {
         DeviceProfile deviceProfile = mActivity.getDeviceProfile();
         Size widgetSize = WidgetSizes.getWidgetItemSizePx(getContext(), deviceProfile, widgetItem);
-        mPreviewWidth = widgetSize.getWidth();
-        mPreviewHeight = widgetSize.getHeight();
-        mPreviewScale = previewScale;
+        mTargetPreviewWidth = widgetSize.getWidth();
+        mTargetPreviewHeight = widgetSize.getHeight();
+        mPreviewContainerScale = previewScale;
         return widgetSize;
     }
 
@@ -410,4 +441,35 @@
         super.onInitializeAccessibilityNodeInfo(info);
         info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
     }
+
+    private static void setNoClip(ViewGroup view) {
+        view.setClipChildren(false);
+        view.setClipToPadding(false);
+    }
+
+    private float computeWidgetPreviewScale() {
+        if (mAppWidgetHostViewPreview.getChildCount() != 1) {
+            return 1f;
+        }
+
+        // Measure the largest possible width & height that the app widget wants to display.
+        mAppWidgetHostViewPreview.measure(
+                makeMeasureSpec(MAX_MEASURE_SPEC_DIMENSION, MeasureSpec.UNSPECIFIED),
+                makeMeasureSpec(MAX_MEASURE_SPEC_DIMENSION, MeasureSpec.UNSPECIFIED));
+        int appWidgetContentWidth = mAppWidgetHostViewPreview.getChildAt(0).getMeasuredWidth();
+        int appWidgetContentHeight = mAppWidgetHostViewPreview.getChildAt(0).getMeasuredHeight();
+        if (appWidgetContentWidth == 0 || appWidgetContentHeight == 0) {
+            return 1f;
+        }
+
+        int horizontalPadding = mAppWidgetHostViewPreview.getPaddingStart()
+                + mAppWidgetHostViewPreview.getPaddingEnd();
+        int verticalPadding = mAppWidgetHostViewPreview.getPaddingTop()
+                + mAppWidgetHostViewPreview.getPaddingBottom();
+        return Math.min(
+                (mTargetPreviewWidth - horizontalPadding) * mPreviewContainerScale
+                        / appWidgetContentWidth,
+                (mTargetPreviewHeight - verticalPadding) * mPreviewContainerScale
+                        / appWidgetContentHeight);
+    }
 }
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 0c9c463..4978c01 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -102,8 +102,7 @@
 
         setResult(acceptConfig);
         if (acceptConfig) {
-            // Dismiss widget resize frame.
-            mDevice.pressHome();
+            // TODO(b/192655785) Assert widget resize frame is shown and then dismiss it.
             Wait.atMost("", new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
             assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
         } else {
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 1cb6b2d..78301e4 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -135,6 +135,7 @@
                                         .collect(Collectors.toList()),
                                 mLauncher.getVisibleBounds(searchBox).bottom
                                         - mLauncher.getVisibleBounds(allAppsContainer).top);
+                        verifyActiveContainer();
                         final int newScroll = getAllAppsScroll();
                         mLauncher.assertTrue(
                                 "Scrolled in a wrong direction in AllApps: from " + scroll + " to "
@@ -144,7 +145,6 @@
                         mLauncher.assertTrue(
                                 "Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
                                 ++attempts <= MAX_SCROLL_ATTEMPTS);
-                        verifyActiveContainer();
                         scroll = newScroll;
                     }
                 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 75b87b7..b290bb1 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -71,6 +71,10 @@
         return mLauncher.isTablet();
     }
 
+    protected boolean zeroButtonToOverviewGestureStateTransitionWhileHolding() {
+        return false;
+    }
+
     protected void goToOverviewUnchecked() {
         switch (mLauncher.getNavigationModel()) {
             case ZERO_BUTTON: {
@@ -90,21 +94,32 @@
 
                 mLauncher.sendPointer(
                         downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
-                mLauncher.executeAndWaitForLauncherEvent(
-                        () -> mLauncher.movePointer(
-                                downTime,
-                                downTime,
-                                ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION,
-                                start,
-                                end,
-                                gestureScope),
-                        event -> TestProtocol.PAUSE_DETECTED_MESSAGE.equals(event.getClassName()),
-                        () -> "Pause wasn't detected", "swiping and holding");
-                mLauncher.runToState(
-                        () -> mLauncher.sendPointer(
-                                downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, end,
-                                gestureScope),
-                        OVERVIEW_STATE_ORDINAL, "sending UP event");
+                Runnable swipeAndHold = () -> mLauncher.movePointer(
+                        downTime,
+                        downTime,
+                        ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION,
+                        start,
+                        end,
+                        gestureScope);
+                String swipeAndHoldAction = "swiping and holding";
+                Runnable up = () -> mLauncher.sendPointer(
+                        downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, end,
+                        gestureScope);
+                String upAction = "sending UP event";
+                if (zeroButtonToOverviewGestureStateTransitionWhileHolding()) {
+                    mLauncher.runToState(swipeAndHold, OVERVIEW_STATE_ORDINAL, swipeAndHoldAction);
+                    try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(upAction)) {
+                        up.run();
+                    }
+                } else {
+                    mLauncher.executeAndWaitForLauncherEvent(
+                            swipeAndHold,
+                            event -> TestProtocol.PAUSE_DETECTED_MESSAGE.equals(
+                                    event.getClassName()),
+                            () -> "Pause wasn't detected",
+                            swipeAndHoldAction);
+                    mLauncher.runToState(up, OVERVIEW_STATE_ORDINAL, upAction);
+                }
                 break;
             }
 
diff --git a/tests/tapl/com/android/launcher3/tapl/Home.java b/tests/tapl/com/android/launcher3/tapl/Home.java
index 0060844..ee9dd1a 100644
--- a/tests/tapl/com/android/launcher3/tapl/Home.java
+++ b/tests/tapl/com/android/launcher3/tapl/Home.java
@@ -63,4 +63,8 @@
         return true;
     }
 
+    @Override
+    protected boolean zeroButtonToOverviewGestureStateTransitionWhileHolding() {
+        return true;
+    }
 }
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 9d6cb4e..c4d46ee 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -505,7 +505,7 @@
         checkForAnomaly();
         Assert.fail(formatSystemHealthMessage(formatErrorWithEvents(
                 "http://go/tapl test failure:\nContext: " + getContextDescription()
-                        + " - visible state is " + getVisibleStateMessage()
+                        + " => resulting visible state is " + getVisibleStateMessage()
                         + ";\nDetails: " + message, true)));
     }
 
@@ -699,7 +699,8 @@
      * @return the Workspace object.
      */
     public Workspace pressHome() {
-        try (LauncherInstrumentation.Closable e = eventsCheck()) {
+        try (LauncherInstrumentation.Closable e = eventsCheck();
+             LauncherInstrumentation.Closable c = addContextLayer("want to switch to home")) {
             waitForLauncherInitialized();
             // Click home, then wait for any accessibility event, then wait until accessibility
             // events stop.
@@ -719,7 +720,7 @@
                             displaySize.x / 2, 0,
                             ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME,
                             false, GestureScope.INSIDE_TO_OUTSIDE);
-                    try (LauncherInstrumentation.Closable c = addContextLayer(
+                    try (LauncherInstrumentation.Closable c1 = addContextLayer(
                             "Swiped up from context menu to home")) {
                         waitUntilLauncherObjectGone(CONTEXT_MENU_RES_ID);
                         // Swiping up can temporarily bring Nexus Launcher if the current
@@ -735,6 +736,7 @@
                     dumpViewHierarchy();
                     action = "swiping up to home";
 
+                    final boolean launcherIsVisible = isLauncherVisible();
                     swipeToState(
                             displaySize.x / 2, displaySize.y - 1,
                             displaySize.x / 2, 0,
@@ -742,6 +744,9 @@
                             launcherWasVisible
                                     ? GestureScope.INSIDE_TO_OUTSIDE
                                     : GestureScope.OUTSIDE_WITH_PILFER);
+                    // b/193653850: launcherWasVisible is a flaky indicator.
+                    log("launcherWasVisible: " + launcherWasVisible + ", launcherIsVisible: "
+                            + launcherIsVisible);
                 }
             } else {
                 log("Hierarchy before clicking home:");
@@ -764,7 +769,7 @@
                                 || hasLauncherObject(OVERVIEW_RES_ID)),
                         action);
             }
-            try (LauncherInstrumentation.Closable c = addContextLayer(
+            try (LauncherInstrumentation.Closable c1 = addContextLayer(
                     "performed action to switch to Home - " + action)) {
                 return getWorkspace();
             }