Merge "Fixing search behavior in Launcher3" into tm-qpr-dev
diff --git a/Android.bp b/Android.bp
index 4adbf53..330c32e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -35,9 +35,7 @@
     ],
     srcs: [
         "tests/tapl/**/*.java",
-        "src/com/android/launcher3/ResourceUtils.java",
-        "src/com/android/launcher3/testing/TestProtocol.java",
-        "src/com/android/launcher3/testing/*Request.java",
+        "src/com/android/launcher3/testing/shared/**/*.java",
     ],
     resource_dirs: [ ],
     manifest: "tests/tapl/AndroidManifest.xml",
diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
index d16e12c..a91ff44 100644
--- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
+++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.testing.shared.TestProtocol;
 
 import java.util.ArrayList;
 import java.util.Collection;
diff --git a/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java b/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java
index e5f0295..84b907f 100644
--- a/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java
+++ b/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java
@@ -28,7 +28,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.taskbar.LauncherTaskbarUIController;
 import com.android.launcher3.testing.DebugTestInformationHandler;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 
 import java.util.concurrent.ExecutionException;
 
diff --git a/quickstep/res/layout/activity_allset.xml b/quickstep/res/layout/activity_allset.xml
index 0cae733..56e1d16 100644
--- a/quickstep/res/layout/activity_allset.xml
+++ b/quickstep/res/layout/activity_allset.xml
@@ -26,7 +26,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:id="@+id/content_view"
-        android:fitsSystemWindows="true">
+        android:fitsSystemWindows="false">
 
         <com.airbnb.lottie.LottieAnimationView
             android:id="@+id/animated_background"
diff --git a/quickstep/res/values-sw600dp/dimens.xml b/quickstep/res/values-sw600dp/dimens.xml
index 7494683..cfbbf8d 100644
--- a/quickstep/res/values-sw600dp/dimens.xml
+++ b/quickstep/res/values-sw600dp/dimens.xml
@@ -14,16 +14,23 @@
  * limitations under the License.
 */
 -->
+<!-- Applies to small tablet screens -->
 <resources>
     <dimen name="navigation_key_padding">25dp</dimen>
 
-    <!--  Task View  -->
+    <!--  Overview Task Views  -->
+    <!--  A touch target for icons, sometimes slightly larger than the icons themselves  -->
     <dimen name="task_thumbnail_icon_size">48dp</dimen>
+    <!--  The icon size for the focused task, placed in center of touch target  -->
     <dimen name="task_thumbnail_icon_drawable_size">44dp</dimen>
+    <!--  The space under the focused task icon  -->
     <dimen name="overview_task_margin">12dp</dimen>
+    <!--  The icon size of all non-focused task icons, placed in center of touch target  -->
     <dimen name="task_thumbnail_icon_drawable_size_grid">44dp</dimen>
