Merge "Add feature flag for All Apps in system Taskbar."
diff --git a/quickstep/res/drawable/ic_sysbar_notifications.xml b/quickstep/res/drawable/ic_sysbar_notifications.xml
new file mode 100644
index 0000000..21fcf90
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_notifications.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M18,17v-6c0,-3.07 -1.63,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.64,5.36 6,7.92 6,11v6L4,17v2h16v-2h-2zM16,17L8,17v-6c0,-2.48 1.51,-4.5 4,-4.5s4,2.02 4,4.5v6zM12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2z"/>
+</vector>
diff --git a/quickstep/res/drawable/ic_sysbar_quick_settings.xml b/quickstep/res/drawable/ic_sysbar_quick_settings.xml
new file mode 100644
index 0000000..958284d
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_quick_settings.xml
@@ -0,0 +1,13 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M13.85,22.25h-3.7c-0.74,0 -1.36,-0.54 -1.45,-1.27l-0.27,-1.89c-0.27,-0.14 -0.53,-0.29 -0.79,-0.46l-1.8,0.72c-0.7,0.26 -1.47,-0.03 -1.81,-0.65L2.2,15.53c-0.35,-0.66 -0.2,-1.44 0.36,-1.88l1.53,-1.19c-0.01,-0.15 -0.02,-0.3 -0.02,-0.46 0,-0.15 0.01,-0.31 0.02,-0.46l-1.52,-1.19c-0.59,-0.45 -0.74,-1.26 -0.37,-1.88l1.85,-3.19c0.34,-0.62 1.11,-0.9 1.79,-0.63l1.81,0.73c0.26,-0.17 0.52,-0.32 0.78,-0.46l0.27,-1.91c0.09,-0.7 0.71,-1.25 1.44,-1.25h3.7c0.74,0 1.36,0.54 1.45,1.27l0.27,1.89c0.27,0.14 0.53,0.29 0.79,0.46l1.8,-0.72c0.71,-0.26 1.48,0.03 1.82,0.65l1.84,3.18c0.36,0.66 0.2,1.44 -0.36,1.88l-1.52,1.19c0.01,0.15 0.02,0.3 0.02,0.46s-0.01,0.31 -0.02,0.46l1.52,1.19c0.56,0.45 0.72,1.23 0.37,1.86l-1.86,3.22c-0.34,0.62 -1.11,0.9 -1.8,0.63l-1.8,-0.72c-0.26,0.17 -0.52,0.32 -0.78,0.46l-0.27,1.91c-0.1,0.68 -0.72,1.22 -1.46,1.22zM10.62,20.25h2.76l0.37,-2.55 0.53,-0.22c0.44,-0.18 0.88,-0.44 1.34,-0.78l0.45,-0.34 2.38,0.96 1.38,-2.4 -2.03,-1.58 0.07,-0.56c0.03,-0.26 0.06,-0.51 0.06,-0.78s-0.03,-0.53 -0.06,-0.78l-0.07,-0.56 2.03,-1.58 -1.39,-2.4 -2.39,0.96 -0.45,-0.35c-0.42,-0.32 -0.87,-0.58 -1.33,-0.77l-0.52,-0.22 -0.37,-2.55h-2.76l-0.37,2.55 -0.53,0.21c-0.44,0.19 -0.88,0.44 -1.34,0.79l-0.45,0.33 -2.38,-0.95 -1.39,2.39 2.03,1.58 -0.07,0.56c-0.03,0.26 -0.06,0.53 -0.06,0.79s0.02,0.53 0.06,0.78l0.07,0.56 -2.03,1.58 1.38,2.4 2.39,-0.96 0.45,0.35c0.43,0.33 0.86,0.58 1.33,0.77l0.53,0.22 0.38,2.55z"/>
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M12,12m-3.5,0a3.5,3.5 0,1 1,7 0a3.5,3.5 0,1 1,-7 0"/>
+</vector>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 38e8e72..4269f2d 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -320,6 +320,11 @@
         mOverviewCommandHelper = binder.getOverviewCommandHelper();
     }
 
