Merge "Treat RTL split placeholder animation same as LTR" into sc-v2-dev
diff --git a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
index 90c035f..f1e6747 100644
--- a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
@@ -60,6 +60,7 @@
 
     @Override
     protected void onDestroy() {
+        super.onDestroy();
         mRecentsActivity.setTaskbarUIController(null);
         mRecentsActivity.getStateManager().removeStateListener(mStateListener);
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 7d23439..2622700 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -99,6 +99,7 @@
 
     @Override
     protected void onDestroy() {
+        super.onDestroy();
         onLauncherResumedOrPaused(false);
         mTaskbarLauncherStateController.onDestroy();
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 0ab7756..ce1e8b6b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -31,6 +31,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 
 import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
@@ -86,6 +87,7 @@
     private static final int FLAG_DISABLE_RECENTS = 1 << 8;
     private static final int FLAG_DISABLE_BACK = 1 << 9;
     private static final int FLAG_NOTIFICATION_SHADE_EXPANDED = 1 << 10;
+    private static final int FLAG_SCREEN_PINNING_ACTIVE = 1 << 10;
 
     private static final int MASK_IME_SWITCHER_VISIBLE = FLAG_SWITCHER_SUPPORTED | FLAG_IME_VISIBLE;
 
@@ -152,7 +154,9 @@
         mPropertyHolders.add(new StatePropertyHolder(
                 mControllers.taskbarViewController.getTaskbarIconAlpha()
                         .getProperty(ALPHA_INDEX_KEYGUARD),
-                flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0, MultiValueAlpha.VALUE, 1, 0));
+                flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0
+                        && (flags & FLAG_SCREEN_PINNING_ACTIVE) == 0,
+                MultiValueAlpha.VALUE, 1, 0));
 
         mPropertyHolders.add(new StatePropertyHolder(mControllers.taskbarDragLayerController
                 .getKeyguardBgTaskbar(),
@@ -286,6 +290,7 @@
         int shadeExpandedFlags = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
                 | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
         boolean isNotificationShadeExpanded = (sysUiStateFlags & shadeExpandedFlags) != 0;
+        boolean isScreenPinningActive = (sysUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0;
 
         // TODO(b/202218289) we're getting IME as not visible on lockscreen from system
         updateStateForFlag(FLAG_IME_VISIBLE, isImeVisible);
@@ -295,6 +300,7 @@
         updateStateForFlag(FLAG_DISABLE_RECENTS, isRecentsDisabled);
         updateStateForFlag(FLAG_DISABLE_BACK, isBackDisabled);
         updateStateForFlag(FLAG_NOTIFICATION_SHADE_EXPANDED, isNotificationShadeExpanded);
+        updateStateForFlag(FLAG_SCREEN_PINNING_ACTIVE, isScreenPinningActive);
 
         if (mA11yButton != null) {
             // Only used in 3 button
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index cb9d4a4..692352b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -361,6 +361,7 @@
         mControllers.taskbarStashController.updateStateForSysuiFlags(systemUiStateFlags, fromInit);
         mControllers.taskbarScrimViewController.updateStateForSysuiFlags(systemUiStateFlags,
                 fromInit);
+        mControllers.navButtonController.updateSysuiFlags(systemUiStateFlags);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 56e9429..3cdcdf7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -29,8 +29,8 @@
 import android.content.res.Configuration;
 import android.hardware.display.DisplayManager;
 import android.net.Uri;
+import android.os.Handler;
 import android.provider.Settings;
-import android.util.Log;
 import android.view.Display;
 
 import androidx.annotation.NonNull;
@@ -93,7 +93,8 @@
         Display display =
                 service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
         mContext = service.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null);
-        mNavButtonController = new TaskbarNavButtonController(service);
+        mNavButtonController = new TaskbarNavButtonController(service,
+                SystemUiProxy.INSTANCE.get(mContext), new Handler());
         mUserSetupCompleteListener = isUserSetupComplete -> recreateTaskbar();
         mComponentCallbacks = new ComponentCallbacks() {
             private Configuration mOldConfig = mContext.getResources().getConfiguration();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index ae23eda..d233365 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -19,8 +19,10 @@
 
 import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS;
 import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_KEY;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 
 import android.os.Bundle;
+import android.os.Handler;
 
 import androidx.annotation.IntDef;
 
@@ -40,6 +42,13 @@
  */
 public class TaskbarNavButtonController {
 
+    /** Allow some time in between the long press for back and recents. */
+    static final int SCREEN_PIN_LONG_PRESS_THRESHOLD = 200;
+    static final int SCREEN_PIN_LONG_PRESS_RESET = SCREEN_PIN_LONG_PRESS_THRESHOLD + 100;
+
+    private long mLastScreenPinLongPress;
+    private boolean mScreenPinned;
+
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(value = {
             BUTTON_BACK,
@@ -57,10 +66,20 @@
     static final int BUTTON_IME_SWITCH = BUTTON_RECENTS << 1;
     static final int BUTTON_A11Y = BUTTON_IME_SWITCH << 1;
 
-    private final TouchInteractionService mService;
+    private static final int SCREEN_UNPIN_COMBO = BUTTON_BACK | BUTTON_RECENTS;
+    private int mLongPressedButtons = 0;
 
-    public TaskbarNavButtonController(TouchInteractionService service) {
+    private final TouchInteractionService mService;
+    private final SystemUiProxy mSystemUiProxy;
+    private final Handler mHandler;
+
+    private final Runnable mResetLongPress = this::resetScreenUnpin;
+
+    public TaskbarNavButtonController(TouchInteractionService service,
+            SystemUiProxy systemUiProxy, Handler handler) {
         mService = service;
+        mSystemUiProxy = systemUiProxy;
+        mHandler = handler;
     }
 
     public void onButtonClick(@TaskbarButton int buttonType) {
@@ -72,13 +91,13 @@
                 navigateHome();
                 break;
             case BUTTON_RECENTS:
-                navigateToOverview();;
+                navigateToOverview();
                 break;
             case BUTTON_IME_SWITCH:
                 showIMESwitcher();
                 break;
             case BUTTON_A11Y:
-                notifyImeClick(false /* longClick */);
+                notifyA11yClick(false /* longClick */);
                 break;
         }
     }
@@ -89,46 +108,98 @@
                 startAssistant();
                 return true;
             case BUTTON_A11Y:
-                notifyImeClick(true /* longClick */);
+                notifyA11yClick(true /* longClick */);
                 return true;
             case BUTTON_BACK:
-            case BUTTON_IME_SWITCH:
             case BUTTON_RECENTS:
+                mLongPressedButtons |= buttonType;
+                return determineScreenUnpin();
+            case BUTTON_IME_SWITCH:
             default:
                 return false;
         }
     }
 
+    /**
+     * Checks if the user has long pressed back and recents buttons
+     * "together" (within {@link #SCREEN_PIN_LONG_PRESS_THRESHOLD})ms
+     * If so, then requests the system to turn off screen pinning.
+     *
+     * @return true if the long press is a valid user action in attempting to unpin an app
+     *         Will always return {@code false} when screen pinning is not active.
+     *         NOTE: Returning true does not mean that screen pinning has stopped
+     */
+    private boolean determineScreenUnpin() {
+        long timeNow = System.currentTimeMillis();
+        if (!mScreenPinned) {
+            return false;
+        }
+
+        if (mLastScreenPinLongPress == 0) {
+            // First button long press registered, just mark time and wait for second button press
+            mLastScreenPinLongPress = System.currentTimeMillis();
+            mHandler.postDelayed(mResetLongPress, SCREEN_PIN_LONG_PRESS_RESET);
+            return true;
+        }
+
+        if ((timeNow - mLastScreenPinLongPress) > SCREEN_PIN_LONG_PRESS_THRESHOLD) {
+            // Too long in-between presses, reset the clock
+            resetScreenUnpin();
+            return false;
+        }
+
+        if ((mLongPressedButtons & SCREEN_UNPIN_COMBO) == SCREEN_UNPIN_COMBO) {
+            // Hooray! They did it (finally...)
+            mSystemUiProxy.stopScreenPinning();
+            mHandler.removeCallbacks(mResetLongPress);
+            resetScreenUnpin();
+        }
+        return true;
+    }
+
+    private void resetScreenUnpin() {
+        mLongPressedButtons = 0;
+        mLastScreenPinLongPress = 0;
+    }
+
+    public void updateSysuiFlags(int sysuiFlags) {
+        mScreenPinned = (sysuiFlags & SYSUI_STATE_SCREEN_PINNING) != 0;
+    }
+
     private void navigateHome() {
         mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_HOME);
     }
 
     private void navigateToOverview() {
+        if (mScreenPinned) {
+            return;
+        }
         TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
         mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_TOGGLE);
     }
 
     private void executeBack() {
-        SystemUiProxy.INSTANCE.getNoCreate().onBackPressed();
+        mSystemUiProxy.onBackPressed();
     }
 
     private void showIMESwitcher() {
-        SystemUiProxy.INSTANCE.getNoCreate().onImeSwitcherPressed();
+        mSystemUiProxy.onImeSwitcherPressed();
     }
 
-    private void notifyImeClick(boolean longClick) {
-        SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
+    private void notifyA11yClick(boolean longClick) {
         if (longClick) {
-            systemUiProxy.notifyAccessibilityButtonLongClicked();
+            mSystemUiProxy.notifyAccessibilityButtonLongClicked();
         } else {
-            systemUiProxy.notifyAccessibilityButtonClicked(mService.getDisplayId());
+            mSystemUiProxy.notifyAccessibilityButtonClicked(mService.getDisplayId());
         }
     }
 
     private void startAssistant() {
+        if (mScreenPinned) {
+            return;
+        }
         Bundle args = new Bundle();
         args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
-        SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
-        systemUiProxy.startAssistant(args);
+        mSystemUiProxy.startAssistant(args);
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index f713dca..f6bc785 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -18,6 +18,8 @@
 import android.graphics.Rect;
 import android.view.View;
 
+import androidx.annotation.CallSuper;
+
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 
@@ -33,11 +35,15 @@
     // Initialized in init.
     protected TaskbarControllers mControllers;
 
+    @CallSuper
     protected void init(TaskbarControllers taskbarControllers) {
         mControllers = taskbarControllers;
     }
 
-    protected void onDestroy() { }
+    @CallSuper
+    protected void onDestroy() {
+        mControllers = null;
+    }
 
     protected boolean isTaskbarTouchable() {
         return true;
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index a9fef41..6c623bc 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -599,11 +599,11 @@
         }
     }
 
-    public void startShortcut(String packageName, String shortcutId, int stage, int position,
+    public void startShortcut(String packageName, String shortcutId, int position,
             Bundle options, UserHandle user) {
         if (mSplitScreen != null) {
             try {
-                mSplitScreen.startShortcut(packageName, shortcutId, stage, position, options,
+                mSplitScreen.startShortcut(packageName, shortcutId, position, options,
                         user);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call startShortcut");
@@ -611,11 +611,11 @@
         }
     }
 
-    public void startIntent(PendingIntent intent, Intent fillInIntent, int stage, int position,
+    public void startIntent(PendingIntent intent, Intent fillInIntent, int position,
             Bundle options) {
         if (mSplitScreen != null) {
             try {
-                mSplitScreen.startIntent(intent, fillInIntent, stage, position, options);
+                mSplitScreen.startIntent(intent, fillInIntent, position, options);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call startIntent");
             }
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index 00f541d..b43626b 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -157,10 +157,20 @@
     @Nullable
     @Override
     public RunnableList launchTaskAnimated() {
-        getRecentsView().getSplitPlaceholder().launchTasks(this /*groupedTaskView*/,
-                null /*callback*/,
+        if (mTask == null || mSecondaryTask == null) {
+            return null;
+        }
+
+        RunnableList endCallback = new RunnableList();
+        RecentsView recentsView = getRecentsView();
+        // Callbacks run from remote animation when recents animation not currently running
+        recentsView.getSplitPlaceholder().launchTasks(this /*groupedTaskView*/,
+                success -> endCallback.executeAllAndDestroy(),
                 false /* freezeTaskList */);
-        return null;
+
+        // Callbacks get run from recentsView for case when recents animation already running
+        recentsView.addSideTaskLaunchCallback(endCallback);
+        return endCallback;
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 3f212c6..9cf76b3 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -107,6 +107,7 @@
 import android.widget.OverScroller;
 import android.widget.Toast;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 import androidx.core.graphics.ColorUtils;
@@ -181,6 +182,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Objects;
 import java.util.function.Consumer;
 
 /**
@@ -822,7 +824,7 @@
     @Override
     public void onTaskIconChanged(String pkg, UserHandle user) {
         for (int i = 0; i < getTaskViewCount(); i++) {
-            TaskView tv = getTaskViewAt(i);
+            TaskView tv = requireTaskViewAt(i);
             Task task = tv.getTask();
             if (task != null && task.key != null && pkg.equals(task.key.getPackageName())
                     && task.key.userId == user.getIdentifier()) {
@@ -1104,7 +1106,7 @@
         }
 
         for (int i = 0; i < getTaskViewCount(); i++) {
-            TaskView taskView = getTaskViewAt(i);
+            TaskView taskView = requireTaskViewAt(i);
             int[] taskIds = taskView.getTaskIds();
             if (taskIds[0] == taskId || taskIds[1] == taskId) {
                 return taskView;
@@ -1181,7 +1183,7 @@
         if (showAsGrid()) {
             int taskCount = getTaskViewCount();
             for (int i = 0; i < taskCount; i++) {
-                TaskView taskView = getTaskViewAt(i);
+                TaskView taskView = requireTaskViewAt(i);
                 if (isTaskViewVisible(taskView) && taskView.offerTouchToChildren(ev)) {
                     // Keep consuming events to pass to delegate
                     return true;
@@ -1418,7 +1420,7 @@
             if (runningTaskId != -1) {
                 targetPage = indexOfChild(newRunningTaskView);
             } else if (getTaskViewCount() > 0) {
-                targetPage = indexOfChild(getTaskViewAt(0));
+                targetPage = indexOfChild(requireTaskViewAt(0));
             }
         } else if (currentTaskId != -1) {
             currentTaskView = getTaskViewByTaskId(currentTaskId);
@@ -1452,7 +1454,7 @@
 
     private void removeTasksViewsAndClearAllButton() {
         for (int i = getTaskViewCount() - 1; i >= 0; i--) {
-            removeView(getTaskViewAt(i));
+            removeView(requireTaskViewAt(i));
         }
         if (indexOfChild(mClearAllButton) != -1) {
             removeView(mClearAllButton);
@@ -1498,7 +1500,7 @@
 
     public void resetTaskVisuals() {
         for (int i = getTaskViewCount() - 1; i >= 0; i--) {
-            TaskView taskView = getTaskViewAt(i);
+            TaskView taskView = requireTaskViewAt(i);
             if (mIgnoreResetTaskId != taskView.getTaskIds()[0]) {
                 taskView.resetViewTransforms();
                 taskView.setIconScaleAndDim(mTaskIconScaledDown ? 0 : 1);
@@ -1539,7 +1541,7 @@
         mFullscreenProgress = fullscreenProgress;
         int taskCount = getTaskViewCount();
         for (int i = 0; i < taskCount; i++) {
-            getTaskViewAt(i).setFullscreenProgress(mFullscreenProgress);
+            requireTaskViewAt(i).setFullscreenProgress(mFullscreenProgress);
         }
         mClearAllButton.setFullscreenProgress(fullscreenProgress);
 
@@ -1666,7 +1668,7 @@
 
         float accumulatedTranslationX = 0;
         for (int i = 0; i < taskCount; i++) {
-            TaskView taskView = getTaskViewAt(i);
+            TaskView taskView = requireTaskViewAt(i);
             taskView.updateTaskSize();
             taskView.getPrimaryNonGridTranslationProperty().set(taskView, accumulatedTranslationX);
             taskView.getSecondaryNonGridTranslationProperty().set(taskView, 0f);
@@ -1812,7 +1814,7 @@
 
         // Update the task data for the in/visible children
         for (int i = 0; i < getTaskViewCount(); i++) {
-            TaskView taskView = getTaskViewAt(i);
+            TaskView taskView = requireTaskViewAt(i);
             Task task = taskView.getTask();
             int index = indexOfChild(taskView);
             boolean visible;
@@ -1955,7 +1957,7 @@
         }
 
         for (int i = 0; i < getTaskViewCount(); i++) {
-            TaskView taskView = getTaskViewAt(i);
+            TaskView taskView = requireTaskViewAt(i);
             if (taskView.getTaskViewId() == taskViewId) {
                 return taskView;
             }
@@ -2053,7 +2055,7 @@
         int runningIndex = getCurrentPage();
         AnimatorSet as = new AnimatorSet();
         for (int i = 0; i < getTaskViewCount(); i++) {
-            View taskView = getTaskViewAt(i);
+            View taskView = requireTaskViewAt(i);
             if (runningIndex == i && taskView.getAlpha() != 0) {
                 continue;
             }
@@ -2064,7 +2066,7 @@
 
     private void updateChildTaskOrientations() {
         for (int i = 0; i < getTaskViewCount(); i++) {
-            getTaskViewAt(i).setOrientationState(mOrientationState);
+            requireTaskViewAt(i).setOrientationState(mOrientationState);
         }
         TaskMenuView tv = (TaskMenuView) getTopOpenViewWithType(mActivity, TYPE_TASK_MENU);
         if (tv != null) {
@@ -2269,7 +2271,7 @@
             mTaskIconScaledDown = isScaledDown;
             int taskCount = getTaskViewCount();
             for (int i = 0; i < taskCount; i++) {
-                getTaskViewAt(i).setIconScaleAndDim(mTaskIconScaledDown ? 0 : 1);
+                requireTaskViewAt(i).setIconScaleAndDim(mTaskIconScaledDown ? 0 : 1);
             }
         }
     }
@@ -2285,7 +2287,7 @@
         mTaskIconScaledDown = false;
         int taskCount = getTaskViewCount();
         for (int i = 0; i < taskCount; i++) {
-            TaskView taskView = getTaskViewAt(i);
+            TaskView taskView = requireTaskViewAt(i);
             taskView.setIconScaleAnimStartProgress(0f);
             taskView.animateIconScaleAndDimIntoView();
         }
@@ -2357,7 +2359,7 @@
             mTopRowIdSet.clear();
         }
         for (int i = 0; i < taskCount; i++) {
-            TaskView taskView = getTaskViewAt(i);
+            TaskView taskView = requireTaskViewAt(i);
             int taskWidthAndSpacing = taskView.getLayoutParams().width + mPageSpacing;
             // Evenly distribute tasks between rows unless rearranging due to task dismissal, in
             // which case keep tasks in their respective rows. For the running task, don't join
@@ -2422,7 +2424,7 @@
                         if (j == focusedTaskIndex) {
                             continue;
                         }
-                        widthOffset += getTaskViewAt(j).getLayoutParams().width + mPageSpacing;
+                        widthOffset += requireTaskViewAt(j).getLayoutParams().width + mPageSpacing;
                     }
 
                     float currentTaskTranslationX = mIsRtl ? widthOffset : -widthOffset;
@@ -2441,7 +2443,7 @@
                         if (j == focusedTaskIndex) {
                             continue;
                         }
-                        widthOffset += getTaskViewAt(j).getLayoutParams().width + mPageSpacing;
+                        widthOffset += requireTaskViewAt(j).getLayoutParams().width + mPageSpacing;
                     }
 
                     float currentTaskTranslationX = mIsRtl ? widthOffset : -widthOffset;
@@ -2519,7 +2521,7 @@
         }
 
         for (int i = 0; i < taskCount; i++) {
-            TaskView taskView = getTaskViewAt(i);
+            TaskView taskView = requireTaskViewAt(i);
             taskView.setGridTranslationX(gridTranslations[i] - snappedTaskGridTranslationX
                     + snappedTaskNonGridScrollAdjustment);
         }
@@ -2560,7 +2562,7 @@
         mGridProgress = gridProgress;
 
         for (int i = 0; i < taskCount; i++) {
-            getTaskViewAt(i).setGridProgress(gridProgress);
+            requireTaskViewAt(i).setGridProgress(gridProgress);
         }
         mClearAllButton.setGridProgress(gridProgress);
     }
@@ -2723,7 +2725,7 @@
                         mTopRowIdSet.size() > 0 && mTopRowIdSet.size() >= (taskCount - 1) / 2f;
                 // Pick the next focused task from the preferred row.
                 for (int i = 0; i < taskCount; i++) {
-                    TaskView taskView = getTaskViewAt(i);
+                    TaskView taskView = requireTaskViewAt(i);
                     if (taskView == dismissedTaskView) {
                         continue;
                     }
@@ -2826,7 +2828,7 @@
                                 + (taskCount - 1) * halfAdditionalDismissTranslationOffset,
                         END_DISMISS_TRANSLATION_INTERPOLATION_OFFSET, 1);
                 for (int i = 0; i < taskCount; i++) {
-                    TaskView taskView = getTaskViewAt(i);
+                    TaskView taskView = requireTaskViewAt(i);
                     anim.setFloat(taskView, TaskView.GRID_END_TRANSLATION_X, longGridRowWidthDiff,
                             clampToProgress(LINEAR, dismissTranslationInterpolationEnd, 1));
                     dismissTranslationInterpolationEnd = Utilities.boundToRange(
@@ -3142,7 +3144,7 @@
                             // Rebalance tasks in the grid
                             int highestVisibleTaskIndex = getHighestVisibleTaskIndex();
                             if (highestVisibleTaskIndex < Integer.MAX_VALUE) {
-                                TaskView taskView = getTaskViewAt(highestVisibleTaskIndex);
+                                TaskView taskView = requireTaskViewAt(highestVisibleTaskIndex);
 
                                 boolean shouldRebalance;
                                 int screenStart = mOrientationHandler.getPrimaryScroll(
@@ -3240,7 +3242,7 @@
         IntArray topArray = new IntArray(mTopRowIdSet.size());
         int taskViewCount = getTaskViewCount();
         for (int i = 0; i < taskViewCount; i++) {
-            int taskViewId = getTaskViewAt(i).getTaskViewId();
+            int taskViewId = requireTaskViewAt(i).getTaskViewId();
             if (mTopRowIdSet.contains(taskViewId)) {
                 topArray.add(taskViewId);
             }
@@ -3259,7 +3261,7 @@
         IntArray bottomArray = new IntArray(bottomRowIdArraySize);
         int taskViewCount = getTaskViewCount();
         for (int i = 0; i < taskViewCount; i++) {
-            int taskViewId = getTaskViewAt(i).getTaskViewId();
+            int taskViewId = requireTaskViewAt(i).getTaskViewId();
             if (!mTopRowIdSet.contains(taskViewId) && taskViewId != mFocusedTaskViewId) {
                 bottomArray.add(taskViewId);
             }
@@ -3329,7 +3331,7 @@
 
         int count = getTaskViewCount();
         for (int i = 0; i < count; i++) {
-            addDismissedTaskAnimations(getTaskViewAt(i), duration, anim);
+            addDismissedTaskAnimations(requireTaskViewAt(i), duration, anim);
         }
 
         mPendingAnimation = anim;
@@ -3452,7 +3454,7 @@
         mContentAlpha = alpha;
         int runningTaskId = getTaskIdsForRunningTaskView()[0];
         for (int i = getTaskViewCount() - 1; i >= 0; i--) {
-            TaskView child = getTaskViewAt(i);
+            TaskView child = requireTaskViewAt(i);
             int[] childTaskIds = child.getTaskIds();
             if (!mRunningTaskTileHidden ||
                     (childTaskIds[0] != runningTaskId && childTaskIds[1] != runningTaskId)) {
@@ -3553,6 +3555,14 @@
         return child instanceof TaskView ? (TaskView) child : null;
     }
 
+    /**
+     * A version of {@link #getTaskViewAt} when the caller is sure about the input index.
+     */
+    @NonNull
+    private TaskView requireTaskViewAt(int index) {
+        return Objects.requireNonNull(getTaskViewAt(index));
+    }
+
     public void setOnEmptyMessageUpdatedListener(OnEmptyMessageUpdatedListener listener) {
         mOnEmptyMessageUpdatedListener = listener;
     }
@@ -3769,7 +3779,7 @@
     protected void setTaskViewsResistanceTranslation(float translation) {
         mTaskViewsSecondaryTranslation = translation;
         for (int i = 0; i < getTaskViewCount(); i++) {
-            TaskView task = getTaskViewAt(i);
+            TaskView task = requireTaskViewAt(i);
             task.getTaskResistanceTranslationProperty().set(task, translation / getScaleY());
         }
         runActionOnRemoteHandles(
@@ -3779,14 +3789,14 @@
 
     private void updateTaskViewsSnapshotRadius() {
         for (int i = 0; i < getTaskViewCount(); i++) {
-            getTaskViewAt(i).updateSnapshotRadius();
+            requireTaskViewAt(i).updateSnapshotRadius();
         }
     }
 
     protected void setTaskViewsPrimarySplitTranslation(float translation) {
         mTaskViewsPrimarySplitTranslation = translation;
         for (int i = 0; i < getTaskViewCount(); i++) {
-            TaskView task = getTaskViewAt(i);
+            TaskView task = requireTaskViewAt(i);
             task.getPrimarySplitTranslationProperty().set(task, translation);
         }
     }
@@ -3794,7 +3804,7 @@
     protected void setTaskViewsSecondarySplitTranslation(float translation) {
         mTaskViewsSecondarySplitTranslation = translation;
         for (int i = 0; i < getTaskViewCount(); i++) {
-            TaskView taskView = getTaskViewAt(i);
+            TaskView taskView = requireTaskViewAt(i);
             if (taskView == mSplitHiddenTaskView) {
                 continue;
             }
@@ -3808,7 +3818,7 @@
     public void applySplitPrimaryScrollOffset() {
         if (isSplitPlaceholderFirstInGrid()) {
             for (int i = 0; i < getTaskViewCount(); i++) {
-                getTaskViewAt(i).setSplitScrollOffsetPrimary(mSplitPlaceholderSize);
+                requireTaskViewAt(i).setSplitScrollOffsetPrimary(mSplitPlaceholderSize);
             }
         } else if (isSplitPlaceholderLastInGrid()) {
             mClearAllButton.setSplitSelectScrollOffsetPrimary(-mSplitPlaceholderSize);
@@ -3850,7 +3860,7 @@
      */
     public void resetSplitPrimaryScrollOffset() {
         for (int i = 0; i < getTaskViewCount(); i++) {
-            getTaskViewAt(i).setSplitScrollOffsetPrimary(0);
+            requireTaskViewAt(i).setSplitScrollOffsetPrimary(0);
         }
         mClearAllButton.setSplitSelectScrollOffsetPrimary(0);
     }
@@ -4077,8 +4087,8 @@
         mTaskViewDeadZoneRect.setEmpty();
         int count = getTaskViewCount();
         if (count > 0) {
-            final View taskView = getTaskViewAt(0);
-            getTaskViewAt(count - 1).getHitRect(mTaskViewDeadZoneRect);
+            final View taskView = requireTaskViewAt(0);
+            requireTaskViewAt(count - 1).getHitRect(mTaskViewDeadZoneRect);
             mTaskViewDeadZoneRect.union(taskView.getLeft(), taskView.getTop(), taskView.getRight(),
                     taskView.getBottom());
         }
@@ -4559,7 +4569,7 @@
 
         final int taskCount = getTaskViewCount();
         for (int i = 0; i < taskCount; i++) {
-            TaskView taskView = getTaskViewAt(i);
+            TaskView taskView = requireTaskViewAt(i);
             float scrollDiff = taskView.getScrollAdjustment(showAsFullscreen, showAsGrid);
             int pageScroll = newPageScrolls[i] + (int) scrollDiff;
             if ((mIsRtl && pageScroll < clearAllScroll + clearAllWidth)
@@ -4727,7 +4737,7 @@
         int overlayEnabledPage = mOverlayEnabled ? getNextPage() : -1;
         int taskCount = getTaskViewCount();
         for (int i = 0; i < taskCount; i++) {
-            getTaskViewAt(i).setOverlayEnabled(i == overlayEnabledPage);
+            requireTaskViewAt(i).setOverlayEnabled(i == overlayEnabledPage);
         }
     }
 
@@ -4881,7 +4891,7 @@
         mColorTint = tintAmount;
 
         for (int i = 0; i < getTaskViewCount(); i++) {
-            getTaskViewAt(i).setColorTint(mColorTint, mTintingColor);
+            requireTaskViewAt(i).setColorTint(mColorTint, mTintingColor);
         }
 
         Drawable scrimBg = mActivity.getScrimView().getBackground();
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index d91669a..da92551 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -19,6 +19,7 @@
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
 
+import static com.android.launcher3.Utilities.comp;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
 
@@ -290,8 +291,17 @@
             float cornerRadius) {
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             if (mTask != null && getTaskView().isRunningTask() && !getTaskView().showScreenshot()) {
-                canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mClearPaint);
-                canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius,
+                // TODO(b/189265196): Temporary fix to align the surface with the cutout perfectly.
+                // Round up only when the live tile task is displayed in Overview.
+                float rounding = comp(mFullscreenParams.mFullscreenProgress);
+                float left = x + rounding / 2;
+                float top = y + rounding / 2;
+                float right = width - rounding;
+                float bottom = height - rounding;
+
+                canvas.drawRoundRect(left, top, right, bottom, cornerRadius, cornerRadius,
+                        mClearPaint);
+                canvas.drawRoundRect(left, top, right, bottom, cornerRadius, cornerRadius,
                         mDimmingPaintAfterClearing);
                 return;
             }
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index f7f5efc..cca9ecf 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -1531,6 +1531,7 @@
         private final float mCornerRadius;
         private final float mWindowCornerRadius;
 
+        public float mFullscreenProgress;
         public RectF mCurrentDrawnInsets = new RectF();
         public float mCurrentDrawnCornerRadius;
         /** The current scale we apply to the thumbnail to adjust for new left/right insets. */
@@ -1548,6 +1549,8 @@
          */
         public void setProgress(float fullscreenProgress, float parentScale, float taskViewScale,
                 int previewWidth, DeviceProfile dp, PreviewPositionHelper pph) {
+            mFullscreenProgress = fullscreenProgress;
+
             RectF insets = pph.getInsetsToDrawInFullscreen(dp);
 
             float currentInsetsLeft = insets.left * fullscreenProgress;
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
new file mode 100644
index 0000000..ba1a60d
--- /dev/null
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
@@ -0,0 +1,159 @@
+package com.android.launcher3.taskbar;
+
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.SCREEN_PIN_LONG_PRESS_THRESHOLD;
+import static com.android.quickstep.OverviewCommandHelper.TYPE_HOME;
+import static com.android.quickstep.OverviewCommandHelper.TYPE_TOGGLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.Handler;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.quickstep.OverviewCommandHelper;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TouchInteractionService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class TaskbarNavButtonControllerTest {
+
+    private final static int DISPLAY_ID = 2;
+
+    @Mock
+    SystemUiProxy mockSystemUiProxy;
+    @Mock
+    TouchInteractionService mockService;
+    @Mock
+    OverviewCommandHelper mockCommandHelper;
+    @Mock
+    Handler mockHandler;
+
+    private TaskbarNavButtonController mNavButtonController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mockService.getDisplayId()).thenReturn(DISPLAY_ID);
+        when(mockService.getOverviewCommandHelper()).thenReturn(mockCommandHelper);
+        mNavButtonController = new TaskbarNavButtonController(mockService,
+                mockSystemUiProxy, mockHandler);
+    }
+
+    @Test
+    public void testPressBack() {
+        mNavButtonController.onButtonClick(BUTTON_BACK);
+        verify(mockSystemUiProxy, times(1)).onBackPressed();
+    }
+
+    @Test
+    public void testPressImeSwitcher() {
+        mNavButtonController.onButtonClick(BUTTON_IME_SWITCH);
+        verify(mockSystemUiProxy, times(1)).onImeSwitcherPressed();
+    }
+
+    @Test
+    public void testPressA11yShortClick() {
+        mNavButtonController.onButtonClick(BUTTON_A11Y);
+        verify(mockSystemUiProxy, times(1))
+                .notifyAccessibilityButtonClicked(DISPLAY_ID);
+    }
+
+    @Test
+    public void testPressA11yLongClick() {
+        mNavButtonController.onButtonLongClick(BUTTON_A11Y);
+        verify(mockSystemUiProxy, times(1)).notifyAccessibilityButtonLongClicked();
+    }
+
+    @Test
+    public void testLongPressHome() {
+        mNavButtonController.onButtonLongClick(BUTTON_HOME);
+        verify(mockSystemUiProxy, times(1)).startAssistant(any());
+    }
+
+    @Test
+    public void testPressHome() {
+        mNavButtonController.onButtonClick(BUTTON_HOME);
+        verify(mockCommandHelper, times(1)).addCommand(TYPE_HOME);
+    }
+
+    @Test
+    public void testPressRecents() {
+        mNavButtonController.onButtonClick(BUTTON_RECENTS);
+        verify(mockCommandHelper, times(1)).addCommand(TYPE_TOGGLE);
+    }
+
+    @Test
+    public void testPressRecentsWithScreenPinned() {
+        mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
+        mNavButtonController.onButtonClick(BUTTON_RECENTS);
+        verify(mockCommandHelper, times(0)).addCommand(TYPE_TOGGLE);
+    }
+
+    @Test
+    public void testLongPressBackRecentsNotPinned() {
+        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+        mNavButtonController.onButtonLongClick(BUTTON_BACK);
+        verify(mockSystemUiProxy, times(0)).stopScreenPinning();
+    }
+
+    @Test
+    public void testLongPressBackRecentsPinned() {
+        mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
+        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+        mNavButtonController.onButtonLongClick(BUTTON_BACK);
+        verify(mockSystemUiProxy, times(1)).stopScreenPinning();
+    }
+
+    @Test
+    public void testLongPressBackRecentsTooLongPinned() {
+        mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
+        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+        try {
+            Thread.sleep(SCREEN_PIN_LONG_PRESS_THRESHOLD + 5);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        mNavButtonController.onButtonLongClick(BUTTON_BACK);
+        verify(mockSystemUiProxy, times(0)).stopScreenPinning();
+    }
+
+    @Test
+    public void testLongPressBackRecentsMultipleAttemptPinned() {
+        mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
+        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+        try {
+            Thread.sleep(SCREEN_PIN_LONG_PRESS_THRESHOLD + 5);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        mNavButtonController.onButtonLongClick(BUTTON_BACK);
+        verify(mockSystemUiProxy, times(0)).stopScreenPinning();
+
+        // Try again w/in threshold
+        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+        mNavButtonController.onButtonLongClick(BUTTON_BACK);
+        verify(mockSystemUiProxy, times(1)).stopScreenPinning();
+    }
+
+    @Test
+    public void testLongPressHomeScreenPinned() {
+        mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
+        mNavButtonController.onButtonLongClick(BUTTON_HOME);
+        verify(mockSystemUiProxy, times(0)).startAssistant(any());
+    }
+}
diff --git a/src/com/android/launcher3/views/Snackbar.java b/src/com/android/launcher3/views/Snackbar.java
index 49fcd2e..f945819 100644
--- a/src/com/android/launcher3/views/Snackbar.java
+++ b/src/com/android/launcher3/views/Snackbar.java
@@ -28,8 +28,9 @@
 import android.view.MotionEvent;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -44,7 +45,7 @@
     private static final long HIDE_DURATION_MS = 180;
     private static final int TIMEOUT_DURATION_MS = 4000;
 
-    private final BaseDraggingActivity mActivity;
+    private final ActivityContext mActivity;
     private Runnable mOnDismissed;
 
     public Snackbar(Context context, AttributeSet attrs) {
@@ -53,12 +54,19 @@
 
     public Snackbar(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mActivity = BaseDraggingActivity.fromContext(context);
+        mActivity = ActivityContext.lookupContext(context);
         inflate(context, R.layout.snackbar, this);
     }
 
-    public static void show(BaseDraggingActivity activity, int labelStringResId,
-            int actionStringResId, Runnable onDismissed, Runnable onActionClicked) {
+    /** Show a snackbar with just a label. */
+    public static <T extends Context & ActivityContext> void show(T activity, int labelStringRedId,
+            Runnable onDismissed) {
+        show(activity, labelStringRedId, NO_ID, onDismissed, null);
+    }
+
+    /** Show a snackbar with a label and action. */
+    public static <T extends Context & ActivityContext> void show(T activity, int labelStringResId,
+            int actionStringResId, Runnable onDismissed, @Nullable Runnable onActionClicked) {
         closeOpenViews(activity, true, TYPE_SNACKBAR);
         Snackbar snackbar = new Snackbar(activity, null);
         // Set some properties here since inflated xml only contains the children.
@@ -87,13 +95,30 @@
         params.setMargins(0, 0, 0, marginBottom + insets.bottom);
 
         TextView labelView = snackbar.findViewById(R.id.label);
-        TextView actionView = snackbar.findViewById(R.id.action);
         String labelText = res.getString(labelStringResId);
-        String actionText = res.getString(actionStringResId);
-        int totalContentWidth = (int) (labelView.getPaint().measureText(labelText)
-                + actionView.getPaint().measureText(actionText))
+        labelView.setText(labelText);
+
+        TextView actionView = snackbar.findViewById(R.id.action);
+        float actionWidth;
+        if (actionStringResId != NO_ID) {
+            String actionText = res.getString(actionStringResId);
+            actionWidth = actionView.getPaint().measureText(actionText)
+                    + actionView.getPaddingRight() + actionView.getPaddingLeft();
+            actionView.setText(actionText);
+            actionView.setOnClickListener(v -> {
+                if (onActionClicked != null) {
+                    onActionClicked.run();
+                }
+                snackbar.mOnDismissed = null;
+                snackbar.close(true);
+            });
+        } else {
+            actionWidth = 0;
+            actionView.setVisibility(GONE);
+        }
+
+        int totalContentWidth = (int) (labelView.getPaint().measureText(labelText) + actionWidth)
                 + labelView.getPaddingRight() + labelView.getPaddingLeft()
-                + actionView.getPaddingRight() + actionView.getPaddingLeft()
                 + padding * 2;
         if (totalContentWidth > params.width) {
             // The text doesn't fit in our standard width so update width to accommodate.
@@ -113,17 +138,8 @@
                 params.width = maxWidth;
             }
         }
-        labelView.setText(labelText);
-        actionView.setText(actionText);
-        actionView.setOnClickListener(v -> {
-            if (onActionClicked != null) {
-                onActionClicked.run();
-            }
-            snackbar.mOnDismissed = null;
-            snackbar.close(true);
-        });
-        snackbar.mOnDismissed = onDismissed;
 
+        snackbar.mOnDismissed = onDismissed;
         snackbar.setAlpha(0);
         snackbar.setScaleX(0.8f);
         snackbar.setScaleY(0.8f);
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 6e7264a..0bac2ca 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -77,7 +77,8 @@
             mLauncher.scroll(
                     widgetsContainer,
                     Direction.UP,
-                    new Rect(0, 0, mLauncher.getVisibleBounds(widgetsContainer).width(), 0),
+                    new Rect(0, 0, mLauncher.getRightGestureMarginInContainer(widgetsContainer) + 1,
+                            0),
                     FLING_STEPS, false);
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) {
                 verifyActiveContainer();