-    <dimen name="overview_task_margin_grid">12dp</dimen>
+    <!--  The space between grid rows (when there's 2 rows of thumbnails)  -->
     <dimen name="overview_grid_row_spacing">28dp</dimen>
+    <!--  The horizontal space between tasks  -->
     <dimen name="overview_page_spacing">36dp</dimen>
+    <!--  The space to the left and to the right of the "Clear all" button  -->
     <dimen name="overview_grid_side_margin">64dp</dimen>
 </resources>
diff --git a/quickstep/res/values-sw720dp/dimens.xml b/quickstep/res/values-sw720dp/dimens.xml
index ceaa8f8..284ce11 100644
--- a/quickstep/res/values-sw720dp/dimens.xml
+++ b/quickstep/res/values-sw720dp/dimens.xml
@@ -14,14 +14,23 @@
  * limitations under the License.
 */
 -->
+<!-- Applies to large tablet screens -->
 <resources>
-    <!--  Task View  -->
+    <!--  Overview Task Views  -->
+    <!--  The primary task thumbnail uses up to this much of the total screen height/width  -->
+    <item name="overview_max_scale" format="float" type="dimen">0.7</item>
+    <!--  A touch target for icons, sometimes slightly larger than the icons themselves  -->
     <dimen name="task_thumbnail_icon_size">48dp</dimen>
+    <!--  The icon size for the focused task, placed in center of touch target  -->
     <dimen name="task_thumbnail_icon_drawable_size">44dp</dimen>
+    <!--  The space under the focused task icon  -->
     <dimen name="overview_task_margin">16dp</dimen>
+    <!--  The icon size of all non-focused task icons, placed in center of touch target  -->
     <dimen name="task_thumbnail_icon_drawable_size_grid">44dp</dimen>
-    <dimen name="overview_task_margin_grid">16dp</dimen>
+    <!--  The space between grid rows (when there's 2 rows of thumbnails)  -->
     <dimen name="overview_grid_row_spacing">36dp</dimen>
+    <!--  The horizontal space between tasks  -->
     <dimen name="overview_page_spacing">44dp</dimen>
+    <!--  The space to the left and to the right of the "Clear all" button  -->
     <dimen name="overview_grid_side_margin">64dp</dimen>
 </resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 0232f86..35215e1 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -31,14 +31,19 @@
 
     <dimen name="overview_minimum_next_prev_size">50dp</dimen>
 
-    <!--  Task View  -->
-    <dimen name="task_thumbnail_icon_size">48dp</dimen>
-    <dimen name="task_thumbnail_icon_drawable_size">44dp</dimen>
-    <dimen name="overview_task_margin">16dp</dimen>
-    <dimen name="overview_page_spacing">16dp</dimen>
-    <dimen name="task_icon_cache_default_icon_size">72dp</dimen>
-
+    <!--  Overview Task Views  -->
+    <!--  The primary task thumbnail uses up to this much of the total screen height/width  -->
     <item name="overview_max_scale" format="float" type="dimen">0.7</item>
+    <!--  A touch target for icons, sometimes slightly larger than the icons themselves  -->
+    <dimen name="task_thumbnail_icon_size">48dp</dimen>
+    <!--  The icon size for the focused task, placed in center of touch target  -->
+    <dimen name="task_thumbnail_icon_drawable_size">44dp</dimen>
+    <!--  The space under the focused task icon  -->
+    <dimen name="overview_task_margin">16dp</dimen>
+    <!--  The horizontal space between tasks  -->
+    <dimen name="overview_page_spacing">16dp</dimen>
+
+    <dimen name="task_icon_cache_default_icon_size">72dp</dimen>
     <item name="overview_modal_max_scale" format="float" type="dimen">1.1</item>
 
     <!-- Overrideable in overlay that provides the Overview Actions. -->
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index bb79c1b..3e13a44 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -43,7 +43,7 @@
 import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
 import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
-import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
+import static com.android.launcher3.testing.shared.TestProtocol.BAD_STATE;
 import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
 import static com.android.launcher3.views.FloatingIconView.getFloatingIconView;
@@ -100,6 +100,7 @@
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.taskbar.LauncherTaskbarUIController;
+import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.util.DynamicResource;
diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRecentAppsController.java b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRecentAppsController.java
new file mode 100644
index 0000000..acfbea3
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRecentAppsController.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.util.SparseArray;
+
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.quickstep.RecentsModel;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Provides recent apps functionality specifically in a desktop environment.
+ */
+public class DesktopTaskbarRecentAppsController extends TaskbarRecentAppsController {
+
+    private final TaskbarActivityContext mContext;
+    private ArrayList<ItemInfo> mRunningApps = new ArrayList<>();
+    private AppInfo[] mApps;
+
+    public DesktopTaskbarRecentAppsController(TaskbarActivityContext context) {
+        mContext = context;
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mApps = null;
+    }
+
+    @Override
+    protected void setApps(AppInfo[] apps) {
+        mApps = apps;
+    }
+
+    @Override
+    protected boolean isEnabled() {
+        return true;
+    }
+
+    /**
+     * Set mRunningApps to hold currently running applications using the list of currently running
+     * tasks. Filtering is also done to ignore applications that are already on the taskbar in the
+     * original hotseat.
+     */
+    @Override
+    protected void updateRunningApps(SparseArray<ItemInfo> hotseatItems) {
+        ArrayList<AppInfo> runningApps = getRunningAppsFromTasks();
+        ArrayList<ItemInfo> filteredRunningApps = new ArrayList<>();
+        for (AppInfo runningApp : runningApps) {
+            boolean shouldAddOnTaskbar = true;
+            for (int i = 0; i < hotseatItems.size(); i++) {
+                if (hotseatItems.keyAt(i) >= mControllers.taskbarActivityContext.getDeviceProfile()
+                        .numShownHotseatIcons) {
+                    break;
+                }
+                if (hotseatItems.valueAt(i).getTargetPackage()
+                        .equals(runningApp.getTargetPackage())) {
+                    shouldAddOnTaskbar = false;
+                    break;
+                }
+            }
+            if (shouldAddOnTaskbar) {
+                filteredRunningApps.add(new WorkspaceItemInfo(runningApp));
+            }
+        }
+        mRunningApps = filteredRunningApps;
+        mControllers.taskbarViewController.commitRunningAppsToUI();
+    }
+
+    /**
+     * Returns a copy of hotseatItems with the addition of currently running applications.
+     */
+    @Override
+    protected ItemInfo[] updateHotseatItemInfos(ItemInfo[] hotseatItemInfos) {
+        // hotseatItemInfos.length would be 0 if deviceProfile.numShownHotseatIcons is 0, so we
+        // don't want to show anything in the hotseat
+        if (hotseatItemInfos.length == 0) return hotseatItemInfos;
+
+        int runningAppsIndex = 0;
+        ItemInfo[] newHotseatItemsInfo = Arrays.copyOf(
+                hotseatItemInfos, hotseatItemInfos.length + mRunningApps.size());
+        for (int i = hotseatItemInfos.length; i < newHotseatItemsInfo.length; i++) {
+            newHotseatItemsInfo[i] = mRunningApps.get(runningAppsIndex);
+            runningAppsIndex++;
+        }
+        return newHotseatItemsInfo;
+    }
+
+
+    /**
+     * Returns a list of running applications from the list of currently running tasks.
+     */
+    private ArrayList<AppInfo> getRunningAppsFromTasks() {
+        ArrayList<ActivityManager.RunningTaskInfo> tasks =
+                RecentsModel.INSTANCE.get(mContext).getRunningTasks();
+        ArrayList<AppInfo> runningApps = new ArrayList<>();
+        // early return if apps is empty, since we would have no AppInfo to compare
+        if (mApps == null)  {
+            return runningApps;
+        }
+
+        Set<String> seenPackages = new HashSet<>();
+        for (ActivityManager.RunningTaskInfo taskInfo : tasks) {
+            if (taskInfo.realActivity == null) continue;
+
+            // If a different task for the same package has already been handled, skip this one
+            String taskPackage = taskInfo.realActivity.getPackageName();
+            if (seenPackages.contains(taskPackage)) continue;
+
+            // Otherwise, get the corresponding AppInfo and add it to the list
+            seenPackages.add(taskPackage);
+            AppInfo app = getAppInfo(taskInfo.realActivity);
+            if (app == null) continue;
+            runningApps.add(app);
+        }
+        return runningApps;
+    }
+
+    /**
+     * Retrieves the corresponding AppInfo for the activity.
+     */
+    private AppInfo getAppInfo(ComponentName activity) {
+        String packageName = activity.getPackageName();
+        for (AppInfo app : mApps) {
+            if (!packageName.equals(app.getTargetPackage())) {
+                continue;
+            }
+            return app;
+        }
+        return null;
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java
index e2359c0..3c76e8e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java
@@ -32,11 +32,14 @@
 
     @Override
     protected void init(TaskbarControllers taskbarControllers) {
+        super.init(taskbarControllers);
         mLauncher.getHotseat().setIconsAlpha(0f);
+        mControllers.taskbarViewController.updateRunningApps();
     }
 
     @Override
     protected void onDestroy() {
+        super.onDestroy();
         mLauncher.getHotseat().setIconsAlpha(1f);
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 6df31e5..62a11d4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -354,6 +354,13 @@
     }
 
     @Override
+    public void onExpandPip() {
+        super.onExpandPip();
+        mTaskbarLauncherStateController.updateStateForFlag(FLAG_RESUMED, false);
+        mTaskbarLauncherStateController.applyState();
+    }
+
+    @Override
     public void dumpLogs(String prefix, PrintWriter pw) {
         super.dumpLogs(prefix, pw);
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 439490e..3145ec3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -23,7 +23,7 @@
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
 import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
-import static com.android.launcher3.ResourceUtils.getBoolByName;
+import static com.android.launcher3.testing.shared.ResourceUtils.getBoolByName;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
@@ -76,7 +76,7 @@
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.NavigationMode;
@@ -173,13 +173,15 @@
 
         mAccessibilityDelegate = new TaskbarShortcutMenuAccessibilityDelegate(this);
 
+        final boolean isDesktopMode = getPackageManager().hasSystemFeature(FEATURE_PC);
+
         // Construct controllers.
         mControllers = new TaskbarControllers(this,
                 new TaskbarDragController(this),
                 buttonController,
-                getPackageManager().hasSystemFeature(FEATURE_PC)
-                        ? new DesktopNavbarButtonsViewController(this, navButtonsView) :
-                        new NavbarButtonsViewController(this, navButtonsView),
+                isDesktopMode
+                        ? new DesktopNavbarButtonsViewController(this, navButtonsView)
+                        : new NavbarButtonsViewController(this, navButtonsView),
                 new RotationButtonController(this,
                         c.getColor(R.color.taskbar_nav_icon_light_color),
                         c.getColor(R.color.taskbar_nav_icon_dark_color),
@@ -202,7 +204,10 @@
                 new TaskbarForceVisibleImmersiveController(this),
                 new TaskbarAllAppsController(this, dp),
                 new TaskbarInsetsController(this),
-                new VoiceInteractionWindowController(this));
+                new VoiceInteractionWindowController(this),
+                isDesktopMode
+                        ? new DesktopTaskbarRecentAppsController(this)
+                        : TaskbarRecentAppsController.DEFAULT);
     }
 
     public void init(@NonNull TaskbarSharedState sharedState) {
@@ -622,7 +627,9 @@
 
     /** Adds the given view to WindowManager with the provided LayoutParams (creates new window). */
     public void addWindowView(View view, WindowManager.LayoutParams windowLayoutParams) {
-        mWindowManager.addView(view, windowLayoutParams);
+        if (!view.isAttachedToWindow()) {
+            mWindowManager.addView(view, windowLayoutParams);
+        }
     }
 
     /** Removes the given view from WindowManager. See {@link #addWindowView}. */
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index d7b50b0..2b80b75 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -53,6 +53,7 @@
     public final TaskbarAllAppsController taskbarAllAppsController;
     public final TaskbarInsetsController taskbarInsetsController;
     public final VoiceInteractionWindowController voiceInteractionWindowController;
+    public final TaskbarRecentAppsController taskbarRecentAppsController;
 
     @Nullable private LoggableTaskbarController[] mControllersToLog = null;
 
@@ -82,7 +83,8 @@
             TaskbarForceVisibleImmersiveController taskbarForceVisibleImmersiveController,
             TaskbarAllAppsController taskbarAllAppsController,
             TaskbarInsetsController taskbarInsetsController,
-            VoiceInteractionWindowController voiceInteractionWindowController) {
+            VoiceInteractionWindowController voiceInteractionWindowController,
+            TaskbarRecentAppsController taskbarRecentAppsController) {
         this.taskbarActivityContext = taskbarActivityContext;
         this.taskbarDragController = taskbarDragController;
         this.navButtonController = navButtonController;
@@ -102,6 +104,7 @@
         this.taskbarAllAppsController = taskbarAllAppsController;
         this.taskbarInsetsController = taskbarInsetsController;
         this.voiceInteractionWindowController = voiceInteractionWindowController;
+        this.taskbarRecentAppsController = taskbarRecentAppsController;
     }
 
     /**
@@ -130,6 +133,7 @@
         navButtonController.init(this);
         taskbarInsetsController.init(this);
         voiceInteractionWindowController.init(this);
+        taskbarRecentAppsController.init(this);
 
         mControllersToLog = new LoggableTaskbarController[] {
                 taskbarDragController, navButtonController, navbarButtonsViewController,
@@ -178,6 +182,7 @@
         navButtonController.onDestroy();
         taskbarInsetsController.onDestroy();
         voiceInteractionWindowController.onDestroy();
+        taskbarRecentAppsController.onDestroy();
 
         mControllersToLog = null;
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 3a0f374..97029fe 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -66,7 +66,7 @@
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.systemui.shared.recents.model.Task;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index c1a6185..1c8148b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -30,7 +30,7 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.systemui.shared.system.ViewTreeObserverWrapper;
 import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
index 75881a3..5e670d294 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
@@ -29,6 +29,7 @@
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.LauncherBindableItemsContainer;
+import com.android.quickstep.RecentsModel;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -42,7 +43,7 @@
  * Launcher model Callbacks for rendering taskbar.
  */
 public class TaskbarModelCallbacks implements
-        BgDataModel.Callbacks, LauncherBindableItemsContainer {
+        BgDataModel.Callbacks, LauncherBindableItemsContainer, RecentsModel.RunningTasksListener {
 
     private final SparseArray<ItemInfo> mHotseatItems = new SparseArray<>();
     private List<ItemInfo> mPredictedItems = Collections.emptyList();
@@ -61,6 +62,16 @@
 
     public void init(TaskbarControllers controllers) {
         mControllers = controllers;
+        if (mControllers.taskbarRecentAppsController.isEnabled()) {
+            RecentsModel.INSTANCE.get(mContext).registerRunningTasksListener(this);
+        }
+    }
+
+    /**
+     * Unregisters listeners in this class.
+     */
+    public void unregisterListeners() {
+        RecentsModel.INSTANCE.get(mContext).unregisterRunningTasksListener();
     }
 
     @Override
@@ -185,6 +196,8 @@
                 isHotseatEmpty = false;
             }
         }
+        hotseatItemInfos = mControllers.taskbarRecentAppsController
+                .updateHotseatItemInfos(hotseatItemInfos);
         mContainer.updateHotseatItems(hotseatItemInfos);
 
         final boolean finalIsHotseatEmpty = isHotseatEmpty;
@@ -196,6 +209,21 @@
     }
 
     @Override
+    public void onRunningTasksChanged() {
+        updateRunningApps();
+    }
+
+    /** Called when there's a change in running apps to update the UI. */
+    public void commitRunningAppsToUI() {
+        commitItemsToUI();
+    }
+
+    /** Call TaskbarRecentAppsController to update running apps with mHotseatItems. */
+    public void updateRunningApps() {
+        mControllers.taskbarRecentAppsController.updateRunningApps(mHotseatItems);
+    }
+
+    @Override
     public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMapCopy) {
         mControllers.taskbarPopupController.setDeepShortcutMap(deepShortcutMapCopy);
     }
@@ -203,6 +231,7 @@
     @Override
     public void bindAllApplications(AppInfo[] apps, int flags) {
         mControllers.taskbarAllAppsController.setApps(apps, flags);
+        mControllers.taskbarRecentAppsController.setApps(apps);
     }
 
     protected void dumpLogs(String prefix, PrintWriter pw) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 60f50a9..a395548 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -43,7 +43,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.quickstep.OverviewCommandHelper;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskUtils;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java
new file mode 100644
index 0000000..8445cff
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar;
+
+import android.util.SparseArray;
+
+import androidx.annotation.CallSuper;
+
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
+
+/**
+ * Base class for providing recent apps functionality
+ */
+public class TaskbarRecentAppsController {
+
+    public static final TaskbarRecentAppsController DEFAULT = new TaskbarRecentAppsController();
+
+    // Initialized in init.
+    protected TaskbarControllers mControllers;
+
+    @CallSuper
+    protected void init(TaskbarControllers taskbarControllers) {
+        mControllers = taskbarControllers;
+    }
+
+    @CallSuper
+    protected void onDestroy() {
+        mControllers = null;
+    }
+
+    /** Stores the current {@link AppInfo} instances, no-op except in desktop environment. */
+    protected void setApps(AppInfo[] apps) { }
+
+    /**
+     * Indicates whether recent apps functionality is enabled, should return false except in
+     * desktop environment.
+     */
+    protected boolean isEnabled() {
+        return false;
+    }
+
+    /** Called to update hotseatItems, no-op except in desktop environment. */
+    protected ItemInfo[] updateHotseatItemInfos(ItemInfo[] hotseatItems) {
+        return hotseatItems;
+    }
+
+    /** Called to update the list of currently running apps, no-op except in desktop environment. */
+    protected void updateRunningApps(SparseArray<ItemInfo> hotseatItems) { }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 513f1bb..8533145 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -40,7 +40,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorListeners;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.SystemUiProxy;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 6d45fd2..077172d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -126,6 +126,7 @@
 
     public void onDestroy() {
         LauncherAppState.getInstance(mActivity).getModel().removeCallbacks(mModelCallbacks);
+        mModelCallbacks.unregisterListeners();
     }
 
     public boolean areIconsVisible() {
@@ -389,6 +390,16 @@
         mModelCallbacks.dumpLogs(prefix + "\t", pw);
     }
 
+    /** Called when there's a change in running apps to update the UI. */
+    public void commitRunningAppsToUI() {
+        mModelCallbacks.commitRunningAppsToUI();
+    }
+
+    /** Call TaskbarModelCallbacks to update running apps. */
+    public void updateRunningApps() {
+        mModelCallbacks.updateRunningApps();
+    }
+
     /**
      * Callbacks for {@link TaskbarView} to interact with its controller.
      */
diff --git a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
index 946873e..81acda3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
@@ -36,7 +36,10 @@
 
                 override fun draw(canvas: Canvas) {
                     super.draw(canvas)
-                    taskbarBackgroundRenderer.draw(canvas)
+                    if (this@VoiceInteractionWindowController.context.isGestureNav
+                        && controllers.taskbarStashController.isInAppAndNotStashed) {
+                        taskbarBackgroundRenderer.draw(canvas)
+                    }
                 }
             }
         separateWindowForTaskbarBackground.recreateControllers()
@@ -74,18 +77,20 @@
             fadeStashedHandle.end()
         }
 
-        if (context.isGestureNav && controllers.taskbarStashController.isInAppAndNotStashed) {
-            moveTaskbarBackgroundToLowerLayer()
-        }
+        moveTaskbarBackgroundToAppropriateLayer()
     }
 
     /**
+     * Either:
      * Hides the TaskbarDragLayer background and creates a new window to draw just that background.
+     * OR
+     * Removes the temporary window and show the TaskbarDragLayer background again.
      */