+    @Override
+    public void runOnBindToTouchInteractionService(Runnable r) {
+        mTISBindHelper.runOnBindToTouchInteractionService(r);
+    }
+
     private void initUnfoldTransitionProgressProvider() {
         final UnfoldTransitionConfig config = UnfoldTransitionFactory.createConfig(this);
         if (config.isEnabled()) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 758a74c..fe892c3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -15,11 +15,15 @@
  */
 package com.android.launcher3.taskbar;
 
+import static android.content.pm.PackageManager.FEATURE_PC;
+
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
 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_NOTIFICATIONS;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_QUICK_SETTINGS;
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
 import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_KEYGUARD;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
@@ -246,6 +250,17 @@
 
         applyState();
         mPropertyHolders.forEach(StatePropertyHolder::endAnimation);
+
+        // quick setting and notification buttons
+        if (mContext.getPackageManager().hasSystemFeature(FEATURE_PC)) {
+            addButton(R.drawable.ic_sysbar_quick_settings, BUTTON_QUICK_SETTINGS,
+                    mNavButtonContainer, mControllers.navButtonController,
+                    R.id.quick_settings_button);
+            addButton(R.drawable.ic_sysbar_notifications, BUTTON_NOTIFICATIONS,
+                    mNavButtonContainer, mControllers.navButtonController,
+                    R.id.notifications_button);
+        }
+
     }
 
     private void initButtons(ViewGroup navContainer, ViewGroup endContainer,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index dfbc519..87de596 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -212,6 +212,8 @@
         mWindowLayoutParams.providedInternalImeInsets = Insets.of(0,
                 getDefaultTaskbarWindowHeight() - mTaskbarHeightForIme, 0, 0);
 
+        mWindowLayoutParams.insetsRoundedCornerFrame = true;
+
         // Initialize controllers after all are constructed.
         mControllers.init(sharedState);
         updateSysuiStateFlags(sharedState.sysuiStateFlags, true /* fromInit */);
@@ -446,6 +448,18 @@
     }
 
     /**
+     * Notify system to inset the rounded corner frame based on the task bar insets.
+     */
+    public void updateInsetRoundedCornerFrame(boolean shouldInsetsRoundedCorner) {
+        if (!mDragLayer.isAttachedToWindow()
+                || mWindowLayoutParams.insetsRoundedCornerFrame == shouldInsetsRoundedCorner) {
+            return;
+        }
+        mWindowLayoutParams.insetsRoundedCornerFrame = shouldInsetsRoundedCorner;
+        mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
+    }
+
+    /**
      * Updates the TaskbarContainer height (pass {@link #getDefaultTaskbarWindowHeight()} to reset).
      */
     public void setTaskbarWindowHeight(int height) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index e2ba459..7a50d0b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -111,6 +111,11 @@
         onIconAlignmentRatioChangedForAppAndHomeTransition();
 
         mLauncher.getStateManager().addStateListener(mStateListener);
+
+        // Initialize to the current launcher state
+        updateStateForFlag(FLAG_RESUMED, launcher.hasBeenResumed());
+        mLauncherState = launcher.getStateManager().getState();
+        applyState(0);
     }
 
     public void onDestroy() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 54893ae..a601984 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3.taskbar;
 
-
 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;
@@ -58,6 +57,8 @@
             BUTTON_RECENTS,
             BUTTON_IME_SWITCH,
             BUTTON_A11Y,
+            BUTTON_QUICK_SETTINGS,
+            BUTTON_NOTIFICATIONS,
     })
 
     public @interface TaskbarButton {}
@@ -67,6 +68,8 @@
     static final int BUTTON_RECENTS = BUTTON_HOME << 1;
     static final int BUTTON_IME_SWITCH = BUTTON_RECENTS << 1;
     static final int BUTTON_A11Y = BUTTON_IME_SWITCH << 1;
+    static final int BUTTON_QUICK_SETTINGS = BUTTON_A11Y << 1;
+    static final int BUTTON_NOTIFICATIONS = BUTTON_QUICK_SETTINGS << 1;
 
     private static final int SCREEN_UNPIN_COMBO = BUTTON_BACK | BUTTON_RECENTS;
     private int mLongPressedButtons = 0;
@@ -101,6 +104,12 @@
             case BUTTON_A11Y:
                 notifyA11yClick(false /* longClick */);
                 break;
+            case BUTTON_QUICK_SETTINGS:
+                showQuickSettings();
+                break;
+            case BUTTON_NOTIFICATIONS:
+                showNotifications();
+                break;
         }
     }
 
@@ -221,4 +230,12 @@
         args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
         mSystemUiProxy.startAssistant(args);
     }
+
+    private void showQuickSettings() {
+        mSystemUiProxy.toggleNotificationPanel();
+    }
+
+    private void showNotifications() {
+        mSystemUiProxy.toggleNotificationPanel();
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 5c31e05..d1560fe 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -171,9 +171,13 @@
 
         boolean isManuallyStashedInApp = supportsManualStashing()
                 && mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF);
+        boolean isInSetup = !mActivity.isUserSetupComplete() || sharedState.setupUIVisible;
         updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp);
-        updateStateForFlag(FLAG_STASHED_IN_APP_SETUP,
-                !mActivity.isUserSetupComplete() || sharedState.setupUIVisible);
+        updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, isInSetup);
+        if (isInSetup) {
+            // Update the in-app state to ensure isStashed() reflects right state during SUW
+            updateStateForFlag(FLAG_IN_APP, true);
+        }
         applyState();
 
         notifyStashChange(/* visible */ false, /* stashed */ isStashedInApp());