-    private fun moveTaskbarBackgroundToLowerLayer() {
+    private fun moveTaskbarBackgroundToAppropriateLayer() {
         val taskbarBackgroundOverride = controllers.taskbarDragLayerController
             .overrideBackgroundAlpha
-        if (isVoiceInteractionWindowVisible) {
+        val moveToLowerLayer = isVoiceInteractionWindowVisible
+        if (moveToLowerLayer) {
             // First add the temporary window, then hide the overlapping taskbar background.
             context.addWindowView(separateWindowForTaskbarBackground, separateWindowLayoutParams)
             ViewRootSync.synchronizeNextDraw(separateWindowForTaskbarBackground, context.dragLayer
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
index e2f7522..c76180e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
@@ -43,7 +43,7 @@
 import com.android.launcher3.taskbar.TaskbarDragController;
 import com.android.launcher3.taskbar.TaskbarStashController;
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.BaseDragLayer;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 7166a3e..f220509 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -23,16 +23,22 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
-import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
+import static com.android.launcher3.testing.shared.TestProtocol.BAD_STATE;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
+import static com.android.quickstep.views.RecentsView.FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN;
 import static com.android.quickstep.views.RecentsView.OVERVIEW_PROGRESS;
 import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
+import static com.android.quickstep.views.RecentsView.SPLIT_INSTRUCTIONS_FADE;
 import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
 
+import android.graphics.Rect;
+import android.graphics.RectF;
 import android.util.FloatProperty;
 import android.util.Log;
 
@@ -40,9 +46,12 @@
 
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
+import com.android.quickstep.views.FloatingTaskView;
 import com.android.quickstep.views.RecentsView;
 
 /**
@@ -106,6 +115,49 @@
         setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f,
                 config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
 
+        if (mRecentsView.isSplitSelectionActive()) {
+            // TODO (b/238651489): Refactor state management to avoid need for double check
+            FloatingTaskView floatingTask = mRecentsView.getFirstFloatingTaskView();
+            if (floatingTask != null) {
+                DragLayer dragLayer = mLauncher.getDragLayer();
+                RectF onScreenRectF = new RectF();
+                Utilities.getBoundsForViewInDragLayer(mLauncher.getDragLayer(), floatingTask,
+                        new Rect(0, 0, floatingTask.getWidth(), floatingTask.getHeight()),
+                        false, null, onScreenRectF);
+                // Get the part of the floatingTask that intersects with the DragLayer (i.e. the
+                // on-screen portion)
+                onScreenRectF.intersect(
+                        dragLayer.getLeft(),
+                        dragLayer.getTop(),
+                        dragLayer.getRight(),
+                        dragLayer.getBottom()
+                );
+
+                setter.setFloat(
+                        mRecentsView,
+                        FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN,
+                        mRecentsView.getPagedOrientationHandler()
+                                .getFloatingTaskOffscreenTranslationTarget(
+                                        floatingTask,
+                                        onScreenRectF,
+                                        floatingTask.getStagePosition(),
+                                        mLauncher.getDeviceProfile()
+                                ),
+                        config.getInterpolator(
+                                ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN,
+                                LINEAR
+                        ));
+                setter.setFloat(
+                        mRecentsView,
+                        SPLIT_INSTRUCTIONS_FADE,
+                        1,
+                        config.getInterpolator(
+                                ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE,
+                                LINEAR
+                        ));
+            }
+        }
+
         float recentsAlpha = toState.overviewUi ? 1 : 0;
         Log.d(BAD_STATE, "BaseRecentsViewStateController setStateWithAnimationInternal toState="
                 + toState + ", alpha=" + recentsAlpha);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 4bb4343..21f03be 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -28,10 +28,10 @@
 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_WIDGET_APP_START;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
-import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
-import static com.android.launcher3.testing.TestProtocol.HINT_STATE_TWO_BUTTON_ORDINAL;
-import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
-import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.HINT_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.HINT_STATE_TWO_BUTTON_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
 
 import android.content.Intent;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index c4c9038..6f07568 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -37,6 +37,10 @@
  */
 public class OverviewState extends LauncherState {
 
+    private static final int OVERVIEW_SLIDE_IN_DURATION = 380;
+    private static final int OVERVIEW_POP_IN_DURATION = 250;
+    private static final int OVERVIEW_EXIT_DURATION = 250;
+
     protected static final Rect sTempRect = new Rect();
 
     private static final int STATE_FLAGS = FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED
@@ -57,8 +61,15 @@
 
     @Override
     public int getTransitionDuration(Context context, boolean isToState) {
-        // In gesture modes, overview comes in all the way from the side, so give it more time.
-        return DisplayController.getNavigationMode(context).hasGestures ? 380 : 250;
+        if (isToState) {
+            // In gesture modes, overview comes in all the way from the side, so give it more time.
+            return DisplayController.getNavigationMode(context).hasGestures
+                    ? OVERVIEW_SLIDE_IN_DURATION
+                    : OVERVIEW_POP_IN_DURATION;
+        } else {
+            // When exiting Overview, exit quickly.
+            return OVERVIEW_EXIT_DURATION;
+        }
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 4d2f965..872e64a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -22,13 +22,15 @@
 import static com.android.launcher3.LauncherState.HINT_STATE_TWO_BUTTON;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
 import static com.android.launcher3.WorkspaceStateTransitionAnimation.getWorkspaceSpringScaleAnimator;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED_ACCELERATE;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE;
 import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
 import static com.android.launcher3.anim.Interpolators.INSTANT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -39,6 +41,8 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
@@ -87,9 +91,16 @@
     public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState,
             StateAnimationConfig config) {
         RecentsView overview = mActivity.getOverviewPanel();
-        if (toState == NORMAL && fromState == OVERVIEW) {
+        if ((fromState == OVERVIEW || fromState == OVERVIEW_SPLIT_SELECT) && toState == NORMAL) {
+            if (fromState == OVERVIEW_SPLIT_SELECT) {
+                config.setInterpolator(ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN,
+                        clampToProgress(EMPHASIZED_ACCELERATE, 0, 0.4f));
+                config.setInterpolator(ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE,
+                        clampToProgress(LINEAR, 0, 0.33f));
+            }
+
             config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, clampToProgress(LINEAR, 0, 0.25f));
-            config.setInterpolator(ANIM_SCRIM_FADE, LINEAR);
+            config.setInterpolator(ANIM_SCRIM_FADE, clampToProgress(LINEAR, 0.33f, 1));
             config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
             config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
 
@@ -98,8 +109,7 @@
                 // Overview is going offscreen, so keep it at its current scale and opacity.
                 config.setInterpolator(ANIM_OVERVIEW_SCALE, FINAL_FRAME);
                 config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME);
-                config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X,
-                        clampToProgress(FAST_OUT_SLOW_IN, 0, 0.75f));
+                config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, EMPHASIZED_DECELERATE);
                 config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, FINAL_FRAME);
             } else {
                 config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL_DEACCEL);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index f294986..4092a73 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -39,7 +39,7 @@
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_ALL_ANIMATIONS;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM;
-import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
+import static com.android.launcher3.testing.shared.TestProtocol.BAD_STATE;
 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
 import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index d1b0a9c..f607aa3 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -29,7 +29,7 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
-import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
+import static com.android.launcher3.testing.shared.TestProtocol.BAD_STATE;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java
index e2747df..9f2c1d4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java
@@ -24,13 +24,11 @@
 
 import android.animation.ValueAnimator;
 import android.os.SystemClock;
-import android.util.Log;
 import android.view.MotionEvent;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.AbstractStateChangeTouchController;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
 import com.android.quickstep.SystemUiProxy;
diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
index 895cf89..47c5dd0 100644
--- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
@@ -32,7 +32,7 @@
 import android.view.Surface;
 
 import com.android.launcher3.R;
-import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.DisplayController.NavigationMode;
 import com.android.launcher3.util.window.CachedDisplayInfo;
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index 528fb97..9bd0cb9 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -8,7 +8,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.testing.TestInformationHandler;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.quickstep.util.LayoutUtils;
 
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index cd93dbe..813e687 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -62,6 +62,10 @@
     private TaskLoadResult mResultsBg = INVALID_RESULT;
     private TaskLoadResult mResultsUi = INVALID_RESULT;
 
+    private RecentsModel.RunningTasksListener mRunningTasksListener;
+    // Tasks are stored in order of least recently launched to most recently launched.
+    private ArrayList<ActivityManager.RunningTaskInfo> mRunningTasks;
+
     public RecentTasksList(LooperExecutor mainThreadExecutor,
             KeyguardManagerCompat keyguardManager, SystemUiProxy sysUiProxy) {
         mMainThreadExecutor = mainThreadExecutor;
@@ -73,7 +77,26 @@
             public void onRecentTasksChanged() throws RemoteException {
                 mMainThreadExecutor.execute(RecentTasksList.this::onRecentTasksChanged);
             }
+
+            @Override
+            public void onRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) {
+                mMainThreadExecutor.execute(() -> {
+                    RecentTasksList.this.onRunningTaskAppeared(taskInfo);
+                });
+            }
+
+            @Override
+            public void onRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+                mMainThreadExecutor.execute(() -> {
+                    RecentTasksList.this.onRunningTaskVanished(taskInfo);
+                });
+            }
         });
+        // We may receive onRunningTaskAppeared events later for tasks which have already been
+        // included in the list returned by mSysUiProxy.getRunningTasks(), or may receive
+        // onRunningTaskVanished for tasks not included in the returned list. These cases will be
+        // addressed when the tasks are added to/removed from mRunningTasks.
+        initRunningTasks(mSysUiProxy.getRunningTasks(Integer.MAX_VALUE));
     }
 
     @VisibleForTesting
@@ -154,6 +177,59 @@
         mChangeId++;
     }
 
+     /**
+     * Registers a listener for running tasks
+     */
+    public void registerRunningTasksListener(RecentsModel.RunningTasksListener listener) {
+        mRunningTasksListener = listener;
+    }
+
+    /**
+     * Removes the previously registered running tasks listener
+     */
+    public void unregisterRunningTasksListener() {
+        mRunningTasksListener = null;
+    }
+
+    private void initRunningTasks(ArrayList<ActivityManager.RunningTaskInfo> runningTasks) {
+        // Tasks are retrieved in order of most recently launched/used to least recently launched.
+        mRunningTasks = new ArrayList<>(runningTasks);
+        Collections.reverse(mRunningTasks);
+    }
+
+    /**
+     * Gets the set of running tasks.
+     */
+    public ArrayList<ActivityManager.RunningTaskInfo> getRunningTasks() {
+        return mRunningTasks;
+    }
+
+    private void onRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) {
+        // Make sure this task is not already in the list
+        for (ActivityManager.RunningTaskInfo existingTask : mRunningTasks) {
+            if (taskInfo.taskId == existingTask.taskId) {
+                return;
+            }
+        }
+        mRunningTasks.add(taskInfo);
+        if (mRunningTasksListener != null) {
+            mRunningTasksListener.onRunningTasksChanged();
+        }
+    }
+
+    private void onRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+        // Find the task from the list of running tasks, if it exists
+        for (ActivityManager.RunningTaskInfo existingTask : mRunningTasks) {
+            if (existingTask.taskId != taskInfo.taskId) continue;
+
+            mRunningTasks.remove(existingTask);
+            if (mRunningTasksListener != null) {
+                mRunningTasksListener.onRunningTasksChanged();
+            }
+            return;
+        }
+    }
+
     /**
      * Loads and creates a list of all the recent tasks.
      */
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 67ce606..98824eb 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -23,8 +23,8 @@
 import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
-import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
-import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.BAD_STATE;
+import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
 import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
 import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 442578e..3074dbb 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -236,4 +236,35 @@
         writer.println(prefix + "RecentsModel:");
         mTaskList.dump("  ", writer);
     }
+
+    /**
+     * Registers a listener for running tasks
+     */
+    public void registerRunningTasksListener(RunningTasksListener listener) {
+        mTaskList.registerRunningTasksListener(listener);
+    }
+
+    /**
+     * Removes the previously registered running tasks listener
+     */
+    public void unregisterRunningTasksListener() {
+        mTaskList.unregisterRunningTasksListener();
+    }
+
+    /**
+     * Gets the set of running tasks.
+     */
+    public ArrayList<ActivityManager.RunningTaskInfo> getRunningTasks() {
+        return mTaskList.getRunningTasks();
+    }
+
+    /**
+     * Listener for receiving running tasks changes
+     */
+    public interface RunningTasksListener {
+        /**
+         * Called when there's a change to running tasks
+         */
+        void onRunningTasksChanged();
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index f1e20db..2186a3b 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -31,7 +31,7 @@
 import android.view.MotionEvent;
 import android.view.OrientationEventListener;
 
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
 import com.android.launcher3.util.DisplayController.Info;
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 9a2619b..0ec7e62 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -20,12 +20,14 @@
 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
+import android.app.ActivityManager;
 import android.app.PendingIntent;
 import android.app.PictureInPictureParams;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
 import android.graphics.Insets;
 import android.graphics.Rect;
@@ -96,7 +98,7 @@
     private IPipAnimationListener mPipAnimationListener;
     private ISplitScreenListener mSplitScreenListener;
     private IStartingWindowListener mStartingWindowListener;
-    private ILauncherUnlockAnimationController mPendingLauncherUnlockAnimationController;
+    private ILauncherUnlockAnimationController mLauncherUnlockAnimationController;
     private IRecentTasksListener mRecentTasksListener;
     private final ArrayList<RemoteTransitionCompat> mRemoteTransitions = new ArrayList<>();
     private IOnBackInvokedCallback mBackToLauncherCallback;
@@ -108,12 +110,14 @@
     private boolean mLastNavButtonAnimate;
     private boolean mHasNavButtonAlphaBeenSet = false;
     private Runnable mPendingSetNavButtonAlpha = null;
+    private Context mContext;
 
     // TODO(141886704): Find a way to remove this
     private int mLastSystemUiStateFlags;
 
     public SystemUiProxy(Context context) {
         DisplayController.INSTANCE.get(context).addChangeListener(this);
+        mContext = context;
     }
 
     @Override
@@ -189,10 +193,8 @@
         if (mStartingWindowListener != null && mStartingWindow != null) {
             setStartingWindowListener(mStartingWindowListener);
         }
-        if (mPendingLauncherUnlockAnimationController != null
-                && mSysuiUnlockAnimationController != null) {
-            setLauncherUnlockAnimationController(mPendingLauncherUnlockAnimationController);
-            mPendingLauncherUnlockAnimationController = null;
+        if (mSysuiUnlockAnimationController != null && mLauncherUnlockAnimationController != null) {
+            setLauncherUnlockAnimationController(mLauncherUnlockAnimationController);
         }
         for (int i = mRemoteTransitions.size() - 1; i >= 0; --i) {
             registerRemoteTransition(mRemoteTransitions.get(i));
@@ -757,11 +759,11 @@
                     controller.dispatchSmartspaceStateToSysui();
                 }
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setStartingWindowListener", e);
+                Log.w(TAG, "Failed call setLauncherUnlockAnimationController", e);
             }
-        } else {
-            mPendingLauncherUnlockAnimationController = controller;
         }
+
+        mLauncherUnlockAnimationController = controller;
     }
 
     /**
@@ -870,4 +872,20 @@
         }
         return new ArrayList<>();
     }
+
+    /**
+     * Gets the set of running tasks.
+     */
+    public ArrayList<ActivityManager.RunningTaskInfo> getRunningTasks(int numTasks) {
+        if (mRecentTasks != null
+                && mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
+            try {
+                return new ArrayList<ActivityManager.RunningTaskInfo>(
+                        Arrays.asList(mRecentTasks.getRunningTasks(numTasks)));
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call getRunningTasks", e);
+            }
+        }
+        return new ArrayList<>();
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index a030568..c28fc9d 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -37,7 +37,7 @@
 import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
-import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
+import static com.android.launcher3.testing.shared.TestProtocol.BAD_STATE;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
diff --git a/quickstep/src/com/android/quickstep/TopTaskTracker.java b/quickstep/src/com/android/quickstep/TopTaskTracker.java
index 42fa86d..cfcba4c 100644
--- a/quickstep/src/com/android/quickstep/TopTaskTracker.java
+++ b/quickstep/src/com/android/quickstep/TopTaskTracker.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.content.Intent.ACTION_CHOOSER;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
 
@@ -31,9 +32,9 @@
 
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitStageInfo;
 import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
 import com.android.launcher3.util.SplitConfigurationOptions.StageType;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitStageInfo;
 import com.android.launcher3.util.TraceHelper;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.Task.TaskKey;
@@ -85,6 +86,19 @@
     public void onTaskMovedToFront(RunningTaskInfo taskInfo) {
         mOrderedTaskList.removeIf(rto -> rto.taskId == taskInfo.taskId);
         mOrderedTaskList.addFirst(taskInfo);
+
+        // Keep the home display's top running task in the first while adding a non-home
+        // display's task to the list, to avoid showing non-home display's task upon going to
+        // Recents animation.
+        if (taskInfo.displayId != DEFAULT_DISPLAY) {
+            final RunningTaskInfo topTaskOnHomeDisplay = mOrderedTaskList.stream()
+                    .filter(rto -> rto.displayId == DEFAULT_DISPLAY).findFirst().orElse(null);
+            if (topTaskOnHomeDisplay != null) {
+                mOrderedTaskList.removeIf(rto -> rto.taskId == topTaskOnHomeDisplay.taskId);
+                mOrderedTaskList.addFirst(topTaskOnHomeDisplay);
+            }
+        }
+
         if (mOrderedTaskList.size() >= HISTORY_SIZE) {
             // If we grow in size, remove the last taskInfo which is not part of the split task.
             Iterator<RunningTaskInfo> itr = mOrderedTaskList.descendingIterator();
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index bf1c998..2df9b9c 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -65,7 +65,7 @@
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.R;
-import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.provider.RestoreDbTask;
@@ -73,7 +73,7 @@
 import com.android.launcher3.taskbar.TaskbarActivityContext;
 import com.android.launcher3.taskbar.TaskbarManager;
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.tracing.LauncherTraceProto;
 import com.android.launcher3.tracing.TouchInteractionServiceProto;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index ab3201a..3734f11 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -15,7 +15,7 @@
  */
 package com.android.quickstep.fallback;
 
-import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
+import static com.android.launcher3.testing.shared.TestProtocol.BAD_STATE;
 import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
 import static com.android.quickstep.fallback.RecentsState.DEFAULT;
 import static com.android.quickstep.fallback.RecentsState.HOME;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
index 8da2fd3..03f8eef 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
@@ -3,7 +3,7 @@
 import android.view.MotionEvent;
 
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.tracing.InputConsumerProto;
 import com.android.quickstep.InputConsumer;
 import com.android.systemui.shared.system.InputMonitorCompat;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index c1750b5..6bc24f2 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -42,7 +42,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.GestureState;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
index bc20902..d7ed79b 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
@@ -21,7 +21,7 @@
 import static android.view.MotionEvent.ACTION_MOVE;
 import static android.view.MotionEvent.ACTION_UP;
 
-import static com.android.launcher3.ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE;
+import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE;
 import static com.android.launcher3.Utilities.squaredHypot;
 
 import android.content.Context;
@@ -30,7 +30,7 @@
 import android.view.MotionEvent;
 
 import com.android.launcher3.R;
-import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.InputConsumer;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 641385d..92d3d23 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -50,7 +50,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.tracing.InputConsumerProto;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.TraceHelper;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index 02ac48e..7899c55 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -29,7 +29,7 @@
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.BaseActivityInterface;
 import com.android.quickstep.GestureState;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index a730183..bde4240 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -28,7 +28,7 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
 import com.android.quickstep.RecentsAnimationDeviceState;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
index 71dca66..2462394 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
@@ -30,7 +30,7 @@
 
 import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
 import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.AnimatedFloat;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
index 878f132..4806ac1 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
@@ -23,7 +23,7 @@
 import android.view.MotionEvent;
 
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.quickstep.InputConsumer;
 import com.android.quickstep.RecentsAnimationDeviceState;
 import com.android.quickstep.util.TriggerSwipeUpTouchTracker;
diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java
index d059d82..8660d87 100644
--- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java
@@ -29,7 +29,7 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.DisplayController;
 
diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java
index b2b2f59..437572b 100644
--- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java
+++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java
@@ -41,7 +41,7 @@
 import androidx.dynamicanimation.animation.SpringForce;
 
 import com.android.launcher3.R;
-import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.anim.Interpolators;
 import com.android.quickstep.util.VibratorWrapper;
 
diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
index f981860..4badf30 100644
--- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
@@ -44,7 +44,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.R;
-import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.NavigationMode;
diff --git a/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java b/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
index edaa326..ac77c67 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
@@ -15,7 +15,7 @@
  */
 package com.android.quickstep.util;
 
-import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
+import static com.android.launcher3.testing.shared.TestProtocol.BAD_STATE;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
 
 import android.animation.Animator;
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index dec934a..c459f30 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -46,7 +46,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.SettingsCache;
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index e89c842..d2a2f36 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -43,7 +43,7 @@
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
 import com.android.quickstep.SystemUiProxy;
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
index 835c9f7..7a66ea0 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -260,6 +260,10 @@
                 mActivity.getDeviceProfile(), mStagePosition);
     }
 
+    public int getStagePosition() {
+        return mStagePosition;
+    }
+
     private static class SplitOverlayProperties {
 
         private final float finalTaskViewScaleX;
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index a736583..8b8558a 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -21,7 +21,7 @@
 import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
 import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
-import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
+import static com.android.launcher3.testing.shared.TestProtocol.BAD_STATE;
 
 import android.annotation.TargetApi;
 import android.content.Context;
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index cf93857..5975106 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -397,6 +397,39 @@
                 }
             };
 
+    public static final FloatProperty<RecentsView> FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN =
+            new FloatProperty<RecentsView>("firstFloatingTaskTranslateOffscreen") {
+                @Override
+                public void setValue(RecentsView view, float translation) {
+                    view.getPagedOrientationHandler().setFloatingTaskPrimaryTranslation(
+                            view.mFirstFloatingTaskView,
+                            translation,
+                            view.mActivity.getDeviceProfile()
+                    );
+                }
+
+                @Override
+                public Float get(RecentsView view) {
+                    return view.getPagedOrientationHandler().getFloatingTaskPrimaryTranslation(
+                            view.mFirstFloatingTaskView,
+                            view.mActivity.getDeviceProfile()
+                    );
+                }
+            };
+
+    public static final FloatProperty<RecentsView> SPLIT_INSTRUCTIONS_FADE =
+            new FloatProperty<RecentsView>("splitInstructionsFade") {
+                @Override
+                public void setValue(RecentsView view, float fade) {
+                    view.mSplitInstructionsView.setAlpha(1 - fade);
+                }
+
+                @Override
+                public Float get(RecentsView view) {
+                    return 1 - view.mSplitInstructionsView.getAlpha();
+                }
+            };
+
     // OverScroll constants
     private static final int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;
 
@@ -3142,7 +3175,9 @@
             anim.addOnFrameCallback(this::updateCurveProperties);
         }
 
-        // Add a tiny bit of translation Z, so that it draws on top of other views
+        // Add a tiny bit of translation Z, so that it draws on top of other views. This is relevant
+        // (e.g.) when we dismiss a task by sliding it upward: if there is a row of icons above, we
+        // want the dragged task to stay above all other views.
         if (animateTaskView) {
             dismissedTaskView.setTranslationZ(0.1f);
         }
@@ -5289,6 +5324,11 @@
         return mRecentsAnimationController;
     }
 
+    @Nullable
+    public FloatingTaskView getFirstFloatingTaskView() {
+        return mFirstFloatingTaskView;
+    }
+
     /** Update the current activity locus id to show the enabled state of Overview */
     public void updateLocusId() {
         String locusId = "Overview";
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 5602d88..b089155 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -74,7 +74,7 @@
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.util.ComponentKey;
@@ -866,10 +866,9 @@
 
         int thumbnailTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
         int taskIconHeight = deviceProfile.overviewTaskIconSizePx;
-        int taskMargin = isGridTask ? deviceProfile.overviewTaskMarginGridPx
-                : deviceProfile.overviewTaskMarginPx;
-        int taskIconMargin = thumbnailTopMargin - taskIconHeight - taskMargin;
-        orientationHandler.setTaskIconParams(iconParams, taskIconMargin, taskIconHeight,
+        int taskMargin = deviceProfile.overviewTaskMarginPx;
+
+        orientationHandler.setTaskIconParams(iconParams, taskMargin, taskIconHeight,
                 thumbnailTopMargin, isRtl);
         iconParams.width = iconParams.height = taskIconHeight;
         mIconView.setLayoutParams(iconParams);
diff --git a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
index 09f0858..f190e27 100644
--- a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
+++ b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
@@ -35,7 +35,7 @@
  * Base class for all instrumentation tests that deal with Quickstep.
  */
 public abstract class AbstractQuickStepTest extends AbstractLauncherUiTest {
-    static final boolean ENABLE_SHELL_TRANSITIONS =
+    public static final boolean ENABLE_SHELL_TRANSITIONS =
             SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
     @Override
     protected TestRule getRulesInsideActivityMonitor() {
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfilePhone3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfilePhone3ButtonTest.kt
index 3daf81d..f41de72 100644
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfilePhone3ButtonTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfilePhone3ButtonTest.kt
@@ -340,11 +340,6 @@
     }
 
     @Test
-    fun overviewTaskMarginGridPx() {
-        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(0)
-    }
-
-    @Test
     fun overviewTaskIconSizePx() {
         assertThat(dp.overviewTaskIconSizePx).isEqualTo(168)
     }
@@ -361,7 +356,7 @@
 
     @Test
     fun overviewTaskThumbnailTopMarginPx() {
-        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(280)
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(224)
     }
 
     @Test
@@ -386,7 +381,7 @@
 
     @Test
     fun overviewRowSpacing() {
-        assertThat(dp.overviewRowSpacing).isEqualTo(-112)
+        assertThat(dp.overviewRowSpacing).isEqualTo(0)
     }
 
     @Test
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfilePhoneTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfilePhoneTest.kt
index e588c71..fe3fd00 100644
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfilePhoneTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfilePhoneTest.kt
@@ -340,11 +340,6 @@
     }
 
     @Test
-    fun overviewTaskMarginGridPx() {
-        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(0)
-    }
-
-    @Test
     fun overviewTaskIconSizePx() {
         assertThat(dp.overviewTaskIconSizePx).isEqualTo(168)
     }
@@ -361,7 +356,7 @@
 
     @Test
     fun overviewTaskThumbnailTopMarginPx() {
-        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(280)
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(224)
     }
 
     @Test
@@ -386,7 +381,7 @@
 
     @Test
     fun overviewRowSpacing() {
-        assertThat(dp.overviewRowSpacing).isEqualTo(-112)
+        assertThat(dp.overviewRowSpacing).isEqualTo(0)
     }
 
     @Test
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscape3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscape3ButtonTest.kt
index d0c9346..33866f0 100644
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscape3ButtonTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscape3ButtonTest.kt
@@ -340,11 +340,6 @@
     }
 
     @Test
-    fun overviewTaskMarginGridPx() {
-        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
-    }
-
-    @Test
     fun overviewTaskIconSizePx() {
         assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
     }
@@ -361,7 +356,7 @@
 
     @Test
     fun overviewTaskThumbnailTopMarginPx() {
-        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(128)
     }
 
     @Test
@@ -386,7 +381,7 @@
 
     @Test
     fun overviewRowSpacing() {
-        assertThat(dp.overviewRowSpacing).isEqualTo(40)
+        assertThat(dp.overviewRowSpacing).isEqualTo(72)
     }
 
     @Test
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscapeTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscapeTest.kt
index 456ed2c..e61a890 100644
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscapeTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscapeTest.kt
@@ -340,11 +340,6 @@
     }
 
     @Test