@@ -535,6 +539,7 @@
 
     private void notifyStashChange(boolean visible, boolean stashed) {
         mSystemUiProxy.notifyTaskbarStatus(visible, stashed);
+        mControllers.taskbarActivityContext.updateInsetRoundedCornerFrame(visible && !stashed);
         mControllers.rotationButtonController.onTaskbarStateChange(visible, stashed);
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index a4d899c..e00d177 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -204,9 +204,11 @@
         PendingAnimation setter = new PendingAnimation(100);
         Rect hotseatPadding = launcherDp.getHotseatLayoutPadding(mActivity);
         float scaleUp = ((float) launcherDp.iconSizePx) / mActivity.getDeviceProfile().iconSizePx;
-        int hotseatCellSize =
-                (launcherDp.availableWidthPx - hotseatPadding.left - hotseatPadding.right)
-                        / launcherDp.numShownHotseatIcons;
+        int borderSpacing = launcherDp.cellLayoutBorderSpacePx.x;
+        int hotseatCellSize = DeviceProfile.calculateCellWidth(
+                launcherDp.availableWidthPx - hotseatPadding.left - hotseatPadding.right,
+                borderSpacing,
+                launcherDp.numShownHotseatIcons);
 
         int offsetY = launcherDp.getTaskbarOffsetY();
         setter.setFloat(mTaskbarIconTranslationYForHome, VALUE, -offsetY, LINEAR);
@@ -225,7 +227,8 @@
             setter.setFloat(child, SCALE_PROPERTY, scaleUp, LINEAR);
 
             float childCenter = (child.getLeft() + child.getRight()) / 2;
-            float hotseatIconCenter = hotseatPadding.left + hotseatCellSize * info.screenId
+            float hotseatIconCenter = hotseatPadding.left
+                    + (hotseatCellSize + borderSpacing) * info.screenId
                     + hotseatCellSize / 2;
             setter.setFloat(child, ICON_TRANSLATE_X, hotseatIconCenter - childCenter, LINEAR);
         }
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 0e5282a..1f5f2a5 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -180,45 +180,51 @@
             getFlagForIndex(0, "STATE_LAUNCHER_PRESENT");
     protected static final int STATE_LAUNCHER_STARTED =
             getFlagForIndex(1, "STATE_LAUNCHER_STARTED");
-    protected static final int STATE_LAUNCHER_DRAWN = getFlagForIndex(2, "STATE_LAUNCHER_DRAWN");
+    protected static final int STATE_LAUNCHER_DRAWN =
+            getFlagForIndex(2, "STATE_LAUNCHER_DRAWN");
+    // Called when the Launcher has connected to the touch interaction service (and the taskbar
+    // ui controller is initialized)
+    protected static final int STATE_LAUNCHER_BIND_TO_SERVICE =
+            getFlagForIndex(3, "STATE_LAUNCHER_BIND_TO_SERVICE");
 
     // Internal initialization states
     private static final int STATE_APP_CONTROLLER_RECEIVED =
-            getFlagForIndex(3, "STATE_APP_CONTROLLER_RECEIVED");
+            getFlagForIndex(4, "STATE_APP_CONTROLLER_RECEIVED");
 
     // Interaction finish states
     private static final int STATE_SCALED_CONTROLLER_HOME =
-            getFlagForIndex(4, "STATE_SCALED_CONTROLLER_HOME");
+            getFlagForIndex(5, "STATE_SCALED_CONTROLLER_HOME");
     private static final int STATE_SCALED_CONTROLLER_RECENTS =
-            getFlagForIndex(5, "STATE_SCALED_CONTROLLER_RECENTS");
+            getFlagForIndex(6, "STATE_SCALED_CONTROLLER_RECENTS");
 
     protected static final int STATE_HANDLER_INVALIDATED =
-            getFlagForIndex(6, "STATE_HANDLER_INVALIDATED");
+            getFlagForIndex(7, "STATE_HANDLER_INVALIDATED");
     private static final int STATE_GESTURE_STARTED =
-            getFlagForIndex(7, "STATE_GESTURE_STARTED");
+            getFlagForIndex(8, "STATE_GESTURE_STARTED");
     private static final int STATE_GESTURE_CANCELLED =
-            getFlagForIndex(8, "STATE_GESTURE_CANCELLED");
+            getFlagForIndex(9, "STATE_GESTURE_CANCELLED");
     private static final int STATE_GESTURE_COMPLETED =
-            getFlagForIndex(9, "STATE_GESTURE_COMPLETED");
+            getFlagForIndex(10, "STATE_GESTURE_COMPLETED");
 
     private static final int STATE_CAPTURE_SCREENSHOT =
-            getFlagForIndex(10, "STATE_CAPTURE_SCREENSHOT");
+            getFlagForIndex(11, "STATE_CAPTURE_SCREENSHOT");
     protected static final int STATE_SCREENSHOT_CAPTURED =
-            getFlagForIndex(11, "STATE_SCREENSHOT_CAPTURED");
+            getFlagForIndex(12, "STATE_SCREENSHOT_CAPTURED");
     private static final int STATE_SCREENSHOT_VIEW_SHOWN =
-            getFlagForIndex(12, "STATE_SCREENSHOT_VIEW_SHOWN");
+            getFlagForIndex(13, "STATE_SCREENSHOT_VIEW_SHOWN");
 
     private static final int STATE_RESUME_LAST_TASK =
-            getFlagForIndex(13, "STATE_RESUME_LAST_TASK");
+            getFlagForIndex(14, "STATE_RESUME_LAST_TASK");
     private static final int STATE_START_NEW_TASK =
-            getFlagForIndex(14, "STATE_START_NEW_TASK");
+            getFlagForIndex(15, "STATE_START_NEW_TASK");
     private static final int STATE_CURRENT_TASK_FINISHED =
-            getFlagForIndex(15, "STATE_CURRENT_TASK_FINISHED");
+            getFlagForIndex(16, "STATE_CURRENT_TASK_FINISHED");
     private static final int STATE_FINISH_WITH_NO_END =
-            getFlagForIndex(16, "STATE_FINISH_WITH_NO_END");
+            getFlagForIndex(17, "STATE_FINISH_WITH_NO_END");
 
     private static final int LAUNCHER_UI_STATES =
-            STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED;
+            STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED |
+                    STATE_LAUNCHER_BIND_TO_SERVICE;
 
     public static final long MAX_SWIPE_DURATION = 350;
     public static final long HOME_DURATION = StaggeredWorkspaceAnim.DURATION_MS;
@@ -429,6 +435,7 @@
 
         setupRecentsViewUi();
         linkRecentsViewScroll();
+        activity.runOnBindToTouchInteractionService(this::onLauncherBindToService);
 
         mActivity.registerActivityLifecycleCallbacks(mLifecycleCallbacks);
         return true;
@@ -510,6 +517,11 @@
         mStateCallback.setState(STATE_LAUNCHER_STARTED);
     }
 
+    private void onLauncherBindToService() {
+        mStateCallback.setState(STATE_LAUNCHER_BIND_TO_SERVICE);
+        flushOnRecentsAnimationAndLauncherBound();
+    }
+
     private void onLauncherPresentAndGestureStarted() {
         // Re-setup the recents UI when gesture starts, as the state could have been changed during
         // that time by a previous window transition.
@@ -812,12 +824,7 @@
         }
 
         // Notify when the animation starts
-        if (!mRecentsAnimationStartCallbacks.isEmpty()) {
-            for (Runnable action : new ArrayList<>(mRecentsAnimationStartCallbacks)) {
-                action.run();
-            }
-            mRecentsAnimationStartCallbacks.clear();
-        }
+        flushOnRecentsAnimationAndLauncherBound();
 
         TaskViewUtils.setSplitAuxiliarySurfacesShown(mRecentsAnimationTargets.nonApps, false);
 
@@ -1195,7 +1202,7 @@
     @UiThread
     private void animateToProgress(float start, float end, long duration, Interpolator interpolator,
             GestureEndTarget target, PointF velocityPxPerMs) {
-        runOnRecentsAnimationStart(() -> animateToProgressInternal(start, end, duration,
+        runOnRecentsAnimationAndLauncherBound(() -> animateToProgressInternal(start, end, duration,
                 interpolator, target, velocityPxPerMs));
     }
 
@@ -1820,12 +1827,12 @@
         SurfaceTransactionApplier.create(mRecentsView, applier -> {
             runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
                             .setSyncTransactionApplier(applier));
-            runOnRecentsAnimationStart(() ->
+            runOnRecentsAnimationAndLauncherBound(() ->
                     mRecentsAnimationTargets.addReleaseCheck(applier));
         });
 
         mRecentsView.addOnScrollChangedListener(mOnRecentsScrollListener);
-        runOnRecentsAnimationStart(() ->
+        runOnRecentsAnimationAndLauncherBound(() ->
                 mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController,
                         mRecentsAnimationTargets));
         mRecentsViewScrollLinked = true;
@@ -1871,14 +1878,26 @@
     }
 
     /**
-     * Runs the given {@param action} if the recents animation has already started, or queues it to
-     * be run when it is next started.
+     * Runs the given {@param action} if the recents animation has already started and Launcher has
+     * been created and bound to the TouchInteractionService, or queues it to be run when it this
+     * next happens.
      */
-    protected void runOnRecentsAnimationStart(Runnable action) {
-        if (mRecentsAnimationTargets == null) {
-            mRecentsAnimationStartCallbacks.add(action);
-        } else {
-            action.run();
+    private void runOnRecentsAnimationAndLauncherBound(Runnable action) {
+        mRecentsAnimationStartCallbacks.add(action);
+        flushOnRecentsAnimationAndLauncherBound();
+    }
+
+    private void flushOnRecentsAnimationAndLauncherBound() {
+        if (mRecentsAnimationTargets == null ||
+                !mStateCallback.hasStates(STATE_LAUNCHER_BIND_TO_SERVICE)) {
+            return;
+        }
+
+        if (!mRecentsAnimationStartCallbacks.isEmpty()) {
+            for (Runnable action : new ArrayList<>(mRecentsAnimationStartCallbacks)) {
+                action.run();
+            }
+            mRecentsAnimationStartCallbacks.clear();
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/OrientationRectF.java b/quickstep/src/com/android/quickstep/OrientationRectF.java
index 59a202c..aa01b05 100644
--- a/quickstep/src/com/android/quickstep/OrientationRectF.java
+++ b/quickstep/src/com/android/quickstep/OrientationRectF.java
@@ -66,7 +66,7 @@
         return applyTransform(event, deltaRotation(mRotation, currentRotation), forceTransform);
     }
 
-    private boolean applyTransform(MotionEvent event, int deltaRotation, boolean forceTransform) {
+    public boolean applyTransform(MotionEvent event, int deltaRotation, boolean forceTransform) {
         mTmpMatrix.reset();
         postDisplayRotation(deltaRotation, mHeight, mWidth, mTmpMatrix);
         if (forceTransform) {
diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
index ecff4f1..d2d3ba3 100644
--- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
@@ -22,6 +22,7 @@
 import static android.view.MotionEvent.ACTION_POINTER_DOWN;
 import static android.view.MotionEvent.ACTION_UP;
 
+import static com.android.launcher3.states.RotationHelper.deltaRotation;
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.RectF;
@@ -358,7 +359,18 @@
                 if (mLastRectTouched == null) {
                     return;
                 }
-                mLastRectTouched.applyTransformFromRotation(event, mCurrentDisplay.rotation, true);
+                if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
+                    if (event.getSurfaceRotation() != mActiveTouchRotation) {
+                        // With Shell transitions, we should rotated to the orientation at the start
+                        // of the gesture not the current display rotation which will happen early
+                        mLastRectTouched.applyTransform(event,
+                                deltaRotation(event.getSurfaceRotation(), mActiveTouchRotation),
+                                true);
+                    }
+                } else {
+                    mLastRectTouched.applyTransformFromRotation(event, mCurrentDisplay.rotation,
+                            true);
+                }
                 break;
             }
             case ACTION_CANCEL:
@@ -366,7 +378,18 @@
                 if (mLastRectTouched == null) {
                     return;
                 }
-                mLastRectTouched.applyTransformFromRotation(event, mCurrentDisplay.rotation, true);
+                if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
+                    if (event.getSurfaceRotation() != mActiveTouchRotation) {
+                        // With Shell transitions, we should rotated to the orientation at the start
+                        // of the gesture not the current display rotation which will happen early
+                        mLastRectTouched.applyTransform(event,
+                                deltaRotation(event.getSurfaceRotation(), mActiveTouchRotation),
+                                true);
+                    }
+                } else {
+                    mLastRectTouched.applyTransformFromRotation(event, mCurrentDisplay.rotation,
+                            true);
+                }
                 mLastRectTouched = null;
                 break;
             }
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
index a551f55..678372c 100644
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -16,9 +16,11 @@
 package com.android.quickstep;
 
 import android.annotation.TargetApi;
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Build;
+import android.os.RemoteException;
 import android.os.UserManager;
 import android.util.Log;
 
@@ -60,5 +62,12 @@
         // Elevate GPU priority for Quickstep and Remote animations.
         ThreadedRendererCompat.setContextPriority(
                 ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
+
+        // Enable binder tracing on system server for calls originating from Launcher
+        try {
+            ActivityManager.getService().enableBinderTracing();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to enable binder tracing", e);
+        }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index d6efc71..103f350 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -141,6 +141,11 @@
         mTaskbarManager.setActivity(this);
     }
 
+    @Override
+    public void runOnBindToTouchInteractionService(Runnable r) {
+        mTISBindHelper.runOnBindToTouchInteractionService(r);
+    }
+
     public void setTaskbarUIController(FallbackTaskbarUIController taskbarUIController) {
         mTaskbarUIController = taskbarUIController;
     }
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index b031c47..a12a670 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -117,8 +117,8 @@
             secondaryTaskTarget = targets.findTask(splitIds[1]);
 
             mStagedSplitBounds = new StagedSplitBounds(
-                    primaryTaskTarget.screenSpaceBounds,
-                    secondaryTaskTarget.screenSpaceBounds, splitIds[0], splitIds[1]);
+                    primaryTaskTarget.startScreenSpaceBounds,
+                    secondaryTaskTarget.startScreenSpaceBounds, splitIds[0], splitIds[1]);
             mRemoteTargetHandles[0].mTransformParams.setTargetSet(
                     createRemoteAnimationTargetsForTarget(primaryTaskTarget, targets));
             mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(primaryTaskTarget,
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 67e7f88..b428b67 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -478,6 +478,17 @@
         }
     }
 
+    @Override
+    public void toggleNotificationPanel() {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.toggleNotificationPanel();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call toggleNotificationPanel", e);
+            }
+        }
+    }
+
     //
     // Pip
     //
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 8ccab71..ae8e45a 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -52,6 +52,7 @@
 import com.android.launcher3.util.SettingsCache;
 import com.android.quickstep.BaseActivityInterface;
 import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TaskAnimationManager;
 import com.android.quickstep.views.TaskView;
 
 import java.lang.annotation.Retention;