-    fun overviewTaskMarginGridPx() {
-        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
-    }
-
-    @Test
     fun overviewTaskIconSizePx() {
         assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
     }
@@ -361,7 +356,7 @@
 
     @Test
     fun overviewTaskThumbnailTopMarginPx() {
-        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(128)
     }
 
     @Test
@@ -386,7 +381,7 @@
 
     @Test
     fun overviewRowSpacing() {
-        assertThat(dp.overviewRowSpacing).isEqualTo(40)
+        assertThat(dp.overviewRowSpacing).isEqualTo(72)
     }
 
     @Test
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortrait3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortrait3ButtonTest.kt
index eed3598..3f350de 100644
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortrait3ButtonTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortrait3ButtonTest.kt
@@ -340,11 +340,6 @@
     }
 
     @Test
-    fun overviewTaskMarginGridPx() {
-        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
-    }
-
-    @Test
     fun overviewTaskIconSizePx() {
         assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
     }
@@ -361,7 +356,7 @@
 
     @Test
     fun overviewTaskThumbnailTopMarginPx() {
-        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(128)
     }
 
     @Test
@@ -386,7 +381,7 @@
 
     @Test
     fun overviewRowSpacing() {
-        assertThat(dp.overviewRowSpacing).isEqualTo(40)
+        assertThat(dp.overviewRowSpacing).isEqualTo(72)
     }
 
     @Test
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortraitTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortraitTest.kt
index 4be3e45..444f7ea 100644
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortraitTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortraitTest.kt
@@ -340,11 +340,6 @@
     }
 
     @Test
-    fun overviewTaskMarginGridPx() {
-        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
-    }
-
-    @Test
     fun overviewTaskIconSizePx() {
         assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
     }
@@ -361,7 +356,7 @@
 
     @Test
     fun overviewTaskThumbnailTopMarginPx() {
-        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(128)
     }
 
     @Test
@@ -386,7 +381,7 @@
 
     @Test
     fun overviewRowSpacing() {
-        assertThat(dp.overviewRowSpacing).isEqualTo(40)
+        assertThat(dp.overviewRowSpacing).isEqualTo(72)
     }
 
     @Test
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscape3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscape3ButtonTest.kt
index 6728540..92ab2b1 100644
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscape3ButtonTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscape3ButtonTest.kt
@@ -340,11 +340,6 @@
     }
 
     @Test
-    fun overviewTaskMarginGridPx() {
-        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
-    }
-
-    @Test
     fun overviewTaskIconSizePx() {
         assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
     }
@@ -361,7 +356,7 @@
 
     @Test
     fun overviewTaskThumbnailTopMarginPx() {
-        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(128)
     }
 
     @Test
@@ -386,7 +381,7 @@
 
     @Test
     fun overviewRowSpacing() {
-        assertThat(dp.overviewRowSpacing).isEqualTo(40)
+        assertThat(dp.overviewRowSpacing).isEqualTo(72)
     }
 
     @Test
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscapeTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscapeTest.kt
index ba3ef55..6c730ca 100644
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscapeTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscapeTest.kt
@@ -340,11 +340,6 @@
     }
 
     @Test
-    fun overviewTaskMarginGridPx() {
-        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
-    }
-
-    @Test
     fun overviewTaskIconSizePx() {
         assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
     }
@@ -361,7 +356,7 @@
 
     @Test
     fun overviewTaskThumbnailTopMarginPx() {
-        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(128)
     }
 
     @Test
@@ -386,7 +381,7 @@
 
     @Test
     fun overviewRowSpacing() {
-        assertThat(dp.overviewRowSpacing).isEqualTo(40)
+        assertThat(dp.overviewRowSpacing).isEqualTo(72)
     }
 
     @Test
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortrait3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortrait3ButtonTest.kt
index a4b446a..9048b3b 100644
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortrait3ButtonTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortrait3ButtonTest.kt
@@ -340,11 +340,6 @@
     }
 
     @Test
-    fun overviewTaskMarginGridPx() {
-        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
-    }
-
-    @Test
     fun overviewTaskIconSizePx() {
         assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
     }
@@ -361,7 +356,7 @@
 
     @Test
     fun overviewTaskThumbnailTopMarginPx() {
-        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(128)
     }
 
     @Test
@@ -386,7 +381,7 @@
 
     @Test
     fun overviewRowSpacing() {
-        assertThat(dp.overviewRowSpacing).isEqualTo(40)
+        assertThat(dp.overviewRowSpacing).isEqualTo(72)
     }
 
     @Test
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortraitTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortraitTest.kt
index 0c5968e..b737c3a 100644
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortraitTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortraitTest.kt
@@ -340,11 +340,6 @@
     }
 
     @Test
-    fun overviewTaskMarginGridPx() {
-        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
-    }
-
-    @Test
     fun overviewTaskIconSizePx() {
         assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
     }
@@ -361,7 +356,7 @@
 
     @Test
     fun overviewTaskThumbnailTopMarginPx() {
-        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(128)
     }
 
     @Test
@@ -386,7 +381,7 @@
 
     @Test
     fun overviewRowSpacing() {
-        assertThat(dp.overviewRowSpacing).isEqualTo(40)
+        assertThat(dp.overviewRowSpacing).isEqualTo(72)
     }
 
     @Test
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBar3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBar3ButtonTest.kt
index 2bad6bb..d90f56b 100644
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBar3ButtonTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBar3ButtonTest.kt
@@ -340,11 +340,6 @@
     }
 
     @Test
-    fun overviewTaskMarginGridPx() {
-        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(0)
-    }
-
-    @Test
     fun overviewTaskIconSizePx() {
         assertThat(dp.overviewTaskIconSizePx).isEqualTo(168)
     }
@@ -361,7 +356,7 @@
 
     @Test
     fun overviewTaskThumbnailTopMarginPx() {
-        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(280)
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(224)
     }
 
     @Test
@@ -386,7 +381,7 @@
 
     @Test
     fun overviewRowSpacing() {
-        assertThat(dp.overviewRowSpacing).isEqualTo(-112)
+        assertThat(dp.overviewRowSpacing).isEqualTo(0)
     }
 
     @Test
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBarTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBarTest.kt
index 6256a43..522a650 100644
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBarTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBarTest.kt
@@ -340,11 +340,6 @@
     }
 
     @Test
-    fun overviewTaskMarginGridPx() {
-        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(0)
-    }
-
-    @Test
     fun overviewTaskIconSizePx() {
         assertThat(dp.overviewTaskIconSizePx).isEqualTo(168)
     }
@@ -361,7 +356,7 @@
 
     @Test
     fun overviewTaskThumbnailTopMarginPx() {
-        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(280)
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(224)
     }
 
     @Test
@@ -386,7 +381,7 @@
 
     @Test
     fun overviewRowSpacing() {
-        assertThat(dp.overviewRowSpacing).isEqualTo(-112)
+        assertThat(dp.overviewRowSpacing).isEqualTo(0)
     }
 
     @Test
diff --git a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
index 1c15e1e..c822578 100644
--- a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
+++ b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
@@ -41,7 +41,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.RotationUtils;
 import com.android.launcher3.util.WindowBounds;
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 81df3c0..4bd627a 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -212,6 +212,7 @@
     @Test
     @NavigationModeSwitch
     @PortraitLandscape
+    @ScreenRecord // b/238461765
     public void testSwitchToOverview() throws Exception {
         assertNotNull("Workspace.switchToOverview() returned null",
                 mLauncher.goHome().switchToOverview());
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 5bb753e..c8554ec 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -378,7 +378,6 @@
     <dimen name="task_thumbnail_icon_drawable_size">0dp</dimen>
     <dimen name="task_thumbnail_icon_drawable_size_grid">0dp</dimen>
     <dimen name="overview_task_margin">0dp</dimen>
-    <dimen name="overview_task_margin_grid">0dp</dimen>
     <dimen name="overview_actions_height">0dp</dimen>
     <dimen name="overview_actions_button_spacing">0dp</dimen>
     <dimen name="overview_actions_margin_gesture">0dp</dimen>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 3a2227a..44eb4f7 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -20,7 +20,7 @@
 import static com.android.launcher3.InvariantDeviceProfile.INDEX_LANDSCAPE;
 import static com.android.launcher3.InvariantDeviceProfile.INDEX_TWO_PANEL_LANDSCAPE;
 import static com.android.launcher3.InvariantDeviceProfile.INDEX_TWO_PANEL_PORTRAIT;
-import static com.android.launcher3.ResourceUtils.pxFromDp;
+import static com.android.launcher3.testing.shared.ResourceUtils.pxFromDp;
 import static com.android.launcher3.Utilities.dpiFromPx;
 import static com.android.launcher3.Utilities.pxFromSp;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
@@ -191,7 +191,6 @@
 
     // Overview
     public int overviewTaskMarginPx;
-    public int overviewTaskMarginGridPx;
     public int overviewTaskIconSizePx;
     public int overviewTaskIconDrawableSizePx;
     public int overviewTaskIconDrawableSizeGridPx;
@@ -426,25 +425,18 @@
         }
 
         overviewTaskMarginPx = res.getDimensionPixelSize(R.dimen.overview_task_margin);
-        overviewTaskMarginGridPx = res.getDimensionPixelSize(R.dimen.overview_task_margin_grid);
         overviewTaskIconSizePx = res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_size);
         overviewTaskIconDrawableSizePx =
                 res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_drawable_size);
         overviewTaskIconDrawableSizeGridPx =
                 res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_drawable_size_grid);
-        overviewTaskThumbnailTopMarginPx = overviewTaskIconSizePx + overviewTaskMarginPx * 2;
+        overviewTaskThumbnailTopMarginPx = overviewTaskIconSizePx + overviewTaskMarginPx;
         overviewActionsTopMarginPx = res.getDimensionPixelSize(R.dimen.overview_actions_top_margin);
         overviewPageSpacing = res.getDimensionPixelSize(R.dimen.overview_page_spacing);
         overviewActionsButtonSpacing = res.getDimensionPixelSize(
                 R.dimen.overview_actions_button_spacing);
         overviewActionsHeight = res.getDimensionPixelSize(R.dimen.overview_actions_height);
-        // Grid task's top margin is only overviewTaskIconSizePx + overviewTaskMarginGridPx, but
-        // overviewTaskThumbnailTopMarginPx is applied to all TaskThumbnailView, so exclude the
-        // extra  margin when calculating row spacing.
-        int extraTopMargin = overviewTaskThumbnailTopMarginPx - overviewTaskIconSizePx
-                - overviewTaskMarginGridPx;
-        overviewRowSpacing = res.getDimensionPixelSize(R.dimen.overview_grid_row_spacing)
-                - extraTopMargin;
+        overviewRowSpacing = res.getDimensionPixelSize(R.dimen.overview_grid_row_spacing);
         overviewGridSideMargin = res.getDimensionPixelSize(R.dimen.overview_grid_side_margin);
 
         // Calculate all of the remaining variables.
@@ -1379,7 +1371,6 @@
         writer.println(prefix + pxToDpStr("workspaceBottomPadding", workspaceBottomPadding));
 
         writer.println(prefix + pxToDpStr("overviewTaskMarginPx", overviewTaskMarginPx));
-        writer.println(prefix + pxToDpStr("overviewTaskMarginGridPx", overviewTaskMarginGridPx));
         writer.println(prefix + pxToDpStr("overviewTaskIconSizePx", overviewTaskIconSizePx));
         writer.println(prefix + pxToDpStr("overviewTaskIconDrawableSizePx",
                 overviewTaskIconDrawableSizePx));
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index c1304d4..3eb2ee0 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -37,7 +37,7 @@
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragController.DragListener;
 import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 
 /*
  * The top bar containing various drop targets: Delete/App Info/Uninstall.
diff --git a/src/com/android/launcher3/FastScrollRecyclerView.java b/src/com/android/launcher3/FastScrollRecyclerView.java
index 94903f2..747b755 100644
--- a/src/com/android/launcher3/FastScrollRecyclerView.java
+++ b/src/com/android/launcher3/FastScrollRecyclerView.java
@@ -57,7 +57,9 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        bindFastScrollbar();
+        if (mScrollbar == null || !mScrollbar.hasRecyclerView()) {
+            bindFastScrollbar();
+        }
     }
 
     public void bindFastScrollbar() {
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index c051292..9b01734 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -50,6 +50,7 @@
 
 import com.android.launcher3.model.DeviceGridState;
 import com.android.launcher3.provider.RestoreDbTask;
+import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.IntArray;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 9c62251..4611408 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -60,7 +60,7 @@
 import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
 import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
 import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
-import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
+import static com.android.launcher3.testing.shared.TestProtocol.BAD_STATE;
 import static com.android.launcher3.util.ItemInfoMatcher.forFolderMatch;
 
 import android.animation.Animator;
@@ -177,7 +177,7 @@
 import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.states.RotationHelper;
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.touch.AllAppsSwipeController;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
@@ -636,7 +636,10 @@
 
     @Override
     public void onIdpChanged(boolean modelPropertiesChanged) {
-        initDeviceProfile(mDeviceProfile.inv);
+        if (!initDeviceProfile(mDeviceProfile.inv)) {
+            return;
+        }
+
         dispatchDeviceProfileChanged();
         reapplyUi();
         mDragLayer.recreateControllers();
@@ -659,9 +662,17 @@
         mDragLayer.onOneHandedModeStateChanged(activated);
     }
 
-    protected void initDeviceProfile(InvariantDeviceProfile idp) {
+    /**
+     * Returns {@code true} if a new DeviceProfile is initialized, and {@code false} otherwise.
+     */
+    protected boolean initDeviceProfile(InvariantDeviceProfile idp) {
         // Load configuration-specific DeviceProfile
-        mDeviceProfile = idp.getDeviceProfile(this);
+        DeviceProfile deviceProfile = idp.getDeviceProfile(this);
+        if (mDeviceProfile == deviceProfile) {
+            return false;
+        }
+
+        mDeviceProfile = deviceProfile;
         if (isInMultiWindowMode()) {
             mDeviceProfile = mDeviceProfile.getMultiWindowProfile(
                     this, getMultiWindowDisplaySize());
@@ -669,6 +680,7 @@
 
         onDeviceProfileInitiated();
         mModelWriter = mModel.getWriter(getDeviceProfile().isVerticalBarLayout(), true, this);
+        return true;
     }
 
     public RotationHelper getRotationHelper() {
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index de0d300..f790789 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -63,7 +63,7 @@
 import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.shortcuts.ShortcutRequest;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.PackageUserKey;
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 8b5cdbd..4532ed4 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -19,16 +19,16 @@
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
-import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
-import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
-import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
-import static com.android.launcher3.testing.TestProtocol.HINT_STATE_TWO_BUTTON_ORDINAL;
-import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
-import static com.android.launcher3.testing.TestProtocol.OVERVIEW_MODAL_TASK_STATE_ORDINAL;
-import static com.android.launcher3.testing.TestProtocol.OVERVIEW_SPLIT_SELECT_ORDINAL;
-import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
-import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
-import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.ALL_APPS_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.HINT_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.HINT_STATE_TWO_BUTTON_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_MODAL_TASK_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_SPLIT_SELECT_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
 
 import android.content.Context;
 import android.graphics.Color;
@@ -38,7 +38,7 @@
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.states.HintState;
 import com.android.launcher3.states.SpringLoadedState;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.uioverrides.states.AllAppsState;
 import com.android.launcher3.uioverrides.states.OverviewState;
 
diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index b81637f..bcd4c3f 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -31,7 +31,7 @@
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.ItemInstallQueue;
 import com.android.launcher3.pm.InstallSessionHelper;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.Executors;
 
 /**
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index e6dea8f..e025790 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -30,7 +30,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPERIGHT;
-import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
+import static com.android.launcher3.testing.shared.TestProtocol.BAD_STATE;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -169,7 +169,7 @@
     /**
      * CellInfo for the cell that is currently being dragged
      */
-    private CellLayout.CellInfo mDragInfo;
+    protected CellLayout.CellInfo mDragInfo;
 
     /**
      * Target drop area calculated during last acceptDrop call.
@@ -214,7 +214,7 @@
     private boolean mUnlockWallpaperFromDefaultPageOnLayout;
 
     public static final int REORDER_TIMEOUT = 650;
-    private final Alarm mReorderAlarm = new Alarm();
+    protected final Alarm mReorderAlarm = new Alarm();
     private PreviewBackground mFolderCreateBg;
     private FolderIcon mDragOverFolderIcon = null;
     private boolean mCreateUserFolderOnDrop = false;
@@ -242,7 +242,7 @@
     private static final int DRAG_MODE_CREATE_FOLDER = 1;
     private static final int DRAG_MODE_ADD_TO_FOLDER = 2;
     private static final int DRAG_MODE_REORDER = 3;
-    private int mDragMode = DRAG_MODE_NONE;
+    protected int mDragMode = DRAG_MODE_NONE;
     @Thunk int mLastReorderX = -1;
     @Thunk int mLastReorderY = -1;
 
@@ -1980,10 +1980,10 @@
 
                 // If the item being dropped is a shortcut and the nearest drop
                 // cell also contains a shortcut, then create a folder with the two shortcuts.
-                if (createUserFolderIfNecessary(cell, container,
-                        dropTargetLayout, mTargetCell, distance, false, d)
+                if (createUserFolderIfNecessary(cell, container, dropTargetLayout, mTargetCell,
+                        distance, false, d)
                         || addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell,
-                                distance, d, false)) {
+                        distance, d, false)) {
                     mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
                     return;
                 }
@@ -2012,8 +2012,8 @@
                     mTargetCell[0] = mTargetCell[1] = -1;
                 } else {
                     mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
-                            (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell,
-                            mTargetCell, resultSpan, CellLayout.MODE_ON_DROP);
+                            (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
+                            cell, mTargetCell, resultSpan, CellLayout.MODE_ON_DROP);
                 }
 
                 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
@@ -2297,7 +2297,7 @@
         }
     }
 
-    private void cleanupFolderCreation() {
+    protected void cleanupFolderCreation() {
         if (mFolderCreateBg != null) {
             mFolderCreateBg.animateToRest();
         }
@@ -2310,7 +2310,7 @@
         }
     }
 
-    private void cleanupReorder(boolean cancelAlarm) {
+    protected void cleanupReorder(boolean cancelAlarm) {
         // Any pending reorders are canceled
         if (cancelAlarm) {
             mReorderAlarm.cancelAlarm();
@@ -2402,26 +2402,7 @@
                     mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX,
                     item.spanY, child, mTargetCell);
 
-            if (!nearestDropOccupied) {
-                mDragTargetLayout.visualizeDropLocation(mTargetCell[0], mTargetCell[1],
-                        item.spanX, item.spanY, d);
-            } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
-                    && !mReorderAlarm.alarmPending()
-                    && (mLastReorderX != reorderX || mLastReorderY != reorderY)
-                    && targetCellDistance < mDragTargetLayout.getReorderRadius(mTargetCell)) {
-
-                int[] resultSpan = new int[2];
-                mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
-                        (int) mDragViewVisualCenter[1], minSpanX, minSpanY, item.spanX, item.spanY,
-                        child, mTargetCell, resultSpan, CellLayout.MODE_SHOW_REORDER_HINT);
-
-                // Otherwise, if we aren't adding to or creating a folder and there's no pending
-                // reorder, then we schedule a reorder
-                ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter,
-                        minSpanX, minSpanY, item.spanX, item.spanY, d, child);
-                mReorderAlarm.setOnAlarmListener(listener);
-                mReorderAlarm.setAlarm(REORDER_TIMEOUT);
-            }
+            manageReorderOnDragOver(d, targetCellDistance, nearestDropOccupied, minSpanX, minSpanY);
 
             if (mDragMode == DRAG_MODE_CREATE_FOLDER || mDragMode == DRAG_MODE_ADD_TO_FOLDER ||
                     !nearestDropOccupied) {
@@ -2432,6 +2413,34 @@
         }
     }
 
+    protected void manageReorderOnDragOver(DragObject d, float targetCellDistance,
+            boolean nearestDropOccupied, int minSpanX, int minSpanY) {
+
+        ItemInfo item = d.dragInfo;
+        final View child = (mDragInfo == null) ? null : mDragInfo.cell;
+        int reorderX = mTargetCell[0];
+        int reorderY = mTargetCell[1];
+        if (!nearestDropOccupied) {
+            mDragTargetLayout.visualizeDropLocation(mTargetCell[0], mTargetCell[1],
+                    item.spanX, item.spanY, d);
+        } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
+                && !mReorderAlarm.alarmPending()
+                && (mLastReorderX != reorderX || mLastReorderY != reorderY)
+                && targetCellDistance < mDragTargetLayout.getReorderRadius(mTargetCell)) {
+
+            int[] resultSpan = new int[2];
+            mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
+                    (int) mDragViewVisualCenter[1], minSpanX, minSpanY, item.spanX, item.spanY,
+                    child, mTargetCell, resultSpan, CellLayout.MODE_SHOW_REORDER_HINT);
+
+            // Otherwise, if we aren't adding to or creating a folder and there's no pending
+            // reorder, then we schedule a reorder
+            ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter,
+                    minSpanX, minSpanY, item.spanX, item.spanY, d, child);
+            mReorderAlarm.setOnAlarmListener(listener);
+            mReorderAlarm.setAlarm(REORDER_TIMEOUT);
+        }
+    }
     /**
      * Updates {@link #mDragTargetLayout} and {@link #mDragOverlappingLayout}
      * based on the DragObject's position.
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index dd58123..770e931 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -27,7 +27,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.Utilities;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 
 public class AccessibilityManagerCompat {
 
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 35cdfef..8616f35 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -34,7 +34,7 @@
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.ActivityContext;
 
diff --git a/src/com/android/launcher3/dragndrop/LauncherDragController.java b/src/com/android/launcher3/dragndrop/LauncherDragController.java
index dcbfa50..75f4ad6 100644
--- a/src/com/android/launcher3/dragndrop/LauncherDragController.java
+++ b/src/com/android/launcher3/dragndrop/LauncherDragController.java
@@ -37,7 +37,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 
 /**
  * Drag controller for Launcher activity
diff --git a/src/com/android/launcher3/graphics/SysUiScrim.java b/src/com/android/launcher3/graphics/SysUiScrim.java
index f0766c5..185b8d3 100644
--- a/src/com/android/launcher3/graphics/SysUiScrim.java
+++ b/src/com/android/launcher3/graphics/SysUiScrim.java
@@ -43,7 +43,7 @@
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.R;
-import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.DynamicResource;
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 31ef2e5..4c0f1ae 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -36,7 +36,7 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.pm.PackageInstallInfo;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.PackageManagerHelper;
 
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index b50ab58..8c6428b 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -33,7 +33,7 @@
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.LooperExecutor;
diff --git a/src/com/android/launcher3/model/ItemInstallQueue.java b/src/com/android/launcher3/model/ItemInstallQueue.java
index 5a220f7..229bb2d 100644
--- a/src/com/android/launcher3/model/ItemInstallQueue.java
+++ b/src/com/android/launcher3/model/ItemInstallQueue.java
@@ -49,7 +49,7 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.shortcuts.ShortcutRequest;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.PersistedItemArray;
 import com.android.launcher3.util.Preconditions;
diff --git a/src/com/android/launcher3/model/ModelUtils.java b/src/com/android/launcher3/model/ModelUtils.java
index 422af43..1ced0b1 100644
--- a/src/com/android/launcher3/model/ModelUtils.java
+++ b/src/com/android/launcher3/model/ModelUtils.java
@@ -29,7 +29,7 @@
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
 
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index 618f926..9da6670 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -39,7 +39,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.ItemInstallQueue;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.MainThreadInitializedObject;
diff --git a/src/com/android/launcher3/pm/InstallSessionTracker.java b/src/com/android/launcher3/pm/InstallSessionTracker.java
index 75cf7a8..b16aaa2 100644
--- a/src/com/android/launcher3/pm/InstallSessionTracker.java
+++ b/src/com/android/launcher3/pm/InstallSessionTracker.java
@@ -30,7 +30,7 @@
 
 import androidx.annotation.WorkerThread;
 
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.PackageUserKey;
 
 import java.lang.ref.WeakReference;
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 38b62d4..fd8b2e5 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -23,19 +23,21 @@
 import static com.android.launcher3.Utilities.dpiFromPx;
 import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
 
+import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.UiThreadHelper;
 
 /**
  * Utility class to manage launcher rotation
  */
 public class RotationHelper implements OnSharedPreferenceChangeListener,
-        DeviceProfile.OnDeviceProfileChangeListener {
+        DisplayController.DisplayInfoChangeListener {
 
     private static final String TAG = "RotationHelper";
 
@@ -119,8 +121,8 @@
     }
 
     @Override
-    public void onDeviceProfileChanged(DeviceProfile dp) {
-        boolean ignoreAutoRotateSettings = dp.isTablet;
+    public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) {
+        boolean ignoreAutoRotateSettings = info.isTablet(info.realBounds);
         if (mIgnoreAutoRotateSettings != ignoreAutoRotateSettings) {
             setIgnoreAutoRotateSettings(ignoreAutoRotateSettings);
             notifyChange();
@@ -157,8 +159,10 @@
     public void initialize() {
         if (!mInitialized) {
             mInitialized = true;
-            setIgnoreAutoRotateSettings(mActivity.getDeviceProfile().isTablet);
-            mActivity.addOnDeviceProfileChangeListener(this);
+            DisplayController displayController = DisplayController.INSTANCE.get(mActivity);
+            DisplayController.Info info = displayController.getInfo();
+            setIgnoreAutoRotateSettings(info.isTablet(info.realBounds));
+            displayController.addChangeListener(this);
             notifyChange();
         }
     }
@@ -166,7 +170,7 @@
     public void destroy() {
         if (!mDestroyed) {
             mDestroyed = true;
-            mActivity.removeOnDeviceProfileChangeListener(this);
+            DisplayController.INSTANCE.get(mActivity).removeChangeListener(this);
             mActivity = null;
             if (mSharedPrefs != null) {
                 mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
diff --git a/src/com/android/launcher3/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java
index f99519d..54735f0 100644
--- a/src/com/android/launcher3/states/StateAnimationConfig.java
+++ b/src/com/android/launcher3/states/StateAnimationConfig.java
@@ -64,6 +64,8 @@
             ANIM_DEPTH,
             ANIM_OVERVIEW_ACTIONS_FADE,
             ANIM_WORKSPACE_PAGE_TRANSLATE_X,
+            ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN,
+            ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AnimType {}
@@ -84,8 +86,10 @@
     public static final int ANIM_DEPTH = 13;
     public static final int ANIM_OVERVIEW_ACTIONS_FADE = 14;
     public static final int ANIM_WORKSPACE_PAGE_TRANSLATE_X = 15;
+    public static final int ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN = 17;
+    public static final int ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE = 18;
 
-    private static final int ANIM_TYPES_COUNT = 17;
+    private static final int ANIM_TYPES_COUNT = 19;
 
     protected final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT];
 
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 0334b96..d3c9bc9 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -41,6 +41,9 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.testing.shared.HotseatCellCenterRequest;
+import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.launcher3.testing.shared.WorkspaceCellCenterRequest;
 import com.android.launcher3.util.ResourceBasedOverride;
 import com.android.launcher3.widget.picker.WidgetsFullSheet;
 
diff --git a/src/com/android/launcher3/testing/TestLogging.java b/src/com/android/launcher3/testing/TestLogging.java
index 103b565..c151606 100644
--- a/src/com/android/launcher3/testing/TestLogging.java
+++ b/src/com/android/launcher3/testing/TestLogging.java
@@ -22,6 +22,7 @@
 import android.view.MotionEvent;
 
 import com.android.launcher3.Utilities;
+import com.android.launcher3.testing.shared.TestProtocol;
 
 import java.util.function.BiConsumer;
 
diff --git a/src/com/android/launcher3/testing/HotseatCellCenterRequest.java b/src/com/android/launcher3/testing/shared/HotseatCellCenterRequest.java
similarity index 98%
rename from src/com/android/launcher3/testing/HotseatCellCenterRequest.java
rename to src/com/android/launcher3/testing/shared/HotseatCellCenterRequest.java
index cbb847e..7eb035a 100644
--- a/src/com/android/launcher3/testing/HotseatCellCenterRequest.java
+++ b/src/com/android/launcher3/testing/shared/HotseatCellCenterRequest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3.testing;
+package com.android.launcher3.testing.shared;
 
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/src/com/android/launcher3/ResourceUtils.java b/src/com/android/launcher3/testing/shared/ResourceUtils.java
similarity index 98%
rename from src/com/android/launcher3/ResourceUtils.java
rename to src/com/android/launcher3/testing/shared/ResourceUtils.java
index f709aca..af462cc 100644
--- a/src/com/android/launcher3/ResourceUtils.java
+++ b/src/com/android/launcher3/testing/shared/ResourceUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.testing.shared;
 
 import android.content.res.Resources;
 import android.util.DisplayMetrics;
diff --git a/src/com/android/launcher3/testing/TestInformationRequest.java b/src/com/android/launcher3/testing/shared/TestInformationRequest.java
similarity index 94%
rename from src/com/android/launcher3/testing/TestInformationRequest.java
rename to src/com/android/launcher3/testing/shared/TestInformationRequest.java
index 272ae56..38282032 100644
--- a/src/com/android/launcher3/testing/TestInformationRequest.java
+++ b/src/com/android/launcher3/testing/shared/TestInformationRequest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3.testing;
+package com.android.launcher3.testing.shared;
 
 import android.os.Parcelable;
 
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/shared/TestProtocol.java
similarity index 99%
rename from src/com/android/launcher3/testing/TestProtocol.java
rename to src/com/android/launcher3/testing/shared/TestProtocol.java
index 9bc9067..d872476 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/shared/TestProtocol.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3.testing;
+package com.android.launcher3.testing.shared;
 
 /**
  * Protocol for custom accessibility events for communication with UI Automation tests.
diff --git a/src/com/android/launcher3/testing/WorkspaceCellCenterRequest.java b/src/com/android/launcher3/testing/shared/WorkspaceCellCenterRequest.java
similarity index 98%
rename from src/com/android/launcher3/testing/WorkspaceCellCenterRequest.java
rename to src/com/android/launcher3/testing/shared/WorkspaceCellCenterRequest.java
index 71ab09f..80dbef8 100644
--- a/src/com/android/launcher3/testing/WorkspaceCellCenterRequest.java
+++ b/src/com/android/launcher3/testing/shared/WorkspaceCellCenterRequest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3.testing;
+package com.android.launcher3.testing.shared;
 
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index e95a787..5fa30bc 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -61,7 +61,7 @@
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.views.FloatingIconView;
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index 53cd416..73f994f 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -36,7 +36,7 @@
 import com.android.launcher3.logging.StatsLogManager.StatsLogger;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.views.BubbleTextHolder;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index fa4eb70..9ac1c0e 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -523,6 +523,7 @@
         iconParams.rightMargin = -taskIconHeight - taskIconMargin / 2;
         iconParams.leftMargin = 0;
         iconParams.topMargin = thumbnailTopMargin / 2;
+        iconParams.bottomMargin = 0;
     }
 
     @Override
@@ -580,4 +581,22 @@
             FloatProperty secondary, DeviceProfile deviceProfile) {
         return new Pair<>(primary, secondary);
     }
+
+    @Override
+    public float getFloatingTaskOffscreenTranslationTarget(View floatingTask, RectF onScreenRect,
+            @StagePosition int stagePosition, DeviceProfile dp) {
+        float currentTranslationY = floatingTask.getTranslationY();
+        return currentTranslationY - onScreenRect.height();
+    }
+
+    @Override
+    public void setFloatingTaskPrimaryTranslation(View floatingTask, float translation,
+            DeviceProfile dp) {
+        floatingTask.setTranslationY(translation);
+    }
+
+    @Override
+    public Float getFloatingTaskPrimaryTranslation(View floatingTask, DeviceProfile dp) {
+        return floatingTask.getTranslationY();
+    }
 }
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 6bc021b..1a8d355 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -34,9 +34,9 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
 
 import java.util.List;
 
@@ -242,6 +242,41 @@
      */
     void fixBoundsForHomeAnimStartRect(RectF outStartRect, DeviceProfile deviceProfile);
 
+    /**
+     * Determine the target translation for animating the FloatingTaskView out. This value could
+     * either be an x-coordinate or a y-coordinate, depending on which way the FloatingTaskView was
+     * docked.
+     *
+     * @param floatingTask The FloatingTaskView.
+     * @param onScreenRect The current on-screen dimensions of the FloatingTaskView.
+     * @param stagePosition STAGE_POSITION_TOP_OR_LEFT or STAGE_POSITION_BOTTOM_OR_RIGHT.
+     * @param dp The device profile.
+     * @return A float. When an animation translates the FloatingTaskView to this position, it will
+     * appear to tuck away off the edge of the screen.
+     */
+    float getFloatingTaskOffscreenTranslationTarget(View floatingTask, RectF onScreenRect,
+            @StagePosition int stagePosition, DeviceProfile dp);
+
+    /**
+     * Sets the translation of a FloatingTaskView along its "slide-in/slide-out" axis (could be
+     * either x or y), depending on how the view is oriented.
+     *
+     * @param floatingTask The FloatingTaskView to be translated.
+     * @param translation The target translation value.
+     * @param dp The current device profile.
+     */
+    void setFloatingTaskPrimaryTranslation(View floatingTask, float translation, DeviceProfile dp);
+
+    /**
+     * Gets the translation of a FloatingTaskView along its "slide-in/slide-out" axis (could be
+     * either x or y), depending on how the view is oriented.
+     *
+     * @param floatingTask The FloatingTaskView in question.
+     * @param dp The current device profile.
+     * @return The current translation value.
+     */
+    Float getFloatingTaskPrimaryTranslation(View floatingTask, DeviceProfile dp);
+
     class ChildBounds {
 
         public final int primaryDimension;
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 181dca5..dd9f642 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -52,9 +52,9 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
 import com.android.launcher3.views.BaseDragLayer;
 
 import java.util.List;
@@ -658,8 +658,9 @@
     public void setTaskIconParams(FrameLayout.LayoutParams iconParams, int taskIconMargin,
             int taskIconHeight, int thumbnailTopMargin, boolean isRtl) {
         iconParams.gravity = TOP | CENTER_HORIZONTAL;
+        // Reset margins, since they may have been set on rotation
         iconParams.leftMargin = iconParams.rightMargin = 0;
-        iconParams.topMargin = taskIconMargin;
+        iconParams.topMargin = iconParams.bottomMargin = 0;
     }
 
     @Override
@@ -756,4 +757,36 @@
             return new Pair<>(secondary, primary);
         }
     }
+
+    @Override
+    public float getFloatingTaskOffscreenTranslationTarget(View floatingTask, RectF onScreenRect,
+            @StagePosition int stagePosition, DeviceProfile dp) {
+        if (dp.isLandscape) {
+            float currentTranslationX = floatingTask.getTranslationX();
+            return stagePosition == STAGE_POSITION_TOP_OR_LEFT
+                    ? currentTranslationX - onScreenRect.width()
+                    : currentTranslationX + onScreenRect.width();
+        } else {
+            float currentTranslationY = floatingTask.getTranslationY();
+            return currentTranslationY - onScreenRect.height();
+        }
+    }
+
+    @Override
+    public void setFloatingTaskPrimaryTranslation(View floatingTask, float translation,
+            DeviceProfile dp) {
+        if (dp.isLandscape) {
+            floatingTask.setTranslationX(translation);
+        } else {
+            floatingTask.setTranslationY(translation);
+        }
+
+    }
+
+    @Override
+    public Float getFloatingTaskPrimaryTranslation(View floatingTask, DeviceProfile dp) {
+        return dp.isLandscape
+                ? floatingTask.getTranslationX()
+                : floatingTask.getTranslationY();
+    }
 }
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 339f910..387e980 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -216,6 +216,7 @@
         iconParams.leftMargin = -taskIconHeight - taskIconMargin / 2;
         iconParams.rightMargin = 0;
         iconParams.topMargin = thumbnailTopMargin / 2;
+        iconParams.bottomMargin = 0;
     }
 
     @Override
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index 17bbdf1..6cb021b 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -43,7 +43,7 @@
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 
 /**
  * Helper class to handle touch on empty space in workspace and show options popup on long press
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 15fe1d9..94f9f25 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -19,7 +19,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
-import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
+import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE;
 import static com.android.launcher3.Utilities.dpiFromPx;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_2_BUTTON;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_3_BUTTON;
@@ -46,7 +46,7 @@
 import androidx.annotation.AnyThread;
 import androidx.annotation.UiThread;
 
-import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
 import com.android.launcher3.util.window.CachedDisplayInfo;
@@ -278,11 +278,11 @@
         public final float fontScale;
         private final int densityDpi;
         public final NavigationMode navigationMode;
-
         private final PortraitSize mScreenSizeDp;
 
+        // WindowBounds
+        public final WindowBounds realBounds;
         public final Set<WindowBounds> supportedBounds = new ArraySet<>();
-
         private final ArrayMap<CachedDisplayInfo, WindowBounds[]> mPerDisplayBounds =
                 new ArrayMap<>();
 
@@ -310,7 +310,7 @@
             mPerDisplayBounds.putAll(perDisplayBoundsCache);
             WindowBounds[] cachedValue = mPerDisplayBounds.get(normalizedDisplayInfo);
 
-            WindowBounds realBounds = wmProxy.getRealBounds(displayInfoContext, displayInfo);
+            realBounds = wmProxy.getRealBounds(displayInfoContext, displayInfo);
             if (cachedValue == null) {
                 // Unexpected normalizedDisplayInfo is found, recreate the cache
                 Log.e(TAG, "Unexpected normalizedDisplayInfo found, invalidating cache");
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index d5a065a..582ff8d 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -17,13 +17,13 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
-import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT;
-import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT_LANDSCAPE;
-import static com.android.launcher3.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE;
-import static com.android.launcher3.ResourceUtils.STATUS_BAR_HEIGHT;
-import static com.android.launcher3.ResourceUtils.STATUS_BAR_HEIGHT_LANDSCAPE;
-import static com.android.launcher3.ResourceUtils.STATUS_BAR_HEIGHT_PORTRAIT;
+import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE;
+import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_HEIGHT;
+import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_HEIGHT_LANDSCAPE;
+import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE;
+import static com.android.launcher3.testing.shared.ResourceUtils.STATUS_BAR_HEIGHT;
+import static com.android.launcher3.testing.shared.ResourceUtils.STATUS_BAR_HEIGHT_LANDSCAPE;
+import static com.android.launcher3.testing.shared.ResourceUtils.STATUS_BAR_HEIGHT_PORTRAIT;
 import static com.android.launcher3.Utilities.dpiFromPx;
 import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
 import static com.android.launcher3.util.RotationUtils.deltaRotation;
@@ -48,7 +48,7 @@
 import android.view.WindowMetrics;
 
 import com.android.launcher3.R;
-import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.ResourceBasedOverride;
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 2a9a8a5..d301925 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -50,7 +50,7 @@
 import com.android.launcher3.popup.ArrowPopup;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.widget.picker.WidgetsFullSheet;
 
 import java.util.ArrayList;
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 11ca130..6e6512d 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -174,6 +174,11 @@
         ta.recycle();
     }
 
+    /** @return whether there is a RecyclerView bound to this scroller. */
+    public boolean hasRecyclerView() {
+        return mRv != null;
+    }
+
     public void setRecyclerView(FastScrollRecyclerView rv, TextView popupView) {
         if (mRv != null && mOnScrollListener != null) {
             mRv.removeOnScrollListener(mOnScrollListener);
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 8962c4f..5cffd48 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -42,7 +42,7 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.Themes;
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
index 98a960c..5ce8fcf 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
@@ -42,7 +42,7 @@
 import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.widget.custom.CustomWidgetManager;
 
 import java.util.ArrayList;
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index 46c0b99..c8d528b 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -42,7 +42,7 @@
 import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.icons.RoundDrawableWrapper;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
 import com.android.launcher3.widget.util.WidgetSizes;
 
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 88d9723..0d9198f 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -19,7 +19,7 @@
 
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
-import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
diff --git a/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java b/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java
index 8d275cc..55b7bf1 100644
--- a/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java
+++ b/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java
@@ -17,10 +17,10 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
-import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT;
-import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT_LANDSCAPE;
-import static com.android.launcher3.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE;
-import static com.android.launcher3.ResourceUtils.getDimenByName;
+import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_HEIGHT;
+import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_HEIGHT_LANDSCAPE;
+import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE;
+import static com.android.launcher3.testing.shared.ResourceUtils.getDimenByName;
 
 import android.content.Context;
 import android.content.res.Resources;
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 1f6e1ec..304153f 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -61,7 +61,7 @@
 import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
 import com.android.launcher3.tapl.TestHelpers;
 import com.android.launcher3.testcomponent.TestCommandReceiver;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.LooperExecutor;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Wait;
diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
index 8f2d528..266f0ae 100644
--- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
+++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
@@ -4,7 +4,6 @@
 import android.view.Surface;
 
 import com.android.launcher3.tapl.TestHelpers;
-import com.android.launcher3.testing.TestProtocol;
 
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index bfb115d..4791846 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -29,7 +29,7 @@
 import androidx.test.uiautomator.StaleObjectException;
 import androidx.test.uiautomator.UiObject2;
 
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 
 import java.util.stream.Collectors;
 
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index e28f0af..d221259 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -22,7 +22,7 @@
 import androidx.test.uiautomator.BySelector;
 import androidx.test.uiautomator.UiObject2;
 
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 
 import java.util.regex.Pattern;
 
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
index 5cf5aba..284168b 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
@@ -18,7 +18,7 @@
 
 import androidx.test.uiautomator.UiObject2;
 
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 
 /**
  * Menu item in an app icon menu.
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 589e13c..eb8d055 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -19,7 +19,7 @@
 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
 
 import static com.android.launcher3.tapl.OverviewTask.TASK_START_EVENT;
-import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
 
 import android.graphics.Point;
 import android.os.SystemClock;
@@ -28,7 +28,7 @@
 import androidx.annotation.NonNull;
 import androidx.test.uiautomator.UiObject2;
 
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 
 import java.util.List;
 import java.util.regex.Pattern;
diff --git a/tests/tapl/com/android/launcher3/tapl/FolderIcon.java b/tests/tapl/com/android/launcher3/tapl/FolderIcon.java
index 9b4717f..0c453bd 100644
--- a/tests/tapl/com/android/launcher3/tapl/FolderIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/FolderIcon.java
@@ -21,7 +21,7 @@
 import androidx.annotation.NonNull;
 import androidx.test.uiautomator.UiObject2;
 
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 
 /**
  * Folder Icon, an app folder in workspace.
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index 33fea2d..3dcb437 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -16,7 +16,7 @@
 
 package com.android.launcher3.tapl;
 
-import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
 
 import android.graphics.Point;
 import android.view.MotionEvent;
@@ -26,7 +26,7 @@
 import androidx.test.uiautomator.UiObject2;
 import androidx.test.uiautomator.Until;
 
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 
 /**
  * Ancestor for AppIcon and AppMenuItem.
diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
index 046d36b..04167839 100644
--- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
+++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
@@ -16,9 +16,9 @@
 
 package com.android.launcher3.tapl;
 
-import static com.android.launcher3.testing.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING;
-import static com.android.launcher3.testing.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
-import static com.android.launcher3.testing.TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT;
+import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING;
+import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
+import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT;
 
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -27,7 +27,7 @@
 
 import androidx.test.uiautomator.By;
 
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 
 /**
  * Background state operations specific to when an app has been launched.
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 5f92097..caaeef8 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -23,7 +23,7 @@
 
 import static com.android.launcher3.tapl.Folder.FOLDER_CONTENT_RES_ID;
 import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName;
-import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
 
 import android.app.ActivityManager;
 import android.app.Instrumentation;
@@ -65,9 +65,9 @@
 import androidx.test.uiautomator.UiObject2;
 import androidx.test.uiautomator.Until;
 
-import com.android.launcher3.ResourceUtils;
-import com.android.launcher3.testing.TestInformationRequest;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.ResourceUtils;
+import com.android.launcher3.testing.shared.TestInformationRequest;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.systemui.shared.system.ContextUtils;
 import com.android.systemui.shared.system.QuickStepContract;
 
diff --git a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
index 710e3cd..672c6e0 100644
--- a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
+++ b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
@@ -15,13 +15,13 @@
  */
 package com.android.launcher3.tapl;
 
-import static com.android.launcher3.testing.TestProtocol.SEQUENCE_MAIN;
-import static com.android.launcher3.testing.TestProtocol.SEQUENCE_PILFER;
-import static com.android.launcher3.testing.TestProtocol.SEQUENCE_TIS;
+import static com.android.launcher3.testing.shared.TestProtocol.SEQUENCE_MAIN;
+import static com.android.launcher3.testing.shared.TestProtocol.SEQUENCE_PILFER;
+import static com.android.launcher3.testing.shared.TestProtocol.SEQUENCE_TIS;
 
 import android.os.SystemClock;
 
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 
 import java.util.ArrayList;
 import java.util.HashMap;
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
index 5710713..386deac 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
@@ -19,7 +19,7 @@
 import androidx.annotation.NonNull;
 import androidx.test.uiautomator.UiObject2;
 
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 
 /**
  * View containing overview actions
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index ac7424e..72a39df 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -24,7 +24,7 @@
 import androidx.test.uiautomator.BySelector;
 import androidx.test.uiautomator.UiObject2;
 
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 
 import java.util.List;
 import java.util.regex.Pattern;
diff --git a/tests/tapl/com/android/launcher3/tapl/Taskbar.java b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
index b5a08c3..5d9be36 100644
--- a/tests/tapl/com/android/launcher3/tapl/Taskbar.java
+++ b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
@@ -15,8 +15,8 @@
  */
 package com.android.launcher3.tapl;
 
-import static com.android.launcher3.testing.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING;
-import static com.android.launcher3.testing.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
+import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING;
+import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
 
 import android.graphics.Point;
 import android.os.SystemClock;
diff --git a/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java
index 69a8a08..424c58e 100644
--- a/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java
+++ b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java
@@ -17,7 +17,7 @@
 
 import androidx.test.uiautomator.UiObject2;
 
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 
 import java.util.regex.Pattern;
 
diff --git a/tests/tapl/com/android/launcher3/tapl/Widget.java b/tests/tapl/com/android/launcher3/tapl/Widget.java
index e1a09af..29eb46c 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widget.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widget.java
@@ -23,7 +23,7 @@
 import androidx.test.uiautomator.UiObject2;
 import androidx.test.uiautomator.Until;
 
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 
 import java.util.regex.Pattern;
 
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 7fd68c0..96e2e3f 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -27,7 +27,7 @@
 import androidx.test.uiautomator.UiObject2;
 import androidx.test.uiautomator.Until;
 
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.shared.TestProtocol;
 
 import java.util.Collection;
 
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 6044b06..efbdb23 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -18,8 +18,8 @@
 
 import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_SCROLLED;
 
-import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
-import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.ALL_APPS_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
 
 import static junit.framework.TestCase.assertNotNull;
 import static junit.framework.TestCase.assertTrue;
@@ -39,9 +39,9 @@
 import androidx.test.uiautomator.UiObject2;
 import androidx.test.uiautomator.Until;
 
-import com.android.launcher3.testing.HotseatCellCenterRequest;
-import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.testing.WorkspaceCellCenterRequest;
+import com.android.launcher3.testing.shared.HotseatCellCenterRequest;
+import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.launcher3.testing.shared.WorkspaceCellCenterRequest;
 
 import java.util.List;
 import java.util.Map;