@@ -340,6 +341,11 @@
 
     @SurfaceRotation
     public int getDisplayRotation() {
+        if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
+            // When shell transitions are enabled, both the display and activity rotations should
+            // be the same once the gesture starts
+            return mRecentsActivityRotation;
+        }
         return mDisplayRotation;
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/TISBindHelper.java b/quickstep/src/com/android/quickstep/util/TISBindHelper.java
index 92c60c8..7b122c6 100644
--- a/quickstep/src/com/android/quickstep/util/TISBindHelper.java
+++ b/quickstep/src/com/android/quickstep/util/TISBindHelper.java
@@ -26,6 +26,7 @@
 import com.android.quickstep.TouchInteractionService;
 import com.android.quickstep.TouchInteractionService.TISBinder;
 
+import java.util.ArrayList;
 import java.util.function.Consumer;
 
 /**
@@ -44,9 +45,11 @@
     private final Runnable mConnectionRunnable = this::internalBindToTIS;
     private final Context mContext;
     private final Consumer<TISBinder> mConnectionCallback;
+    private final ArrayList<Runnable> mPendingConnectedCallbacks = new ArrayList<>();
 
     private short mConnectionAttempts;
     private boolean mTisServiceBound;
+    private boolean mIsConnected;
 
     public TISBindHelper(Context context, Consumer<TISBinder> connectionCallback) {
         mContext = context;
@@ -66,7 +69,13 @@
         }
 
         Log.d(TAG, "TIS service connected");
+        mIsConnected = true;
         mConnectionCallback.accept((TISBinder) iBinder);
+        // Flush the pending callbacks
+        for (Runnable r : mPendingConnectedCallbacks) {
+            r.run();
+        }
+        mPendingConnectedCallbacks.clear();
         resetServiceBindRetryState();
     }
 
@@ -79,6 +88,16 @@
         internalBindToTIS();
     }
 
+    /**
+     * Runs the given {@param r} runnable when the service is connected.
+     */
+    public void runOnBindToTouchInteractionService(Runnable r) {
+        if (mIsConnected) {
+            r.run();
+        } else {
+            mPendingConnectedCallbacks.add(r);
+        }
+    }
 
     /**
      * Binds to {@link TouchInteractionService}. If the binding fails, attempts to retry via
@@ -120,5 +139,7 @@
     public void onDestroy() {
         internalUnbindToTIS();
         resetServiceBindRetryState();
+        mIsConnected = false;
+        mPendingConnectedCallbacks.clear();
     }
 }
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index f676091..f66a6de 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -44,6 +44,7 @@
 import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.BaseActivityInterface;
+import com.android.quickstep.TaskAnimationManager;
 import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
 import com.android.quickstep.views.TaskView.FullscreenDrawParams;
 import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -170,7 +171,7 @@
      * Sets the targets which the simulator will control
      */
     public void setPreview(RemoteAnimationTargetCompat runningTarget) {
-        setPreviewBounds(runningTarget.screenSpaceBounds, runningTarget.contentInsets);
+        setPreviewBounds(runningTarget.startScreenSpaceBounds, runningTarget.contentInsets);
     }
 
     /**
@@ -304,7 +305,13 @@
             mOrientationStateId = mOrientationState.getStateId();
 
             getFullScreenScale();
-            mThumbnailData.rotation = mOrientationState.getDisplayRotation();
+            if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
+                // With shell transitions, the display is rotated early so we need to actually use
+                // the rotation when the gesture starts
+                mThumbnailData.rotation = mOrientationState.getTouchRotation();
+            } else {
+                mThumbnailData.rotation = mOrientationState.getDisplayRotation();
+            }
 
             // mIsRecentsRtl is the inverse of TaskView RTL.
             boolean isRtlEnabled = !mIsRecentsRtl;
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index c5b191e..4ec16ad 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1396,7 +1396,7 @@
 
         // Removing views sets the currentPage to 0, so we save this and restore it after
         // the new set of views are added
-        int previousPage = mCurrentPage;
+        int previousCurrentPage = mCurrentPage;
         removeAllViews();
 
         // Add views as children based on whether it's grouped or single task
@@ -1420,7 +1420,14 @@
         if (!taskGroups.isEmpty()) {
             addView(mClearAllButton);
         }
-        setCurrentPage(previousPage);
+
+        boolean settlingOnNewTask = mNextPage != INVALID_PAGE;
+        if (settlingOnNewTask) {
+            // Restore mCurrentPage but don't call setCurrentPage() as that clobbers the scroll.
+            mCurrentPage = previousCurrentPage;
+        } else {
+            setCurrentPage(previousCurrentPage);
+        }
 
         // Keep same previous focused task
         TaskView newFocusedTaskView = getTaskViewByTaskId(focusedTaskId);
@@ -1446,7 +1453,7 @@
         }
 
         int targetPage = -1;
-        if (mNextPage == INVALID_PAGE) {
+        if (!settlingOnNewTask) {
             // Set the current page to the running task, but not if settling on new task.
             if (runningTaskId != -1) {
                 targetPage = indexOfChild(newRunningTaskView);
@@ -2254,7 +2261,7 @@
         updateChildTaskOrientations();
 
         // Reload the task list
-        mTaskListChangeId = mModel.getTasks(this::applyLoadPlan);
+        reloadIfNeeded();
     }
 
     /**
diff --git a/res/values/id.xml b/res/values/id.xml
index 508caff..7ad1412 100644
--- a/res/values/id.xml
+++ b/res/values/id.xml
@@ -34,4 +34,7 @@
     <item type="id" name="accessibility_button" />
     <item type="id" name="rotate_suggestion" />
     <!--  /Do not change, must be kept in sync with sysui navbar button IDs for tests!  -->
+
+    <item type="id" name="quick_settings_button" />
+    <item type="id" name="notifications_button" />
 </resources>
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 801a31d..7abdef8 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -106,6 +106,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
 import android.view.WindowManager.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.OvershootInterpolator;
@@ -503,6 +504,23 @@
         setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
 
         setContentView(getRootView());
+        getRootView().getViewTreeObserver().addOnPreDrawListener(
+                new ViewTreeObserver.OnPreDrawListener() {
+                    @Override
+                    public boolean onPreDraw() {
+                        // Checks the status of fade in animation.
+                        final AlphaProperty property =
+                                mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD);
+                        if (property.getValue() == 0) {
+                            // Animation haven't started yet; suspend.
+                            return false;
+                        } else {
+                            // The animation is started; start drawing.
+                            getRootView().getViewTreeObserver().removeOnPreDrawListener(this);
+                            return true;
+                        }
+                    }
+                });
         getRootView().dispatchInsets();
 
         // Listen for broadcasts
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index e30829e..1e8e3ca 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -376,6 +376,7 @@
     protected LauncherAtom.ItemInfo.Builder getDefaultItemInfoBuilder() {
         LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder();
         itemBuilder.setIsWork(!Process.myUserHandle().equals(user));
+        itemBuilder.setRank(rank);
         return itemBuilder;
     }
 
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index 7a23caa..e036943 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -185,4 +185,11 @@
     @RequiresApi(api = Build.VERSION_CODES.R)
     public void updateWindowInsets(WindowInsets.Builder updatedInsetsBuilder,
             WindowInsets oldInsets) { }
+
+    /**
+     * Runs the given {@param r} runnable when this activity binds to the touch interaction service.
+     */
+    public void runOnBindToTouchInteractionService(Runnable r) {
+        r.run();
+    }
 }
diff --git a/src_plugins/com/android/systemui/plugins/OneSearch.java b/src_plugins/com/android/systemui/plugins/OneSearch.java
index 13a956b..534bc87 100644
--- a/src_plugins/com/android/systemui/plugins/OneSearch.java
+++ b/src_plugins/com/android/systemui/plugins/OneSearch.java
@@ -28,7 +28,7 @@
 @ProvidesInterface(action = OneSearch.ACTION, version = OneSearch.VERSION)
 public interface OneSearch extends Plugin {
     String ACTION = "com.android.systemui.action.PLUGIN_ONE_SEARCH";
-    int VERSION = 5;
+    int VERSION = 6;
 
     /**
      * Get the content provider warmed up.
@@ -45,6 +45,8 @@
     /** Get image bitmap with the URL. */
     Parcelable getImageBitmap(String imageUrl);
 
+    void setSuggestOnChrome(boolean enable);
+
     /**
      * Notifies search events to plugin
      *
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 41c7c37..5b940a8 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -423,6 +423,18 @@
         waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
     }
 
+    @Test
+    @PortraitLandscape
+    public void testDeleteFromWorkspace() throws Exception {
+        // test delete both built-in apps and user-installed app from workspace
+        for (String appName : new String[] {"Gmail", "Play Store", APP_NAME}) {
+            final AppIcon appIcon = createShortcutIfNotExist(appName);
+            Workspace workspace = mLauncher.getWorkspace().deleteAppIcon(appIcon);
+            assertNull(appName + " app was found after being deleted from workspace",
+                    workspace.tryGetWorkspaceAppIcon(appName));
+        }
+    }
+
     public static String getAppPackageName() {
         return getInstrumentation().getContext().getPackageName();
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 91b1bc7..631e8f1 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -1399,14 +1399,15 @@
         final Point start = new Point(startX, startY);
         final Point end = new Point(endX, endY);
         sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
-        final long endTime = movePointer(start, end, steps, downTime, slowDown, gestureScope);
+        final long endTime = movePointer(
+                start, end, steps, false, downTime, slowDown, gestureScope);
         sendPointer(downTime, endTime, MotionEvent.ACTION_UP, end, gestureScope);
     }
 
-    long movePointer(Point start, Point end, int steps, long downTime, boolean slowDown,
-            GestureScope gestureScope) {
-        long endTime = movePointer(
-                downTime, downTime, steps * GESTURE_STEP_MS, start, end, gestureScope);
+    long movePointer(Point start, Point end, int steps, boolean isDecelerating,
+            long downTime, boolean slowDown, GestureScope gestureScope) {
+        long endTime = movePointer(downTime, downTime, steps * GESTURE_STEP_MS,
+                isDecelerating, start, end, gestureScope);
         if (slowDown) {
             endTime = movePointer(downTime, endTime + GESTURE_STEP_MS, 5 * GESTURE_STEP_MS, end,
                     end, gestureScope);
@@ -1485,21 +1486,55 @@
 
     public long movePointer(long downTime, long startTime, long duration, Point from, Point to,
             GestureScope gestureScope) {
+        return movePointer(
+                downTime, startTime, duration, false, from, to, gestureScope);
+    }
+
+    public long movePointer(long downTime, long startTime, long duration, boolean isDecelerating,
+            Point from, Point to, GestureScope gestureScope) {
         log("movePointer: " + from + " to " + to);
         final Point point = new Point();
         long steps = duration / GESTURE_STEP_MS;
+
         long currentTime = startTime;
-        for (long i = 0; i < steps; ++i) {
-            sleep(GESTURE_STEP_MS);
 
-            currentTime += GESTURE_STEP_MS;
-            final float progress = (currentTime - startTime) / (float) duration;
+        if (isDecelerating) {
+            // formula: V = V0 - D*T, assuming V = 0 when T = duration
 
-            point.x = from.x + (int) (progress * (to.x - from.x));
-            point.y = from.y + (int) (progress * (to.y - from.y));
+            // vx0: initial speed at the x-dimension, set as twice the avg speed
+            // dx: the constant deceleration at the x-dimension
+            double vx0 = 2 * (to.x - from.x) / duration;
+            double dx = vx0 / duration;
+            // vy0: initial speed at the y-dimension, set as twice the avg speed
+            // dy: the constant deceleration at the y-dimension
+            double vy0 = 2 * (to.y - from.y) / duration;
+            double dy = vy0 / duration;
 
-            sendPointer(downTime, currentTime, MotionEvent.ACTION_MOVE, point, gestureScope);
+            for (long i = 0; i < steps; ++i) {
+                sleep(GESTURE_STEP_MS);
+                currentTime += GESTURE_STEP_MS;
+
+                // formula: P = P0 + V0*T - (D*T^2/2)
+                final double t = (i + 1) * GESTURE_STEP_MS;
+                point.x = from.x + (int) (vx0 * t - 0.5 * dx * t * t);
+                point.y = from.y + (int) (vy0 * t - 0.5 * dy * t * t);
+
+                sendPointer(downTime, currentTime, MotionEvent.ACTION_MOVE, point, gestureScope);
+            }
+        } else {
+            for (long i = 0; i < steps; ++i) {
+                sleep(GESTURE_STEP_MS);
+                currentTime += GESTURE_STEP_MS;
+
+                final float progress = (currentTime - startTime) / (float) duration;
+                point.x = from.x + (int) (progress * (to.x - from.x));
+                point.y = from.y + (int) (progress * (to.y - from.y));
+
+                sendPointer(downTime, currentTime, MotionEvent.ACTION_MOVE, point, gestureScope);
+
+            }
         }
+
         return currentTime;
     }
 
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index d9f5cc8..3f0d7fd 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -46,6 +46,9 @@
  */
 public final class Workspace extends Home {
     private static final int FLING_STEPS = 10;
+    private static final int DEFAULT_DRAG_STEPS = 10;
+    private static final String DROP_BAR_RES_ID = "drop_target_bar";
+    private static final String DELETE_TARGET_TEXT_ID = "delete_target_text";
 
     static final Pattern EVENT_CTRL_W_DOWN = Pattern.compile(
             "Key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_W"
@@ -211,6 +214,40 @@
                 TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
 
+    /*
+     * Get the center point of the delete icon in the drop target bar.
+     */
+    private Point getDeleteDropPoint() {
+        return mLauncher.waitForObjectInContainer(
+                mLauncher.waitForLauncherObject(DROP_BAR_RES_ID),
+                DELETE_TARGET_TEXT_ID).getVisibleCenter();
+    }
+
+    /**
+     * Delete the appIcon from the workspace.
+     *
+     * @param appIcon to be deleted.
+     * @return validated workspace after the existing appIcon being deleted.
+     */
+    public Workspace deleteAppIcon(AppIcon appIcon) {
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+             LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                     "removing app icon from workspace")) {
+            dragIconToWorkspace(
+                    mLauncher, appIcon,
+                    () -> getDeleteDropPoint(),
+                    true, /* decelerating */
+                    appIcon.getLongPressIndicator(),
+                    () -> mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
+                    null);
+
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+                    "dragged the app to the drop bar")) {
+                return new Workspace(mLauncher);
+            }
+        }
+    }
+
     /**
      * Finds folder icons in the current workspace.
      *
@@ -241,8 +278,8 @@
             expectLongClickEvents.run();
             launcher.waitForLauncherObject(longPressIndicator);
             LauncherInstrumentation.log("dragIconToSpringLoaded: indicator");
-            launcher.movePointer(iconCenter, dragStartCenter, 10, downTime, true,
-                    LauncherInstrumentation.GestureScope.INSIDE);
+            launcher.movePointer(iconCenter, dragStartCenter, DEFAULT_DRAG_STEPS, false,
+                    downTime, true, LauncherInstrumentation.GestureScope.INSIDE);
         }, SPRING_LOADED_STATE_ORDINAL, "long-pressing and triggering drag start");
         return dragStartCenter;
     }
@@ -270,7 +307,7 @@
             expectDropEvents = () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN,
                     LauncherInstrumentation.EVENT_START);
         }
-        dragIconToWorkspace(launcher, launchable, () -> dest, longPressIndicator,
+        dragIconToWorkspace(launcher, launchable, () -> dest, false, longPressIndicator,
                 expectLongClickEvents, expectDropEvents);
     }
 
@@ -280,13 +317,13 @@
      */
     static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable,
             Supplier<Point> destSupplier, String longPressIndicator) {
-        dragIconToWorkspace(launcher, launchable, destSupplier, longPressIndicator,
+        dragIconToWorkspace(launcher, launchable, destSupplier, false, longPressIndicator,
                 () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT), null);
     }
 
     static void dragIconToWorkspace(
             LauncherInstrumentation launcher, Launchable launchable, Supplier<Point> dest,
-            String longPressIndicator, Runnable expectLongClickEvents,
+            boolean isDecelerating, String longPressIndicator, Runnable expectLongClickEvents,
             @Nullable Runnable expectDropEvents) {
         try (LauncherInstrumentation.Closable ignored = launcher.addContextLayer(
                 "want to drag icon to workspace")) {
@@ -301,8 +338,8 @@
             while (targetDest.x > displayX || targetDest.x < 0) {
                 int edgeX = targetDest.x > 0 ? displayX : 0;
                 Point screenEdge = new Point(edgeX, targetDest.y);
-                launcher.movePointer(dragStart, screenEdge, 10, downTime, true,
-                        LauncherInstrumentation.GestureScope.INSIDE);
+                launcher.movePointer(dragStart, screenEdge, DEFAULT_DRAG_STEPS, isDecelerating,
+                        downTime, true, LauncherInstrumentation.GestureScope.INSIDE);
                 launcher.waitForIdle(); // Wait for the page change to happen
                 targetDest.x += displayX * (targetDest.x > 0 ? -1 : 1);
                 dragStart = screenEdge;
@@ -310,8 +347,8 @@
 
             // targetDest.x is now between 0 and displayX so we found the target page,
             // we just have to put move the icon to the destination and drop it
-            launcher.movePointer(dragStart, targetDest, 10, downTime, true,
-                    LauncherInstrumentation.GestureScope.INSIDE);
+            launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, isDecelerating,
+                    downTime, true, LauncherInstrumentation.GestureScope.INSIDE);
             dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents);
         }
     }