Merge "[a11y] Do not announce "item added to homescreen" after adding web shortcut." into main
diff --git a/quickstep/dagger/LauncherAppComponent.java b/quickstep/dagger/com/android/launcher3/dagger/LauncherAppComponent.java
similarity index 91%
rename from quickstep/dagger/LauncherAppComponent.java
rename to quickstep/dagger/com/android/launcher3/dagger/LauncherAppComponent.java
index 068f01c..a4cb420 100644
--- a/quickstep/dagger/LauncherAppComponent.java
+++ b/quickstep/dagger/com/android/launcher3/dagger/LauncherAppComponent.java
@@ -17,7 +17,6 @@
 package com.android.launcher3.dagger;
 
 
-import com.android.quickstep.dagger.QuickStepModule;
 import com.android.quickstep.dagger.QuickstepBaseAppComponent;
 
 import dagger.Component;
@@ -26,7 +25,7 @@
  * Root component for Dagger injection for Launcher Quickstep.
  */
 @LauncherAppSingleton
-@Component(modules = QuickStepModule.class)
+@Component(modules = LauncherAppModule.class)
 public interface LauncherAppComponent extends QuickstepBaseAppComponent {
     /** Builder for quickstep LauncherAppComponent. */
     @Component.Builder
diff --git a/quickstep/dagger/com/android/launcher3/dagger/LauncherAppModule.java b/quickstep/dagger/com/android/launcher3/dagger/LauncherAppModule.java
new file mode 100644
index 0000000..1711fc1
--- /dev/null
+++ b/quickstep/dagger/com/android/launcher3/dagger/LauncherAppModule.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 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.dagger;
+
+import com.android.quickstep.dagger.QuickStepModule;
+
+import dagger.Module;
+
+@Module(includes = QuickStepModule.class)
+public class LauncherAppModule {}
diff --git a/quickstep/res/drawable/bg_bubble_dismiss_circle.xml b/quickstep/res/drawable/bg_bubble_dismiss_circle.xml
deleted file mode 100644
index b793eec..0000000
--- a/quickstep/res/drawable/bg_bubble_dismiss_circle.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2023 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.
-  -->
-
-<shape
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval">
-
-    <stroke
-        android:width="2dp"
-        android:color="@android:color/system_accent1_600" />
-
-    <solid android:color="@android:color/system_accent1_600" />
-</shape>
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_bubble_dismiss_white.xml b/quickstep/res/drawable/ic_bubble_dismiss_white.xml
deleted file mode 100644
index b15111b..0000000
--- a/quickstep/res/drawable/ic_bubble_dismiss_white.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2023 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="20dp"
-    android:height="20dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
-    <path
-        android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
-        android:fillColor="@android:color/system_neutral1_50"/>
-</vector>
diff --git a/quickstep/res/layout/floating_header_content.xml b/quickstep/res/layout/floating_header_content.xml
index b21c34b..0021e22 100644
--- a/quickstep/res/layout/floating_header_content.xml
+++ b/quickstep/res/layout/floating_header_content.xml
@@ -3,8 +3,8 @@
 
     <com.android.launcher3.appprediction.PredictionRowView
         android:id="@+id/prediction_row"
-        android:accessibilityPaneTitle="@string/title_app_suggestions"
         android:layout_width="match_parent"
+        android:importantForAccessibility="yes"
         android:layout_height="wrap_content" />
 
     <com.android.launcher3.appprediction.AppsDividerView
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index b221b22..b404bb5 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -467,9 +467,9 @@
     <dimen name="bubblebar_icon_elevation">1dp</dimen>
 
     <!-- Bubble bar dismiss view -->
-    <dimen name="bubblebar_dismiss_target_size">96dp</dimen>
+    <dimen name="bubblebar_dismiss_target_size">@dimen/floating_dismiss_background_size</dimen>
     <dimen name="bubblebar_dismiss_target_small_size">60dp</dimen>
-    <dimen name="bubblebar_dismiss_target_icon_size">24dp</dimen>
+    <dimen name="bubblebar_dismiss_target_icon_size">@dimen/floating_dismiss_icon_size</dimen>
     <dimen name="bubblebar_dismiss_target_bottom_margin">50dp</dimen>
     <dimen name="bubblebar_dismiss_floating_gradient_height">548dp</dimen>
     <dimen name="bubblebar_dismiss_zone_width">192dp</dimen>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 026e25c..df949c3 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -125,8 +125,6 @@
     <string name="back_gesture_intro_title">Swipe to go back</string>
     <!-- Introduction subtitle for the Back gesture tutorial. [CHAR LIMIT=200] -->
     <string name="back_gesture_intro_subtitle">To go back to the last screen, swipe from the left or right edge to the middle of the screen.</string>
-    <!-- Introduction subtitle for the Back gesture tutorial that will be spoken by screen readers. [CHAR LIMIT=200] -->
-    <string name="back_gesture_spoken_intro_subtitle">To go back to the last screen, swipe with 2 fingers from the left or right edge to the middle of the screen.</string>
     <!-- Title of the gesture tutorial section educating users on how to go back to the previous screen. [CHAR LIMIT=100] -->
     <string name="back_gesture_tutorial_title">Go back</string>
     <!-- Subtitle of the gesture tutorial section educating users on how to go to back to the previous screen [CHAR LIMIT=100] -->
@@ -145,8 +143,6 @@
     <string name="home_gesture_intro_title">Swipe to go home</string>
     <!-- Introduction subtitle for the Home gesture tutorial. [CHAR LIMIT=100] -->
     <string name="home_gesture_intro_subtitle">Swipe up from the bottom of your screen. This gesture always takes you to the Home screen.</string>
-    <!-- Introduction subtitle for the Home gesture tutorial that will be spoken by screen readers. [CHAR LIMIT=100] -->
-    <string name="home_gesture_spoken_intro_subtitle">Swipe up with 2 fingers from the bottom of the screen. This gesture always takes you to the Home screen.</string>
     <!-- Title of the gesture tutorial section educating users on how to go to the home screen. [CHAR LIMIT=100] -->
     <string name="home_gesture_tutorial_title">Go home</string>
     <!-- Subtitle of the gesture tutorial section educating users on how to go to the home screen [CHAR LIMIT=100] -->
@@ -168,8 +164,6 @@
     <string name="overview_gesture_intro_title">Swipe to switch apps</string>
     <!-- Introduction subtitle for the Overview gesture tutorial. [CHAR LIMIT=100] -->
     <string name="overview_gesture_intro_subtitle">To switch between apps, swipe up from the bottom of your screen, hold, then release.</string>
-    <!-- Introduction subtitle for the Overview gesture tutorial that will be spoken by screen readers. [CHAR LIMIT=100] -->
-    <string name="overview_gesture_spoken_intro_subtitle">To switch between apps, swipe up with 2 fingers from the bottom of your screen, hold, then release.</string>
     <!-- Title of the gesture tutorial section educating users on how to switch between apps. [CHAR LIMIT=100] -->
     <string name="overview_gesture_tutorial_title">Switch apps</string>
     <!-- Subtitle of the gesture tutorial section educating users on how to switch between apps [CHAR LIMIT=100] -->
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index 8e80aa5..22e491f 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -16,12 +16,16 @@
 
 package com.android.launcher3.appprediction;
 
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+
 import android.content.Context;
 import android.graphics.Canvas;
+import android.os.Build;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.LinearLayout;
 
 import androidx.annotation.NonNull;
@@ -91,6 +95,14 @@
     }
 
     @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        if (Build.VERSION.SDK_INT >= UPSIDE_DOWN_CAKE) {
+            info.setContainerTitle(mActivityContext.getString(R.string.title_app_suggestions));
+        }
+    }
+
+    @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         mActivityContext.addOnDeviceProfileChangeListener(this);
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index fd0243a..488cea5 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -111,6 +111,17 @@
     public boolean areDesktopTasksVisible() {
         boolean desktopTasksVisible = mVisibleDesktopTasksCount > 0;
         if (DEBUG) {
+            Log.d(TAG, "areDesktopTasksVisible: desktopVisible=" + desktopTasksVisible);
+        }
+        return desktopTasksVisible;
+    }
+
+    /**
+     * Whether desktop tasks are visible in desktop mode.
+     */
+    public boolean areDesktopTasksVisibleAndNotInOverview() {
+        boolean desktopTasksVisible = mVisibleDesktopTasksCount > 0;
+        if (DEBUG) {
             Log.d(TAG, "areDesktopTasksVisible: desktopVisible=" + desktopTasksVisible
                     + " overview=" + mInOverviewState);
         }
@@ -160,9 +171,9 @@
         if (visibleTasksCount != mVisibleDesktopTasksCount) {
             final boolean wasVisible = mVisibleDesktopTasksCount > 0;
             final boolean isVisible = visibleTasksCount > 0;
-            final boolean wereDesktopTasksVisibleBefore = areDesktopTasksVisible();
+            final boolean wereDesktopTasksVisibleBefore = areDesktopTasksVisibleAndNotInOverview();
             mVisibleDesktopTasksCount = visibleTasksCount;
-            final boolean areDesktopTasksVisibleNow = areDesktopTasksVisible();
+            final boolean areDesktopTasksVisibleNow = areDesktopTasksVisibleAndNotInOverview();
             if (wereDesktopTasksVisibleBefore != areDesktopTasksVisibleNow) {
                 notifyDesktopVisibilityListeners(areDesktopTasksVisibleNow);
             }
@@ -219,13 +230,12 @@
                     + " currentValue=" + mInOverviewState);
         }
         if (overviewStateEnabled != mInOverviewState) {
-            final boolean wereDesktopTasksVisibleBefore = areDesktopTasksVisible();
+            final boolean wereDesktopTasksVisibleBefore = areDesktopTasksVisibleAndNotInOverview();
             mInOverviewState = overviewStateEnabled;
-            final boolean areDesktopTasksVisibleNow = areDesktopTasksVisible();
+            final boolean areDesktopTasksVisibleNow = areDesktopTasksVisibleAndNotInOverview();
             if (wereDesktopTasksVisibleBefore != areDesktopTasksVisibleNow) {
                 notifyDesktopVisibilityListeners(areDesktopTasksVisibleNow);
             }
-
             if (ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()) {
                 return;
             }
@@ -279,7 +289,7 @@
             if (mBackgroundStateEnabled) {
                 setLauncherViewsVisibility(View.VISIBLE);
                 markLauncherResumed();
-            } else if (areDesktopTasksVisible() && !mGestureInProgress) {
+            } else if (areDesktopTasksVisibleAndNotInOverview() && !mGestureInProgress) {
                 // Switching out of background state. If desktop tasks are visible, pause launcher.
                 setLauncherViewsVisibility(View.INVISIBLE);
                 markLauncherPaused();
diff --git a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
index 6a908ca..d6327bc 100644
--- a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
@@ -28,7 +28,6 @@
 import com.android.launcher3.statemanager.StatefulContainer;
 import com.android.quickstep.TopTaskTracker;
 import com.android.quickstep.fallback.RecentsState;
-import com.android.quickstep.util.TISBindHelper;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.RecentsViewContainer;
 
@@ -139,12 +138,6 @@
         return topTask.isHomeTask() || topTask.isRecentsTask();
     }
 
-    @Nullable
-    @Override
-    protected TISBindHelper getTISBindHelper() {
-        return mRecentsContainer.getTISBindHelper();
-    }
-
     @Override
     protected String getTaskbarUIControllerName() {
         return "FallbackTaskbarUIController<" + mRecentsContainer.getClass().getSimpleName() + ">";
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index de42669..cd38e5e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -188,8 +188,8 @@
         mQuickSwitchViewController = new KeyboardQuickSwitchViewController(
                 mControllers, mOverlayContext, keyboardQuickSwitchView, mControllerCallbacks);
 
-        final boolean onDesktop =
-                mControllers.taskbarDesktopModeController.getAreDesktopTasksVisible();
+        final boolean onDesktop = mControllers.taskbarDesktopModeController
+                .getAreDesktopTasksVisibleAndNotInOverview();
 
         if (mModel.isTaskListValid(mTaskListChangeId)
                 && taskIdsToExclude.equals(mExcludedTaskIds)) {
@@ -233,7 +233,7 @@
     private void processLoadedTasks(List<GroupTask> tasks, Set<Integer> taskIdsToExclude) {
         mHasDesktopTask = false;
         mWasDesktopTaskFilteredOut = false;
-        if (mControllers.taskbarDesktopModeController.getAreDesktopTasksVisible()) {
+        if (mControllers.taskbarDesktopModeController.getAreDesktopTasksVisibleAndNotInOverview()) {
             processLoadedTasksOnDesktop(tasks, taskIdsToExclude);
         } else {
             processLoadedTasksOutsideDesktop(tasks, taskIdsToExclude);
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index ea42d77..80da467 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -49,7 +49,6 @@
 import com.android.quickstep.RecentsAnimationCallbacks;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.util.GroupTask;
-import com.android.quickstep.util.TISBindHelper;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
 import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
@@ -245,7 +244,8 @@
         }
 
         if (!ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()
-                && mControllers.taskbarDesktopModeController.getAreDesktopTasksVisible()) {
+                && mControllers.taskbarDesktopModeController
+                    .getAreDesktopTasksVisibleAndNotInOverview()) {
             // TODO: b/333533253 - Remove after flag rollout
             isVisible = false;
         }
@@ -485,12 +485,6 @@
         mTaskbarLauncherStateController.resetIconAlignment();
     }
 
-    @Nullable
-    @Override
-    protected TISBindHelper getTISBindHelper() {
-        return mLauncher.getTISBindHelper();
-    }
-
     @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 933eb96..060173a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -189,6 +189,7 @@
     private final TaskbarControllers mControllers;
 
     private final WindowManager mWindowManager;
+    private final boolean mIsPrimaryDisplay;
     private DeviceProfile mDeviceProfile;
     private WindowManager.LayoutParams mWindowLayoutParams;
     private WindowManager.LayoutParams mLastUpdatedLayoutParams;
@@ -234,11 +235,12 @@
 
     public TaskbarActivityContext(Context windowContext,
             @Nullable Context navigationBarPanelContext, DeviceProfile launcherDp,
-            TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider
-            unfoldTransitionProgressProvider,
-            @NonNull DesktopVisibilityController desktopVisibilityController) {
+            TaskbarNavButtonController buttonController,
+            ScopedUnfoldTransitionProgressProvider unfoldTransitionProgressProvider,
+            @NonNull DesktopVisibilityController desktopVisibilityController,
+            boolean isPrimaryDisplay) {
         super(windowContext);
-
+        mIsPrimaryDisplay = isPrimaryDisplay;
         mNavigationBarPanelContext = navigationBarPanelContext;
         applyDeviceProfile(launcherDp);
         final Resources resources = getResources();
@@ -627,7 +629,8 @@
      */
     private WindowManager.LayoutParams createAllWindowParams() {
         final int windowType =
-                ENABLE_TASKBAR_NAVBAR_UNIFICATION ? TYPE_NAVIGATION_BAR : TYPE_NAVIGATION_BAR_PANEL;
+                (ENABLE_TASKBAR_NAVBAR_UNIFICATION && mIsPrimaryDisplay) ? TYPE_NAVIGATION_BAR
+                        : TYPE_NAVIGATION_BAR_PANEL;
         WindowManager.LayoutParams windowLayoutParams =
                 createDefaultWindowLayoutParams(windowType, TaskbarActivityContext.WINDOW_TITLE);
 
@@ -1297,7 +1300,18 @@
             RemoteTransition remoteTransition =
                     (areDesktopTasksVisible() && canUnminimizeDesktopTask(groupTask.task1.key.id))
                             ? createUnminimizeRemoteTransition() : null;
-            handleGroupTaskLaunch(groupTask, remoteTransition, areDesktopTasksVisible());
+            if (areDesktopTasksVisible() && mControllers.uiController.isInOverviewUi()) {
+                RunnableList runnableList = recents.launchRunningDesktopTaskView();
+                // Wrapping it in runnable so we post after DW is ready for the app
+                // launch.
+                if (runnableList != null) {
+                    runnableList.add(() -> UI_HELPER_EXECUTOR.execute(
+                            () -> handleGroupTaskLaunch(groupTask, remoteTransition,
+                                    areDesktopTasksVisible())));
+                }
+            } else {
+                handleGroupTaskLaunch(groupTask, remoteTransition, areDesktopTasksVisible());
+            }
             mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
         } else if (tag instanceof FolderInfo) {
             // Tapping an expandable folder icon on Taskbar
@@ -1317,9 +1331,29 @@
         } else if (tag instanceof TaskItemInfo info) {
             RemoteTransition remoteTransition = canUnminimizeDesktopTask(info.getTaskId())
                     ? createUnminimizeRemoteTransition() : null;
-            UI_HELPER_EXECUTOR.execute(() ->
-                    SystemUiProxy.INSTANCE.get(this).showDesktopApp(
-                            info.getTaskId(), remoteTransition));
+
+            TaskView taskView = null;
+            if (recents != null) {
+                taskView = recents.getTaskViewByTaskId(info.getTaskId());
+            }
+
+            if (areDesktopTasksVisible() && taskView != null) {
+                RunnableList runnableList = taskView.launchWithAnimation();
+                if (runnableList != null) {
+                    runnableList.add(() ->
+                            // wrapped it in runnable here since we need the post for DW to be
+                            // ready. if we don't other DW will be gone and only the launched
+                            // task will show.
+                            UI_HELPER_EXECUTOR.execute(() ->
+                                    SystemUiProxy.INSTANCE.get(this).showDesktopApp(
+                                            info.getTaskId(), remoteTransition)));
+                }
+            } else {
+                UI_HELPER_EXECUTOR.execute(() ->
+                        SystemUiProxy.INSTANCE.get(this).showDesktopApp(
+                                info.getTaskId(), remoteTransition));
+            }
+
             mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(
                     /* stash= */ true);
         } else if (tag instanceof WorkspaceItemInfo) {
@@ -1564,7 +1598,18 @@
                                                 .launchAppPair((AppPairIcon) launchingIconView,
                                                         -1 /*cuj*/)));
                     } else {
-                        startItemInfoActivity(itemInfos.get(0), foundTask);
+                        if (areDesktopTasksVisible()
+                                && mControllers.uiController.isInOverviewUi()) {
+                            RunnableList runnableList = recents.launchRunningDesktopTaskView();
+                            // Wrapping it in runnable so we post after DW is ready for the app
+                            // launch.
+                            if (runnableList != null) {
+                                runnableList.add(() -> UI_HELPER_EXECUTOR.execute(
+                                        () -> startItemInfoActivity(itemInfos.get(0), foundTask)));
+                            }
+                        } else {
+                            startItemInfoActivity(itemInfos.get(0), foundTask);
+                        }
                     }
                 }
         );
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index 826722d..af60f10 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -194,7 +194,7 @@
                 voiceInteractionWindowController
         };
 
-        if (taskbarDesktopModeController.getAreDesktopTasksVisible()) {
+        if (taskbarDesktopModeController.getAreDesktopTasksVisibleAndNotInOverview()) {
             mCornerRoundness.value = taskbarDesktopModeController.getTaskbarCornerRoundness(
                     mSharedState.showCornerRadiusInDesktopMode);
         } else {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt
index 47a35c5..a7c7381 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt
@@ -27,6 +27,9 @@
     private lateinit var taskbarControllers: TaskbarControllers
     private lateinit var taskbarSharedState: TaskbarSharedState
 
+    val areDesktopTasksVisibleAndNotInOverview: Boolean
+        get() = desktopVisibilityController.areDesktopTasksVisibleAndNotInOverview()
+
     val areDesktopTasksVisible: Boolean
         get() = desktopVisibilityController.areDesktopTasksVisible()
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index fc307b2..a9e8d6d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -344,7 +344,8 @@
         // TODO(297921594) clean it up when taskbar to desktop drag is implemented.
         // Pre-drag has ended, start the global system drag.
         if (mDisallowGlobalDrag
-                || mControllers.taskbarDesktopModeController.getAreDesktopTasksVisible()) {
+                || mControllers.taskbarDesktopModeController
+                    .getAreDesktopTasksVisibleAndNotInOverview()) {
             AbstractFloatingView.closeAllOpenViewsExcept(mActivity, TYPE_TASKBAR_ALL_APPS);
             return;
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 250e33a..1905561 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -635,7 +635,7 @@
 
         float cornerRoundness = isInLauncher ? 0 : 1;
 
-        if (mControllers.taskbarDesktopModeController.getAreDesktopTasksVisible()
+        if (mControllers.taskbarDesktopModeController.getAreDesktopTasksVisibleAndNotInOverview()
                 && mControllers.getSharedState() != null) {
             cornerRoundness =
                     mControllers.taskbarDesktopModeController.getTaskbarCornerRoundness(
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 9407e73..13f9a51 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -47,6 +47,7 @@
 import android.provider.Settings;
 import android.util.Log;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.view.Display;
 import android.view.MotionEvent;
 import android.view.WindowManager;
@@ -116,7 +117,6 @@
     private final Context mWindowContext;
     private final @Nullable Context mNavigationBarPanelContext;
     private WindowManager mWindowManager;
-    private boolean mAddedWindow;
     private final TaskbarNavButtonController mDefaultNavButtonController;
     private final ComponentCallbacks mDefaultComponentCallbacks;
 
@@ -133,6 +133,8 @@
     private final SparseArray<TaskbarActivityContext> mTaskbars = new SparseArray<>();
     /** DisplayId - {@link FrameLayout} map for Connected Display. */
     private final SparseArray<FrameLayout> mRootLayouts = new SparseArray<>();
+    /** DisplayId - {@link Boolean} map indicating if RootLayout was added to window. */
+    private final SparseBooleanArray mAddedRootLayouts = new SparseBooleanArray();
     private StatefulActivity mActivity;
     private RecentsViewContainer mRecentsViewContainer;
 
@@ -590,9 +592,12 @@
         }
     }
 
-    public void setWallpaperVisible(boolean isVisible) {
+    /**
+     * Sets wallpaper visibility for specific display.
+     */
+    public void setWallpaperVisible(int displayId, boolean isVisible) {
         mSharedState.wallpaperVisible = isVisible;
-        TaskbarActivityContext taskbar = getTaskbarForDisplay(getDefaultDisplayId());
+        TaskbarActivityContext taskbar = getTaskbarForDisplay(displayId);
         if (taskbar != null) {
             taskbar.setWallpaperVisible(isVisible);
         }
@@ -742,23 +747,42 @@
 
     private void addTaskbarRootViewToWindow(int displayId) {
         TaskbarActivityContext taskbar = getTaskbarForDisplay(displayId);
-        if (enableTaskbarNoRecreate() && !mAddedWindow && taskbar != null) {
+        if (!enableTaskbarNoRecreate() || taskbar == null) {
+            return;
+        }
+
+        if (!isTaskbarRootLayoutAddedForDisplay(displayId)) {
             mWindowManager.addView(getTaskbarRootLayoutForDisplay(displayId),
                     taskbar.getWindowLayoutParams());
-            mAddedWindow = true;
+            mAddedRootLayouts.put(displayId, true);
         }
     }
 
     private void removeTaskbarRootViewFromWindow(int displayId) {
         FrameLayout rootLayout = getTaskbarRootLayoutForDisplay(displayId);
-        if (enableTaskbarNoRecreate() && mAddedWindow && rootLayout != null) {
+        if (!enableTaskbarNoRecreate() || rootLayout == null) {
+            return;
+        }
+
+        if (isTaskbarRootLayoutAddedForDisplay(displayId)) {
             mWindowManager.removeViewImmediate(rootLayout);
-            mAddedWindow = false;
+            mAddedRootLayouts.put(displayId, false);
             removeTaskbarRootLayoutFromMap(displayId);
         }
     }
 
     /**
+     * Retrieves whether RootLayout was added to window for specific display, or false if no
+     * such mapping has been made.
+     *
+     * @param displayId The ID of the display for which to retrieve the taskbar root layout.
+     * @return if RootLayout was added to window {@link Boolean} for a display or {@code false}.
+     */
+    private boolean isTaskbarRootLayoutAddedForDisplay(int displayId) {
+        return mAddedRootLayouts.get(displayId);
+    }
+
+    /**
      * Returns the {@link TaskbarActivityContext} associated with the given display ID.
      *
      * @param displayId The ID of the display to retrieve the taskbar for.
@@ -776,7 +800,7 @@
     private TaskbarActivityContext createTaskbarActivityContext(DeviceProfile dp, int displayId) {
         TaskbarActivityContext newTaskbar = new TaskbarActivityContext(mWindowContext,
                 mNavigationBarPanelContext, dp, mDefaultNavButtonController,
-                mUnfoldProgressProvider, mDesktopVisibilityController);
+                mUnfoldProgressProvider, mDesktopVisibilityController, isDefaultDisplay(displayId));
 
         addTaskbarToMap(displayId, newTaskbar);
         return newTaskbar;
@@ -824,6 +848,10 @@
         addTaskbarRootLayoutToMap(displayId, newTaskbarRootLayout);
     }
 
+    private boolean isDefaultDisplay(int displayId) {
+        return displayId == getDefaultDisplayId();
+    }
+
     /**
      * Retrieves the root layout of the taskbar for the specified display.
      *
@@ -853,6 +881,7 @@
      */
     private void removeTaskbarRootLayoutFromMap(int displayId) {
         if (mRootLayouts.contains(displayId)) {
+            mAddedRootLayouts.delete(displayId);
             mRootLayouts.delete(displayId);
         }
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 4881836..d1f9be0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -344,6 +344,10 @@
         mCallbacks.onToggleOverview();
     }
 
+    public void hideOverview() {
+        mCallbacks.onHideOverview();
+    }
+
     void sendBackKeyEvent(int action, boolean cancelled) {
         if (action == mLastSentBackAction) {
             // There must always be an alternating sequence of ACTION_DOWN and ACTION_UP events
@@ -411,5 +415,8 @@
 
         /** Callback invoked when the overview button is pressed. */
         default void onToggleOverview() {}
+
+        /** Callback invoken when a visible overview needs to be hidden. */
+        default void onHideOverview() { }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt
index bcfc718..23c5070 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt
@@ -57,7 +57,10 @@
                     return
                 }
                 val shouldPinTaskbar =
-                    if (controllers.taskbarDesktopModeController.areDesktopTasksVisible) {
+                    if (
+                        controllers.taskbarDesktopModeController
+                            .areDesktopTasksVisibleAndNotInOverview
+                    ) {
                         !launcherPrefs.get(TASKBAR_PINNING_IN_DESKTOP_MODE)
                     } else {
                         !launcherPrefs.get(TASKBAR_PINNING)
@@ -137,7 +140,7 @@
     @VisibleForTesting
     fun recreateTaskbarAndUpdatePinningValue() {
         updateIsAnimatingTaskbarPinningAndNotifyTaskbarDragLayer(false)
-        if (controllers.taskbarDesktopModeController.areDesktopTasksVisible) {
+        if (controllers.taskbarDesktopModeController.areDesktopTasksVisibleAndNotInOverview) {
             launcherPrefs.put(
                 TASKBAR_PINNING_IN_DESKTOP_MODE,
                 !launcherPrefs.get(TASKBAR_PINNING_IN_DESKTOP_MODE),
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 6789824..e704691 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -215,7 +215,8 @@
             shortcuts.add(PIN_UNPIN_ITEM);
         }
         shortcuts.add(APP_INFO);
-        if (!mControllers.taskbarDesktopModeController.getAreDesktopTasksVisible()) {
+        if (!mControllers.taskbarDesktopModeController
+                .getAreDesktopTasksVisibleAndNotInOverview()) {
             shortcuts.addAll(mControllers.uiController.getSplitMenuOptions().toList());
         }
         if (com.android.wm.shell.Flags.enableBubbleAnything()) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 67be8da..502c001 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -84,8 +84,6 @@
     private static final String TAG = "TaskbarStashController";
     private static final boolean DEBUG = false;
 
-    private static boolean sEnableSoftwareImeForTests = false;
-
     /**
      * Def. value for @param shouldBubblesFollow in
      * {@link #updateAndAnimateTransientTaskbar(boolean)} */
@@ -1172,15 +1170,16 @@
         }
 
         // Do not stash if pinned taskbar, hardware keyboard is attached and no IME is docked
-        if (isHardwareKeyboard() && DisplayController.isPinnedTaskbar(mActivity)
+        if (mActivity.isHardwareKeyboard() && DisplayController.isPinnedTaskbar(mActivity)
                 && !mActivity.isImeDocked()) {
             return false;
         }
 
         // Do not stash if hardware keyboard is attached, in 3 button nav and desktop windowing mode
-        if (isHardwareKeyboard()
+        if (mActivity.isHardwareKeyboard()
                 && mActivity.isThreeButtonNav()
-                && mControllers.taskbarDesktopModeController.getAreDesktopTasksVisible()) {
+                && mControllers.taskbarDesktopModeController
+                    .getAreDesktopTasksVisibleAndNotInOverview()) {
             return false;
         }
 
@@ -1192,21 +1191,6 @@
         return mIsImeShowing || mIsImeSwitcherShowing;
     }
 
-    private boolean isHardwareKeyboard() {
-        return mActivity.isHardwareKeyboard() && !sEnableSoftwareImeForTests;
-    }
-
-    /**
-     * Overrides {@link #isHardwareKeyboard()} to {@code false} for testing, if enabled.
-     * <p>
-     * Virtual devices are sometimes in hardware keyboard mode, leading to an inconsistent
-     * testing environment.
-     */
-    @VisibleForTesting
-    static void enableSoftwareImeForTests(boolean enable) {
-        sEnableSoftwareImeForTests = enable;
-    }
-
     /**
      * Updates the proper flag to indicate whether the task bar should be stashed.
      *
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 8b636dd..f29f95d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -39,10 +39,7 @@
 import com.android.launcher3.taskbar.bubbles.BubbleBarController;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.SplitConfigurationOptions;
-import com.android.quickstep.OverviewCommandHelper;
-import com.android.quickstep.OverviewCommandHelper.CommandType;
 import com.android.quickstep.util.GroupTask;
-import com.android.quickstep.util.TISBindHelper;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskContainer;
 import com.android.quickstep.views.TaskView;
@@ -389,26 +386,13 @@
     /** Adjusts the hotseat for the bubble bar. */
     public void adjustHotseatForBubbleBar(boolean isBubbleBarVisible) {}
 
-    @Nullable
-    protected TISBindHelper getTISBindHelper() {
-        return null;
-    }
-
     /**
      * Launches the focused task in the Keyboard Quick Switch view through the OverviewCommandHelper
      * <p>
      * Use this helper method when the focused task may be the overview task.
      */
     public void launchKeyboardFocusedTask() {
-        TISBindHelper tisBindHelper = getTISBindHelper();
-        if (tisBindHelper == null) {
-            return;
-        }
-        OverviewCommandHelper overviewCommandHelper = tisBindHelper.getOverviewCommandHelper();
-        if (overviewCommandHelper == null) {
-            return;
-        }
-        overviewCommandHelper.addCommand(CommandType.HIDE);
+        mControllers.navButtonController.hideOverview();
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 438478f..60de066 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -84,6 +84,7 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.LauncherBindableItemsContainer;
+import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
 import com.android.launcher3.util.MultiPropertyFactory;
 import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
 import com.android.launcher3.util.MultiTranslateDelegate;
@@ -130,8 +131,6 @@
     private static final int TRANSITION_FADE_IN_DURATION = 167;
     private static final int TRANSITION_FADE_OUT_DURATION = 83;
 
-    private static boolean sEnableModelLoadingForTests = true;
-
     private final TaskbarActivityContext mActivity;
     private final TaskbarView mTaskbarView;
     private final MultiValueAlpha mTaskbarIconAlpha;
@@ -243,7 +242,8 @@
         mTaskbarIconTranslationXForPinning.updateValue(pinningValue);
 
         mModelCallbacks.init(controllers);
-        if (mActivity.isUserSetupComplete() && sEnableModelLoadingForTests) {
+        if (mActivity.isUserSetupComplete()
+                && !(mActivity.getApplicationContext() instanceof SandboxContext)) {
             // Only load the callbacks if user setup is completed
             controllers.runAfterInit(() -> LauncherAppState.getInstance(mActivity).getModel()
                     .addCallbacksAndLoad(mModelCallbacks));
@@ -1215,12 +1215,6 @@
         mModelCallbacks.dumpLogs(prefix + "\t", pw);
     }
 
-    /** Enables model loading for tests. */
-    @VisibleForTesting
-    public static void enableModelLoadingForTests(boolean enable) {
-        sEnableModelLoadingForTests = enable;
-    }
-
     private ObjectAnimator createTaskbarIconsShiftAnimator(float translationX) {
         ObjectAnimator animator = mIconsTranslationXForNavbar.animateToValue(translationX);
         animator.setStartDelay(FADE_OUT_ANIM_POSITION_DURATION_MS);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 9683f8b..db5e0d5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -345,7 +345,8 @@
 
         if (Flags.enableOptionalBubbleOverflow()
                 && update.showOverflowChanged && !update.showOverflow && update.addedBubble != null
-                && update.removedBubbles.isEmpty()) {
+                && update.removedBubbles.isEmpty()
+                && !mBubbles.isEmpty()) {
             // A bubble was added from the overflow (& now it's empty / not showing)
             mBubbles.put(update.addedBubble.getKey(), update.addedBubble);
             mBubbleBarViewController.removeOverflowAndAddBubble(update.addedBubble);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 37c6194..0d0feff 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -729,7 +729,8 @@
             Runnable onEndRunnable) {
         FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams((int) mIconSize, (int) mIconSize,
                 Gravity.LEFT);
-        boolean isOverflowSelected = mSelectedBubbleView.isOverflow();
+        boolean isOverflowSelected =
+                mSelectedBubbleView != null && mSelectedBubbleView.isOverflow();
         boolean removingOverflow = removedBubble.isOverflow();
         boolean addingOverflow = addedBubble.isOverflow();
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt
index a8002a5..3245fd1e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt
@@ -18,6 +18,7 @@
 package com.android.launcher3.taskbar.bubbles
 
 import com.android.launcher3.R
+import com.android.wm.shell.shared.R as SharedR
 import com.android.wm.shell.shared.bubbles.DismissView
 
 /**
@@ -36,8 +37,8 @@
             bottomMarginResId = R.dimen.bubblebar_dismiss_target_bottom_margin,
             floatingGradientHeightResId = R.dimen.bubblebar_dismiss_floating_gradient_height,
             floatingGradientColorResId = android.R.color.system_neutral1_900,
-            backgroundResId = R.drawable.bg_bubble_dismiss_circle,
-            iconResId = R.drawable.ic_bubble_dismiss_white
+            backgroundResId = SharedR.drawable.floating_dismiss_background,
+            iconResId = SharedR.drawable.floating_dismiss_ic_close,
         )
     )
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
index 3bff58b..745c689 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
@@ -531,7 +531,7 @@
         bubbleStashController.getStashedHandlePhysicsAnimator().cancelIfRunning()
         resetBubbleBarPropertiesOnInterrupt()
         bubbleStashController.onNewBubbleAnimationInterrupted(
-            /* isStashed= */ bubbleBarView.alpha == 0f,
+            /* isStashed= */ bubbleStashController.isStashed,
             bubbleBarView.translationY,
         )
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index e17771c..6d744c2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -169,6 +169,7 @@
 import com.android.launcher3.widget.LauncherWidgetHolder;
 import com.android.quickstep.OverviewCommandHelper;
 import com.android.quickstep.OverviewComponentObserver;
+import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
 import com.android.quickstep.RecentsAnimationDeviceState;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.SystemUiProxy;
@@ -224,7 +225,6 @@
             SystemProperties.getBoolean("persist.debug.trace_layouts", false);
     private static final String TRACE_RELAYOUT_CLASS =
             SystemProperties.get("persist.debug.trace_request_layout_class", null);
-    public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false;
 
     protected static final String RING_APPEAR_ANIMATION_PREFIX = "RingAppearAnimation\t";
 
@@ -264,7 +264,7 @@
 
     private boolean mIsOverlayVisible;
 
-    private final Runnable mOverviewTargetChangeRunnable = this::onOverviewTargetChanged;
+    private final OverviewChangeListener mOverviewChangeListener = this::onOverviewTargetChanged;
 
     public static QuickstepLauncher getLauncher(Context context) {
         return fromContext(context);
@@ -283,9 +283,6 @@
                         systemUiProxy, RecentsModel.INSTANCE.get(this),
                         () -> onStateBack());
         RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(asContext());
-        // TODO(b/337863494): Explore use of the same OverviewComponentObserver across launcher
-        OverviewComponentObserver overviewComponentObserver = new OverviewComponentObserver(
-                asContext(), deviceState);
         if (DesktopModeStatus.canEnterDesktopMode(this)) {
             mDesktopRecentsTransitionController = new DesktopRecentsTransitionController(
                     getStateManager(), systemUiProxy, getIApplicationThread(),
@@ -294,7 +291,7 @@
         overviewPanel.init(mActionsView, mSplitSelectStateController,
                 mDesktopRecentsTransitionController);
         mSplitWithKeyboardShortcutController = new SplitWithKeyboardShortcutController(this,
-                mSplitSelectStateController, overviewComponentObserver, deviceState);
+                mSplitSelectStateController, deviceState);
         mSplitToWorkspaceController = new SplitToWorkspaceController(this,
                 mSplitSelectStateController);
         mActionsView.updateDimension(getDeviceProfile(), overviewPanel.getLastComputedTaskSize());
@@ -307,8 +304,7 @@
         mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
         mDepthController = new DepthController(this);
         if (DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(this)) {
-            mSplitSelectStateController.initSplitFromDesktopController(this,
-                    overviewComponentObserver);
+            mSplitSelectStateController.initSplitFromDesktopController(this);
         }
         mHotseatPredictionController = new HotseatPredictionController(this);
 
@@ -556,10 +552,8 @@
             mUnfoldTransitionProgressProvider.destroy();
         }
 
-        TISBinder binder = mTISBindHelper.getBinder();
-        if (binder != null) {
-            binder.unregisterOverviewTargetChangeListener(mOverviewTargetChangeRunnable);
-        }
+        OverviewComponentObserver.INSTANCE.get(this)
+                .removeOverviewChangeListener(mOverviewChangeListener);
         mTISBindHelper.onDestroy();
 
         if (mLauncherUnfoldAnimationController != null) {
@@ -701,6 +695,8 @@
         QuickstepOnboardingPrefs.setup(this);
         View.setTraceLayoutSteps(TRACE_LAYOUTS);
         View.setTracedRequestLayoutClassClass(TRACE_RELAYOUT_CLASS);
+        OverviewComponentObserver.INSTANCE.get(this)
+                .addOverviewChangeListener(mOverviewChangeListener);
     }
 
     @Override
@@ -1025,7 +1021,7 @@
         DesktopVisibilityController desktopVisibilityController = getDesktopVisibilityController();
         if (!ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()
                 && desktopVisibilityController != null
-                && desktopVisibilityController.areDesktopTasksVisible()
+                && desktopVisibilityController.areDesktopTasksVisibleAndNotInOverview()
                 && !desktopVisibilityController.isRecentsGestureInProgress()) {
             // Return early to skip setting activity to appear as resumed
             // TODO: b/333533253 - Remove after flag rollout
@@ -1051,7 +1047,7 @@
         }
     }
 
-    private void onOverviewTargetChanged() {
+    private void onOverviewTargetChanged(boolean isHomeAndOverviewSame) {
         QuickstepTransitionManager transitionManager = getAppTransitionManager();
         if (transitionManager != null) {
             transitionManager.onOverviewTargetChange();
@@ -1064,7 +1060,6 @@
             taskbarManager.setActivity(this);
         }
         mTISBindHelper.setPredictiveBackToHomeInProgress(mIsPredictiveBackToHomeInProgress);
-        binder.registerOverviewTargetChangeListener(mOverviewTargetChangeRunnable);
     }
 
     @Override
@@ -1354,7 +1349,7 @@
     public boolean areDesktopTasksVisible() {
         DesktopVisibilityController desktopVisibilityController = getDesktopVisibilityController();
         if (desktopVisibilityController != null) {
-            return desktopVisibilityController.areDesktopTasksVisible();
+            return desktopVisibilityController.areDesktopTasksVisibleAndNotInOverview();
         }
         return false;
     }
@@ -1434,12 +1429,6 @@
         return (mTaskbarUIController != null && mTaskbarUIController.hasBubbles());
     }
 
-    @NonNull
-    @Override
-    public TISBindHelper getTISBindHelper() {
-        return mTISBindHelper;
-    }
-
     @Override
     public boolean handleIncorrectSplitTargetSelection() {
         if (!enableSplitContextually() || !mSplitSelectStateController.isSplitSelectActive()) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index d387794..dae63af 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -102,7 +102,7 @@
 
     @Override
     public int getTitle() {
-        return R.string.all_apps_label;
+        return R.string.all_apps_list_label;
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 3740a68..03394ef 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -131,7 +131,6 @@
 import com.android.launcher3.util.WindowBounds;
 import com.android.quickstep.GestureState.GestureEndTarget;
 import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
-import com.android.quickstep.fallback.window.RecentsWindowFactory;
 import com.android.quickstep.util.ActiveGestureErrorDetector;
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.ActiveGestureProtoLogProxy;
@@ -310,7 +309,6 @@
     private static final int LOG_NO_OP_PAGE_INDEX = -1;
 
     protected final TaskAnimationManager mTaskAnimationManager;
-    protected final RecentsWindowFactory mRecentsWindowFactory;
     // Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
     private RunningWindowAnim[] mRunningWindowAnim;
     // Possible second animation running at the same time as mRunningWindowAnim
@@ -376,7 +374,7 @@
     public AbsSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
             TaskAnimationManager taskAnimationManager, GestureState gestureState,
             long touchTimeMs, boolean continuingLastGesture,
-            InputConsumerController inputConsumer, RecentsWindowFactory recentsWindowFactory,
+            InputConsumerController inputConsumer,
             MSDLPlayerWrapper msdlPlayerWrapper) {
         super(context, deviceState, gestureState);
         mContainerInterface = gestureState.getContainerInterface();
@@ -393,7 +391,6 @@
                     endLauncherTransitionController();
                 }, new InputProxyHandlerFactory(mContainerInterface, mGestureState));
         mTaskAnimationManager = taskAnimationManager;
-        mRecentsWindowFactory = recentsWindowFactory;
         mTouchTimeMs = touchTimeMs;
         mContinuingLastGesture = continuingLastGesture;
 
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 1e755eb..1437a6e 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -87,7 +87,7 @@
             // We were on our way to this state when we got canceled, end there instead.
             startState = stateFromGestureEndTarget(endTarget);
             DesktopVisibilityController controller = getDesktopVisibilityController();
-            if (controller != null && controller.areDesktopTasksVisible()
+            if (controller != null && controller.areDesktopTasksVisibleAndNotInOverview()
                     && endTarget == LAST_TASK) {
                 // When we are cancelling the transition and going back to last task, move to
                 // rest state instead when desktop tasks are visible.
diff --git a/quickstep/src/com/android/quickstep/BaseContainerInterface.java b/quickstep/src/com/android/quickstep/BaseContainerInterface.java
index 2164bc2..2171c47 100644
--- a/quickstep/src/com/android/quickstep/BaseContainerInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseContainerInterface.java
@@ -242,7 +242,7 @@
             // We were on our way to this state when we got canceled, end there instead.
             startState = stateFromGestureEndTarget(endTarget);
             DesktopVisibilityController controller = getDesktopVisibilityController();
-            if (controller != null && controller.areDesktopTasksVisible()
+            if (controller != null && controller.areDesktopTasksVisibleAndNotInOverview()
                     && endTarget == LAST_TASK) {
                 // When we are cancelling the transition and going back to last task, move to
                 // rest state instead when desktop tasks are visible.
diff --git a/quickstep/src/com/android/quickstep/DisplayModel.kt b/quickstep/src/com/android/quickstep/DisplayModel.kt
new file mode 100644
index 0000000..cbc2f7d
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/DisplayModel.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 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.quickstep
+
+import android.content.Context
+import android.hardware.display.DisplayManager
+import android.util.Log
+import android.util.SparseArray
+import androidx.core.util.valueIterator
+import com.android.quickstep.DisplayModel.DisplayResource
+
+/** data model for managing resources with lifecycles that match that of the connected display */
+abstract class DisplayModel<RESOURCE_TYPE : DisplayResource>(val context: Context) {
+
+    companion object {
+        private const val TAG = "DisplayViewModel"
+        private const val DEBUG = false
+    }
+
+    protected val displayManager =
+        context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
+    protected val displayResourceArray = SparseArray<RESOURCE_TYPE>()
+
+    abstract fun createDisplayResource(displayId: Int)
+
+    protected val displayListener: DisplayManager.DisplayListener =
+        (object : DisplayManager.DisplayListener {
+            override fun onDisplayAdded(displayId: Int) {
+                if (DEBUG) Log.d(TAG, "onDisplayAdded: displayId=$displayId")
+                createDisplayResource(displayId)
+            }
+
+            override fun onDisplayRemoved(displayId: Int) {
+                if (DEBUG) Log.d(TAG, "onDisplayRemoved: displayId=$displayId")
+                deleteDisplayResource(displayId)
+            }
+
+            override fun onDisplayChanged(displayId: Int) {
+                if (DEBUG) Log.d(TAG, "onDisplayChanged: displayId=$displayId")
+            }
+        })
+
+    fun destroy() {
+        displayResourceArray.valueIterator().forEach { displayResource ->
+            displayResource.cleanup()
+        }
+        displayResourceArray.clear()
+        displayManager.unregisterDisplayListener(displayListener)
+    }
+
+    fun getDisplayResource(displayId: Int): RESOURCE_TYPE? {
+        if (DEBUG) Log.d(TAG, "get: displayId=$displayId")
+        return displayResourceArray[displayId]
+    }
+
+    fun deleteDisplayResource(displayId: Int) {
+        if (DEBUG) Log.d(TAG, "delete: displayId=$displayId")
+        getDisplayResource(displayId)?.cleanup()
+        displayResourceArray.remove(displayId)
+    }
+
+    abstract class DisplayResource() {
+        abstract fun cleanup()
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index b0c69cf..1e857ca 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -106,7 +106,7 @@
             boolean continuingLastGesture, InputConsumerController inputConsumer,
             MSDLPlayerWrapper msdlPlayerWrapper) {
         super(context, deviceState, taskAnimationManager, gestureState, touchTimeMs,
-                continuingLastGesture, inputConsumer, null, msdlPlayerWrapper);
+                continuingLastGesture, inputConsumer, msdlPlayerWrapper);
 
         mRunningOverHome = mGestureState.getRunningTask() != null
                 && mGestureState.getRunningTask().isHomeTask();
diff --git a/quickstep/src/com/android/quickstep/FallbackWindowInterface.java b/quickstep/src/com/android/quickstep/FallbackWindowInterface.java
index a202ebd..f7836b0 100644
--- a/quickstep/src/com/android/quickstep/FallbackWindowInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackWindowInterface.java
@@ -41,8 +41,6 @@
 import com.android.quickstep.util.ContextInitListener;
 import com.android.quickstep.views.RecentsView;
 
-import java.util.HashMap;
-import java.util.Map;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
@@ -53,35 +51,13 @@
  */
 public final class FallbackWindowInterface extends BaseWindowInterface{
 
-    static Map<Integer, FallbackWindowInterface> sWindowInterfaceMap = new HashMap<>();
-
     private final RecentsWindowManager mRecentsWindowManager;
-    /**
-     * This is only null before init() or after destroy()
-     */
-    @Nullable
-    public static FallbackWindowInterface getInstance(int displayId) {
-        return sWindowInterfaceMap.get(displayId);
-    }
 
-    /**
-     * initializing instance and mapping it to display id
-     */
-    public static void init(int displayId, RecentsWindowManager recentsWindowManager) {
-        if (!sWindowInterfaceMap.containsKey(displayId)) {
-            sWindowInterfaceMap.put(displayId, new FallbackWindowInterface(recentsWindowManager));
-        }
-    }
-
-    private FallbackWindowInterface(RecentsWindowManager recentsWindowManager) {
+    public FallbackWindowInterface(RecentsWindowManager recentsWindowManager) {
         super(DEFAULT, BACKGROUND_APP);
         mRecentsWindowManager = recentsWindowManager;
     }
 
-    public void destroy() {
-        sWindowInterfaceMap.clear();
-    }
-
     /** 2 */
     @Override
     public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect,
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 7dd2f2e..243a577 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -71,7 +71,7 @@
             boolean continuingLastGesture, InputConsumerController inputConsumer,
             MSDLPlayerWrapper msdlPlayerWrapper) {
         super(context, deviceState, taskAnimationManager, gestureState, touchTimeMs,
-                continuingLastGesture, inputConsumer, null, msdlPlayerWrapper);
+                continuingLastGesture, inputConsumer, msdlPlayerWrapper);
     }
 
 
@@ -106,7 +106,8 @@
                 && workspaceView.isAttachedToWindow()
                 && workspaceView.getHeight() > 0
                 && (mContainer.getDesktopVisibilityController() == null
-                || !mContainer.getDesktopVisibilityController().areDesktopTasksVisible());
+                || !mContainer.getDesktopVisibilityController()
+                    .areDesktopTasksVisibleAndNotInOverview());
 
         mContainer.getRootView().setForceHideBackArrow(true);
 
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index bac5e64..64c1129 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -19,6 +19,7 @@
 import static android.content.Intent.ACTION_PACKAGE_ADDED;
 import static android.content.Intent.ACTION_PACKAGE_CHANGED;
 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -41,22 +42,35 @@
 
 import com.android.launcher3.Flags;
 import com.android.launcher3.R;
+import com.android.launcher3.dagger.ApplicationContext;
+import com.android.launcher3.dagger.LauncherAppComponent;
+import com.android.launcher3.dagger.LauncherAppSingleton;
+import com.android.launcher3.util.DaggerSingletonObject;
+import com.android.launcher3.util.DaggerSingletonTracker;
 import com.android.launcher3.util.SimpleBroadcastReceiver;
+import com.android.quickstep.fallback.window.RecentsDisplayModel;
 import com.android.quickstep.util.ActiveGestureProtoLogProxy;
 import com.android.systemui.shared.system.PackageManagerWrapper;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
-import java.util.function.Consumer;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.inject.Inject;
 
 /**
  * Class to keep track of the current overview component based off user preferences and app updates
  * and provide callers the relevant classes.
  */
+@LauncherAppSingleton
 public final class OverviewComponentObserver {
     private static final String TAG = "OverviewComponentObserver";
 
+    public static final DaggerSingletonObject<OverviewComponentObserver> INSTANCE =
+            new DaggerSingletonObject<>(LauncherAppComponent::getOverviewComponentObserver);
+
     // We register broadcast receivers on main thread to avoid missing updates.
     private final SimpleBroadcastReceiver mUserPreferenceChangeReceiver =
             new SimpleBroadcastReceiver(MAIN_EXECUTOR, this::updateOverviewTargets);
@@ -64,14 +78,16 @@
             new SimpleBroadcastReceiver(MAIN_EXECUTOR, this::updateOverviewTargets);
 
     private final Context mContext;
-    private final RecentsAnimationDeviceState mDeviceState;
+    private final RecentsDisplayModel mRecentsDisplayModel;
+
     private final Intent mCurrentHomeIntent;
     private final Intent mMyHomeIntent;
     private final Intent mFallbackIntent;
     private final SparseIntArray mConfigChangesMap = new SparseIntArray();
     private final String mSetupWizardPkg;
 
-    private Consumer<Boolean> mOverviewChangeListener = b -> { };
+    private final List<OverviewChangeListener> mOverviewChangeListeners =
+            new CopyOnWriteArrayList<>();
 
     private String mUpdateRegisteredPackage;
     private BaseContainerInterface mContainerInterface;
@@ -80,10 +96,13 @@
     private boolean mIsDefaultHome;
     private boolean mIsHomeDisabled;
 
-
-    public OverviewComponentObserver(Context context, RecentsAnimationDeviceState deviceState) {
+    @Inject
+    public OverviewComponentObserver(
+            @ApplicationContext Context context,
+            RecentsDisplayModel recentsDisplayModel,
+            DaggerSingletonTracker lifecycleTracker) {
         mContext = context;
-        mDeviceState = deviceState;
+        mRecentsDisplayModel = recentsDisplayModel;
         mCurrentHomeIntent = createHomeIntent();
         mMyHomeIntent = new Intent(mCurrentHomeIntent).setPackage(mContext.getPackageName());
         ResolveInfo info = context.getPackageManager().resolveActivity(mMyHomeIntent, 0);
@@ -107,21 +126,27 @@
 
         mUserPreferenceChangeReceiver.register(mContext, ACTION_PREFERRED_ACTIVITY_CHANGED);
         updateOverviewTargets();
+
+        lifecycleTracker.addCloseable(this::onDestroy);
+    }
+
+    /** Adds a listener for changes in {@link #isHomeAndOverviewSame()} */
+    public void addOverviewChangeListener(OverviewChangeListener overviewChangeListener) {
+        mOverviewChangeListeners.add(overviewChangeListener);
+    }
+
+    /** Removes a previously added listener */
+    public void removeOverviewChangeListener(OverviewChangeListener overviewChangeListener) {
+        mOverviewChangeListeners.remove(overviewChangeListener);
     }
 
     /**
-     * Sets a listener for changes in {@link #isHomeAndOverviewSame()}
+     * Called to set home enabled/disabled state via systemUI
+     * @param isHomeDisabled
      */
-    public void setOverviewChangeListener(Consumer<Boolean> overviewChangeListener) {
-        // TODO(b/337861962): This method should be able to support multiple listeners instead of
-        // one so that we can reuse the same instance of this class across multiple places
-        mOverviewChangeListener = overviewChangeListener;
-    }
-
-    /** Called on {@link TouchInteractionService#onSystemUiFlagsChanged} */
-    @UiThread
-    public void onSystemUiStateChanged() {
-        if (mDeviceState.isHomeDisabled() != mIsHomeDisabled) {
+    public void setHomeDisabled(boolean isHomeDisabled) {
+        if (isHomeDisabled != mIsHomeDisabled) {
+            mIsHomeDisabled = isHomeDisabled;
             updateOverviewTargets();
         }
     }
@@ -145,7 +170,6 @@
             defaultHome = null;
         }
 
-        mIsHomeDisabled = mDeviceState.isHomeDisabled();
         mIsDefaultHome = Objects.equals(mMyHomeIntent.getComponent(), defaultHome);
 
         // Set assistant visibility to 0 from launcher's perspective, ensures any elements that
@@ -181,7 +205,7 @@
 
             if (Flags.enableLauncherOverviewInWindow() || Flags.enableFallbackOverviewInWindow()) {
                 mContainerInterface =
-                        FallbackWindowInterface.getInstance(mDeviceState.getDisplayId());
+                        mRecentsDisplayModel.getFallbackWindowInterface(DEFAULT_DISPLAY);
             } else {
                 mContainerInterface = FallbackActivityInterface.INSTANCE;
             }
@@ -204,13 +228,13 @@
                         ACTION_PACKAGE_CHANGED, ACTION_PACKAGE_REMOVED);
             }
         }
-        mOverviewChangeListener.accept(mIsHomeAndOverviewSame);
+        mOverviewChangeListeners.forEach(l -> l.onOverviewTargetChange(mIsHomeAndOverviewSame));
     }
 
     /**
      * Clean up any registered receivers.
      */
-    public void onDestroy() {
+    private void onDestroy() {
         mUserPreferenceChangeReceiver.unregisterReceiverSafely(mContext);
         unregisterOtherHomeAppUpdateReceiver();
     }
@@ -294,11 +318,7 @@
      */
     public static void startHomeIntentSafely(@NonNull Context context, @Nullable Bundle options,
             @NonNull String reason) {
-        RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(context);
-        OverviewComponentObserver observer = new OverviewComponentObserver(context, deviceState);
-        Intent intent = observer.getHomeIntent();
-        observer.onDestroy();
-        deviceState.destroy();
+        Intent intent = OverviewComponentObserver.INSTANCE.get(context).getHomeIntent();
         startHomeIntentSafely(context, intent, options, reason);
     }
 
@@ -318,6 +338,17 @@
         }
     }
 
+    /**
+     * Interface for listening to overview changes
+     */
+    public interface OverviewChangeListener {
+
+        /**
+         * Called when the overview target changes
+         */
+        void onOverviewTargetChange(boolean isHomeAndOverviewSame);
+    }
+
     private static Intent createHomeIntent() {
         return new Intent(Intent.ACTION_MAIN)
                 .addCategory(Intent.CATEGORY_HOME)
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index a5d2f3e..a04ff2e 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -204,29 +204,15 @@
 
     @Override
     protected WindowInsets getWindowInsets() {
-        RecentsAnimationDeviceState rads = new RecentsAnimationDeviceState(mContext);
-        OverviewComponentObserver observer = new OverviewComponentObserver(mContext, rads);
-        try {
-            RecentsViewContainer container = observer.getContainerInterface().getCreatedContainer();
-            WindowInsets insets = container == null
-                    ? null : container.getRootView().getRootWindowInsets();
-
-            return insets == null ? super.getWindowInsets() : insets;
-        } finally {
-            observer.onDestroy();
-            rads.destroy();
-        }
+        RecentsViewContainer container = getRecentsViewContainer();
+        WindowInsets insets = container == null
+                ? null : container.getRootView().getRootWindowInsets();
+        return insets == null ? super.getWindowInsets() : insets;
     }
 
     private RecentsViewContainer getRecentsViewContainer() {
-        RecentsAnimationDeviceState rads = new RecentsAnimationDeviceState(mContext);
-        OverviewComponentObserver observer = new OverviewComponentObserver(mContext, rads);
-        try {
-            return observer.getContainerInterface().getCreatedContainer();
-        } finally {
-            observer.onDestroy();
-            rads.destroy();
-        }
+        return OverviewComponentObserver.INSTANCE.get(mContext)
+                .getContainerInterface().getCreatedContainer();
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 85e2b6e..08d43c0 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -257,7 +257,7 @@
         mRecentTasksChangedListener = null;
     }
 
-    private void initRunningTasks(ArrayList<RunningTaskInfo> runningTasks) {
+    private void initRunningTasks(List<RunningTaskInfo> runningTasks) {
         // Tasks are retrieved in order of most recently launched/used to least recently launched.
         mRunningTasks = new ArrayList<>(runningTasks);
         Collections.reverse(mRunningTasks);
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 61a150b..17c17cc 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -554,12 +554,6 @@
         return overviewCommandHelper == null || overviewCommandHelper.canStartHomeSafely();
     }
 
-    @NonNull
-    @Override
-    public TISBindHelper getTISBindHelper() {
-        return mTISBindHelper;
-    }
-
     @Override
     public boolean isRecentsViewVisible() {
         return getStateManager().getState().isRecentsViewVisible();
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
deleted file mode 100644
index 6a25ecb..0000000
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ /dev/null
@@ -1,1660 +0,0 @@
-/*
- * Copyright (C) 2019 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.quickstep;
-
-import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
-
-import static com.android.launcher3.Flags.enableUnfoldStateAnimation;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
-import static com.android.quickstep.util.LogUtils.splitFailureMessage;
-
-import android.app.ActivityManager;
-import android.app.ActivityOptions;
-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.ShortcutInfo;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.Log;
-import android.view.IRemoteAnimationRunner;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.window.DesktopModeFlags;
-import android.window.IOnBackInvokedCallback;
-import android.window.RemoteTransition;
-import android.window.TaskSnapshot;
-import android.window.TransitionFilter;
-
-import androidx.annotation.MainThread;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-import androidx.annotation.WorkerThread;
-
-import com.android.internal.logging.InstanceId;
-import com.android.internal.util.ScreenshotRequest;
-import com.android.internal.view.AppearanceRegion;
-import com.android.launcher3.dagger.ApplicationContext;
-import com.android.launcher3.dagger.LauncherAppSingleton;
-import com.android.launcher3.util.DaggerSingletonObject;
-import com.android.launcher3.util.Preconditions;
-import com.android.quickstep.dagger.QuickstepBaseAppComponent;
-import com.android.quickstep.util.ActiveGestureProtoLogProxy;
-import com.android.quickstep.util.ContextualSearchInvoker;
-import com.android.quickstep.util.unfold.ProxyUnfoldTransitionProvider;
-import com.android.systemui.shared.recents.ISystemUiProxy;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
-import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RecentsAnimationListener;
-import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController;
-import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
-import com.android.systemui.shared.system.smartspace.SmartspaceState;
-import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig;
-import com.android.systemui.unfold.progress.IUnfoldAnimation;
-import com.android.systemui.unfold.progress.IUnfoldTransitionListener;
-import com.android.wm.shell.back.IBackAnimation;
-import com.android.wm.shell.bubbles.IBubbles;
-import com.android.wm.shell.bubbles.IBubblesListener;
-import com.android.wm.shell.common.pip.IPip;
-import com.android.wm.shell.common.pip.IPipAnimationListener;
-import com.android.wm.shell.desktopmode.IDesktopMode;
-import com.android.wm.shell.desktopmode.IDesktopTaskListener;
-import com.android.wm.shell.draganddrop.IDragAndDrop;
-import com.android.wm.shell.onehanded.IOneHanded;
-import com.android.wm.shell.recents.IRecentTasks;
-import com.android.wm.shell.recents.IRecentTasksListener;
-import com.android.wm.shell.recents.IRecentsAnimationController;
-import com.android.wm.shell.recents.IRecentsAnimationRunner;
-import com.android.wm.shell.shared.GroupedTaskInfo;
-import com.android.wm.shell.shared.IShellTransitions;
-import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
-import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
-import com.android.wm.shell.shared.split.SplitBounds;
-import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;
-import com.android.wm.shell.splitscreen.ISplitScreen;
-import com.android.wm.shell.splitscreen.ISplitScreenListener;
-import com.android.wm.shell.splitscreen.ISplitSelectListener;
-import com.android.wm.shell.startingsurface.IStartingWindow;
-import com.android.wm.shell.startingsurface.IStartingWindowListener;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * Holds the reference to SystemUI.
- */
-@LauncherAppSingleton
-public class SystemUiProxy implements ISystemUiProxy, NavHandle {
-    private static final String TAG = "SystemUiProxy";
-
-    public static final DaggerSingletonObject<SystemUiProxy> INSTANCE =
-            new DaggerSingletonObject<>(QuickstepBaseAppComponent::getSystemUiProxy);
-
-    private static final int MSG_SET_SHELF_HEIGHT = 1;
-    private static final int MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT = 2;
-
-    private ISystemUiProxy mSystemUiProxy;
-    private IPip mPip;
-    private IBubbles mBubbles;
-    private ISysuiUnlockAnimationController mSysuiUnlockAnimationController;
-    private ISplitScreen mSplitScreen;
-    private IOneHanded mOneHanded;
-    private IShellTransitions mShellTransitions;
-    private IStartingWindow mStartingWindow;
-    private IRecentTasks mRecentTasks;
-    private IBackAnimation mBackAnimation;
-    private IDesktopMode mDesktopMode;
-    private IUnfoldAnimation mUnfoldAnimation;
-    private final DeathRecipient mSystemUiProxyDeathRecipient = () -> {
-        MAIN_EXECUTOR.execute(() -> clearProxy());
-    };
-
-    // Save the listeners passed into the proxy since OverviewProxyService may not have been bound
-    // yet, and we'll need to set/register these listeners with SysUI when they do.  Note that it is
-    // up to the caller to clear the listeners to prevent leaks as these can be held indefinitely
-    // in case SysUI needs to rebind.
-    private IPipAnimationListener mPipAnimationListener;
-    private IBubblesListener mBubblesListener;
-    private ISplitScreenListener mSplitScreenListener;
-    private ISplitSelectListener mSplitSelectListener;
-    private IStartingWindowListener mStartingWindowListener;
-    private ILauncherUnlockAnimationController mLauncherUnlockAnimationController;
-    private String mLauncherActivityClass;
-    private IRecentTasksListener mRecentTasksListener;
-    private IUnfoldTransitionListener mUnfoldAnimationListener;
-    private IDesktopTaskListener mDesktopTaskListener;
-    private final LinkedHashMap<RemoteTransition, TransitionFilter> mRemoteTransitions =
-            new LinkedHashMap<>();
-
-    private final List<Runnable> mStateChangeCallbacks = new ArrayList<>();
-
-    private IBinder mOriginalTransactionToken = null;
-    private IOnBackInvokedCallback mBackToLauncherCallback;
-    private IRemoteAnimationRunner mBackToLauncherRunner;
-    private IDragAndDrop mDragAndDrop;
-    private final HomeVisibilityState mHomeVisibilityState = new HomeVisibilityState();
-    private final FocusState mFocusState = new FocusState();
-
-    // Used to dedupe calls to SystemUI
-    private int mLastShelfHeight;
-    private boolean mLastShelfVisible;
-
-    // Used to dedupe calls to SystemUI
-    private int mLastLauncherKeepClearAreaHeight;
-    private boolean mLastLauncherKeepClearAreaHeightVisible;
-
-    private final Context mContext;
-    private final Handler mAsyncHandler;
-
-    // TODO(141886704): Find a way to remove this
-    @SystemUiStateFlags
-    private long mLastSystemUiStateFlags;
-
-    /**
-     * This is a singleton pending intent that is used to start recents via Shell (which is a
-     * different process). It is bare-bones, so it's expected that the component and options will
-     * be provided via fill-in intent.
-     */
-    private final PendingIntent mRecentsPendingIntent;
-
-    @Nullable
-    private final ProxyUnfoldTransitionProvider mUnfoldTransitionProvider;
-
-    @Inject
-    public SystemUiProxy(@ApplicationContext Context context) {
-        mContext = context;
-        mAsyncHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessageAsync);
-        final Intent baseIntent = new Intent().setPackage(mContext.getPackageName());
-        final ActivityOptions options = ActivityOptions.makeBasic()
-                .setPendingIntentCreatorBackgroundActivityStartMode(
-                        ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
-        mRecentsPendingIntent = PendingIntent.getActivity(mContext, 0, baseIntent,
-                PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT
-                        | Intent.FILL_IN_COMPONENT, options.toBundle());
-
-        mUnfoldTransitionProvider =
-                (enableUnfoldStateAnimation() && new ResourceUnfoldTransitionConfig().isEnabled())
-                         ? new ProxyUnfoldTransitionProvider() : null;
-    }
-
-    @Override
-    public void onBackEvent(KeyEvent backEvent) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.onBackEvent(backEvent);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onBackPressed", e);
-            }
-        }
-    }
-
-    @Override
-    public void onImeSwitcherPressed() {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.onImeSwitcherPressed();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onImeSwitcherPressed", e);
-            }
-        }
-    }
-
-    @Override
-    public void onImeSwitcherLongPress() {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.onImeSwitcherLongPress();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onImeSwitcherLongPress");
-            }
-        }
-    }
-
-    @Override
-    public void updateContextualEduStats(boolean isTrackpadGesture, String gestureType) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.updateContextualEduStats(isTrackpadGesture, gestureType);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call updateContextualEduStats");
-            }
-        }
-    }
-
-    @Override
-    public void setHomeRotationEnabled(boolean enabled) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.setHomeRotationEnabled(enabled);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onBackPressed", e);
-            }
-        }
-    }
-
-    @Override
-    public IBinder asBinder() {
-        // Do nothing
-        return null;
-    }
-
-    /**
-     * Sets proxy state, including death linkage, various listeners, and other configuration objects
-     */
-    @MainThread
-    public void setProxy(ISystemUiProxy proxy, IPip pip, IBubbles bubbles, ISplitScreen splitScreen,
-            IOneHanded oneHanded, IShellTransitions shellTransitions,
-            IStartingWindow startingWindow, IRecentTasks recentTasks,
-            ISysuiUnlockAnimationController sysuiUnlockAnimationController,
-            IBackAnimation backAnimation, IDesktopMode desktopMode,
-            IUnfoldAnimation unfoldAnimation, IDragAndDrop dragAndDrop) {
-        Preconditions.assertUIThread();
-        unlinkToDeath();
-        mSystemUiProxy = proxy;
-        mPip = pip;
-        mBubbles = bubbles;
-        mSplitScreen = splitScreen;
-        mOneHanded = oneHanded;
-        mShellTransitions = shellTransitions;
-        mStartingWindow = startingWindow;
-        mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
-        mRecentTasks = recentTasks;
-        mBackAnimation = backAnimation;
-        mDesktopMode = desktopMode;
-        mUnfoldAnimation = enableUnfoldStateAnimation() ? null : unfoldAnimation;
-        mDragAndDrop = dragAndDrop;
-        linkToDeath();
-        // re-attach the listeners once missing due to setProxy has not been initialized yet.
-        setPipAnimationListener(mPipAnimationListener);
-        setBubblesListener(mBubblesListener);
-        registerSplitScreenListener(mSplitScreenListener);
-        registerSplitSelectListener(mSplitSelectListener);
-        mHomeVisibilityState.init(mShellTransitions);
-        mFocusState.init(mShellTransitions);
-        setStartingWindowListener(mStartingWindowListener);
-        setLauncherUnlockAnimationController(
-                mLauncherActivityClass, mLauncherUnlockAnimationController);
-        new LinkedHashMap<>(mRemoteTransitions).forEach(this::registerRemoteTransition);
-        setupTransactionQueue();
-        registerRecentTasksListener(mRecentTasksListener);
-        setBackToLauncherCallback(mBackToLauncherCallback, mBackToLauncherRunner);
-        setUnfoldAnimationListener(mUnfoldAnimationListener);
-        setDesktopTaskListener(mDesktopTaskListener);
-        setAssistantOverridesRequested(new ContextualSearchInvoker(mContext)
-                .getSysUiAssistOverrideInvocationTypes());
-        mStateChangeCallbacks.forEach(Runnable::run);
-
-        if (mUnfoldTransitionProvider != null) {
-            if (unfoldAnimation != null) {
-                try {
-                    unfoldAnimation.setListener(mUnfoldTransitionProvider);
-                    mUnfoldTransitionProvider.setActive(true);
-                } catch (RemoteException e) {
-                    // Ignore
-                }
-            } else {
-                mUnfoldTransitionProvider.setActive(false);
-            }
-        }
-    }
-
-    /**
-     * Clear the proxy to release held resources and turn the majority of its operations into no-ops
-     */
-    @MainThread
-    public void clearProxy() {
-        setProxy(null, null, null, null, null, null, null, null, null, null, null, null, null);
-    }
-
-    /**
-     * Adds a callback to be notified whenever the active state changes
-     */
-    public void addOnStateChangeListener(Runnable callback) {
-        mStateChangeCallbacks.add(callback);
-    }
-
-    /**
-     * Removes a previously added state change callback
-     */
-    public void removeOnStateChangeListener(Runnable callback) {
-        mStateChangeCallbacks.remove(callback);
-    }
-
-    // TODO(141886704): Find a way to remove this
-    public void setLastSystemUiStateFlags(@SystemUiStateFlags long stateFlags) {
-        mLastSystemUiStateFlags = stateFlags;
-    }
-
-    // TODO(141886704): Find a way to remove this
-    @SystemUiStateFlags
-    public long getLastSystemUiStateFlags() {
-        return mLastSystemUiStateFlags;
-    }
-
-    public boolean isActive() {
-        return mSystemUiProxy != null;
-    }
-
-    private void linkToDeath() {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.asBinder().linkToDeath(mSystemUiProxyDeathRecipient, 0 /* flags */);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to link sysui proxy death recipient");
-            }
-        }
-    }
-
-    private void unlinkToDeath() {
-        if (mSystemUiProxy != null) {
-            mSystemUiProxy.asBinder().unlinkToDeath(mSystemUiProxyDeathRecipient, 0 /* flags */);
-        }
-    }
-
-    @Override
-    public void startScreenPinning(int taskId) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.startScreenPinning(taskId);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call startScreenPinning", e);
-            }
-        }
-    }
-
-    @Override
-    public void onOverviewShown(boolean fromHome) {
-        onOverviewShown(fromHome, TAG);
-    }
-
-    public void onOverviewShown(boolean fromHome, String tag) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.onOverviewShown(fromHome);
-            } catch (RemoteException e) {
-                Log.w(tag, "Failed call onOverviewShown from: " + (fromHome ? "home" : "app"), e);
-            }
-        }
-    }
-
-    @MainThread
-    @Override
-    public void onStatusBarTouchEvent(MotionEvent event) {
-        Preconditions.assertUIThread();
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.onStatusBarTouchEvent(event);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onStatusBarTouchEvent with arg: " + event, e);
-            }
-        }
-    }
-
-    @Override
-    public void onStatusBarTrackpadEvent(MotionEvent event) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.onStatusBarTrackpadEvent(event);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onStatusBarTrackpadEvent with arg: " + event, e);
-            }
-        }
-    }
-
-    @Override
-    public void onAssistantProgress(float progress) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.onAssistantProgress(progress);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onAssistantProgress with progress: " + progress, e);
-            }
-        }
-    }
-
-    @Override
-    public void onAssistantGestureCompletion(float velocity) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.onAssistantGestureCompletion(velocity);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onAssistantGestureCompletion", e);
-            }
-        }
-    }
-
-    @Override
-    public void startAssistant(Bundle args) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.startAssistant(args);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call startAssistant", e);
-            }
-        }
-    }
-
-    @Override
-    public void setAssistantOverridesRequested(int[] invocationTypes) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.setAssistantOverridesRequested(invocationTypes);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setAssistantOverridesRequested", e);
-            }
-        }
-    }
-
-    @Override
-    public void animateNavBarLongPress(boolean isTouchDown, boolean shrink, long durationMs) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.animateNavBarLongPress(isTouchDown, shrink, durationMs);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call animateNavBarLongPress", e);
-            }
-        }
-    }
-
-    @Override
-    public void setOverrideHomeButtonLongPress(long duration, float slopMultiplier,
-            boolean haptic) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.setOverrideHomeButtonLongPress(duration, slopMultiplier, haptic);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setOverrideHomeButtonLongPress", e);
-            }
-        }
-    }
-
-    @Override
-    public void notifyAccessibilityButtonClicked(int displayId) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.notifyAccessibilityButtonClicked(displayId);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call notifyAccessibilityButtonClicked", e);
-            }
-        }
-    }
-
-    @Override
-    public void notifyAccessibilityButtonLongClicked() {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.notifyAccessibilityButtonLongClicked();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call notifyAccessibilityButtonLongClicked", e);
-            }
-        }
-    }
-
-    @Override
-    public void stopScreenPinning() {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.stopScreenPinning();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call stopScreenPinning", e);
-            }
-        }
-    }
-
-    @Override
-    public void notifyPrioritizedRotation(int rotation) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.notifyPrioritizedRotation(rotation);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call notifyPrioritizedRotation with arg: " + rotation, e);
-            }
-        }
-    }
-
-    @Override
-    public void notifyTaskbarStatus(boolean visible, boolean stashed) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.notifyTaskbarStatus(visible, stashed);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call notifyTaskbarStatus with arg: " +
-                        visible + ", " + stashed, e);
-            }
-        }
-    }
-
-    /**
-     * NOTE: If called to suspend, caller MUST call this method to also un-suspend
-     * @param suspend should be true to stop auto-hide, false to resume normal behavior
-     */
-    @Override
-    public void notifyTaskbarAutohideSuspend(boolean suspend) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.notifyTaskbarAutohideSuspend(suspend);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call notifyTaskbarAutohideSuspend with arg: " +
-                        suspend, e);
-            }
-        }
-    }
-
-    @Override
-    public void takeScreenshot(ScreenshotRequest request) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.takeScreenshot(request);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call takeScreenshot");
-            }
-        }
-    }
-
-    @Override
-    public void expandNotificationPanel() {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.expandNotificationPanel();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call expandNotificationPanel", e);
-            }
-        }
-    }
-
-    @Override
-    public void toggleNotificationPanel() {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.toggleNotificationPanel();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call toggleNotificationPanel", e);
-            }
-        }
-    }
-
-    @Override
-    public void toggleQuickSettingsPanel() {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.toggleQuickSettingsPanel();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call toggleQuickSettingsPanel", e);
-            }
-        }
-    }
-
-    //
-    // Pip
-    //
-
-    /**
-     * Sets the shelf height.
-     */
-    public void setShelfHeight(boolean visible, int shelfHeight) {
-        Message.obtain(mAsyncHandler, MSG_SET_SHELF_HEIGHT,
-                visible ? 1 : 0 , shelfHeight).sendToTarget();
-    }
-
-    @WorkerThread
-    private void setShelfHeightAsync(int visibleInt, int shelfHeight) {
-        boolean visible = visibleInt != 0;
-        boolean changed = visible != mLastShelfVisible || shelfHeight != mLastShelfHeight;
-        IPip pip = mPip;
-        if (pip != null && changed) {
-            mLastShelfVisible = visible;
-            mLastShelfHeight = shelfHeight;
-            try {
-                pip.setShelfHeight(visible, shelfHeight);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setShelfHeight visible: " + visible
-                        + " height: " + shelfHeight, e);
-            }
-        }
-    }
-
-    /**
-     * Sets the height of the keep clear area that is going to be reported by
-     * the Launcher for the Hotseat.
-     */
-    public void setLauncherKeepClearAreaHeight(boolean visible, int height) {
-        Message.obtain(mAsyncHandler, MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT,
-                visible ? 1 : 0 , height).sendToTarget();
-    }
-
-    @WorkerThread
-    private void setLauncherKeepClearAreaHeight(int visibleInt, int height) {
-        boolean visible = visibleInt != 0;
-        boolean changed = visible != mLastLauncherKeepClearAreaHeightVisible
-                || height != mLastLauncherKeepClearAreaHeight;
-        IPip pip = mPip;
-        if (pip != null && changed) {
-            mLastLauncherKeepClearAreaHeightVisible = visible;
-            mLastLauncherKeepClearAreaHeight = height;
-            try {
-                pip.setLauncherKeepClearAreaHeight(visible, height);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setLauncherKeepClearAreaHeight visible: " + visible
-                        + " height: " + height, e);
-            }
-        }
-    }
-
-    /**
-     * Sets listener to get pip animation callbacks.
-     */
-    public void setPipAnimationListener(IPipAnimationListener listener) {
-        if (mPip != null) {
-            try {
-                mPip.setPipAnimationListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setPinnedStackAnimationListener", e);
-            }
-        }
-        mPipAnimationListener = listener;
-    }
-
-    /**
-     * @return Destination bounds of auto-pip animation, {@code null} if the animation is not ready.
-     */
-    @Nullable
-    public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
-            PictureInPictureParams pictureInPictureParams, int launcherRotation,
-            Rect hotseatKeepClearArea) {
-        if (mPip != null) {
-            try {
-                return mPip.startSwipePipToHome(componentName, activityInfo,
-                        pictureInPictureParams, launcherRotation, hotseatKeepClearArea);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call startSwipePipToHome", e);
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Notifies WM Shell that launcher has finished the preparation of the animation for swipe to
-     * home. WM Shell can choose to fade out the overlay when entering PIP is finished, and WM Shell
-     * should be responsible for cleaning up the overlay.
-     */
-    public void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds,
-            SurfaceControl overlay, Rect appBounds, Rect sourceRectHint) {
-        if (mPip != null) {
-            try {
-                mPip.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay,
-                        appBounds, sourceRectHint);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call stopSwipePipToHome");
-            }
-        }
-    }
-
-    /**
-     * Notifies WM Shell that launcher has aborted all the animation for swipe to home. WM Shell
-     * can use this callback to clean up its internal states.
-     */
-    public void abortSwipePipToHome(int taskId, ComponentName componentName) {
-        if (mPip != null) {
-            try {
-                mPip.abortSwipePipToHome(taskId, componentName);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call abortSwipePipToHome");
-            }
-        }
-    }
-
-    /**
-     * Sets the next pip animation type to be the alpha animation.
-     */
-    public void setPipAnimationTypeToAlpha() {
-        if (mPip != null) {
-            try {
-                mPip.setPipAnimationTypeToAlpha();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setPipAnimationTypeToAlpha", e);
-            }
-        }
-    }
-
-    /**
-     * Sets the app icon size in pixel used by Launcher all apps.
-     */
-    public void setLauncherAppIconSize(int iconSizePx) {
-        if (mPip != null) {
-            try {
-                mPip.setLauncherAppIconSize(iconSizePx);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setLauncherAppIconSize", e);
-            }
-        }
-    }
-
-    //
-    // Bubbles
-    //
-
-    /**
-     * Sets the listener to be notified of bubble state changes.
-     */
-    public void setBubblesListener(IBubblesListener listener) {
-        if (mBubbles != null) {
-            try {
-                if (mBubblesListener != null) {
-                    // Clear out any previous listener
-                    mBubbles.unregisterBubbleListener(mBubblesListener);
-                }
-                if (listener != null) {
-                    mBubbles.registerBubbleListener(listener);
-                }
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call registerBubblesListener");
-            }
-        }
-        mBubblesListener = listener;
-    }
-
-    /**
-     * Tells SysUI to show the bubble with the provided key.
-     * @param key the key of the bubble to show.
-     * @param top top coordinate of bubble bar on screen
-     */
-    public void showBubble(String key, int top) {
-        if (mBubbles != null) {
-            try {
-                mBubbles.showBubble(key, top);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call showBubble");
-            }
-        }
-    }
-
-    /**
-     * Tells SysUI to remove all bubbles.
-     */
-    public void removeAllBubbles() {
-        if (mBubbles == null) return;
-        try {
-            mBubbles.removeAllBubbles();
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed call removeAllBubbles");
-        }
-    }
-
-    /**
-     * Tells SysUI to collapse the bubbles.
-     */
-    public void collapseBubbles() {
-        if (mBubbles != null) {
-            try {
-                mBubbles.collapseBubbles();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call collapseBubbles");
-            }
-        }
-    }
-
-    /**
-     * Tells SysUI when the bubble is being dragged.
-     * Should be called only when the bubble bar is expanded.
-     * @param bubbleKey key of the bubble being dragged
-     */
-    public void startBubbleDrag(@Nullable String bubbleKey) {
-        if (mBubbles == null) return;
-        try {
-            mBubbles.startBubbleDrag(bubbleKey);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed call startBubbleDrag");
-        }
-    }
-
-    /**
-     * Tells SysUI when the bubble stops being dragged.
-     * Should be called only when the bubble bar is expanded.
-     *
-     * @param location location of the bubble bar
-     * @param top      new top coordinate for bubble bar on screen
-     */
-    public void stopBubbleDrag(BubbleBarLocation location, int top) {
-        if (mBubbles == null) return;
-        try {
-            mBubbles.stopBubbleDrag(location, top);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed call stopBubbleDrag");
-        }
-    }
-
-    /**
-     * Tells SysUI to dismiss the bubble with the provided key.
-     *
-     * @param key the key of the bubble to dismiss.
-     * @param timestamp the timestamp when the removal happened.
-     */
-    public void dragBubbleToDismiss(String key, long timestamp) {
-        if (mBubbles == null) return;
-        try {
-            mBubbles.dragBubbleToDismiss(key, timestamp);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed call dragBubbleToDismiss");
-        }
-    }
-
-    /**
-     * Tells SysUI to show user education relative to the reference point provided.
-     * @param position the bubble bar top center position in Screen coordinates.
-     */
-    public void showUserEducation(Point position) {
-        try {
-            mBubbles.showUserEducation(position.x, position.y);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed call showUserEducation");
-        }
-    }
-
-    /**
-     * Tells SysUI to update the bubble bar location to the new location.
-     * @param location new location for the bubble bar
-     * @param source what triggered the location update
-     */
-    public void setBubbleBarLocation(BubbleBarLocation location,
-            @BubbleBarLocation.UpdateSource int source) {
-        try {
-            mBubbles.setBubbleBarLocation(location, source);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed call setBubbleBarLocation");
-        }
-    }
-
-    /**
-     * Tells SysUI the top coordinate of bubble bar on screen
-     *
-     * @param topOnScreen top coordinate for bubble bar on screen
-     */
-    public void updateBubbleBarTopOnScreen(int topOnScreen) {
-        try {
-            if (mBubbles != null) {
-                mBubbles.updateBubbleBarTopOnScreen(topOnScreen);
-            }
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed call updateBubbleBarTopOnScreen");
-        }
-    }
-
-    /**
-     * Tells SysUI to show a shortcut bubble.
-     *
-     * @param info the shortcut info used to create or identify the bubble.
-     */
-    public void showShortcutBubble(ShortcutInfo info) {
-        try {
-            if (mBubbles != null) {
-                mBubbles.showShortcutBubble(info);
-            }
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed call show bubble for shortcut");
-        }
-    }
-
-    /**
-     * Tells SysUI to show a bubble of an app.
-     *
-     * @param intent the intent used to create the bubble.
-     */
-    public void showAppBubble(Intent intent) {
-        try {
-            if (mBubbles != null) {
-                mBubbles.showAppBubble(intent);
-            }
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed call show bubble for app");
-        }
-    }
-
-    /** Tells SysUI to show the expanded view. */
-    public void showExpandedView() {
-        try {
-            if (mBubbles != null) {
-                mBubbles.showExpandedView();
-            }
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed to call showExpandedView");
-        }
-    }
-
-    //
-    // Splitscreen
-    //
-
-    public void registerSplitScreenListener(ISplitScreenListener listener) {
-        if (mSplitScreen != null) {
-            try {
-                mSplitScreen.registerSplitScreenListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call registerSplitScreenListener");
-            }
-        }
-        mSplitScreenListener = listener;
-    }
-
-    public void unregisterSplitScreenListener(ISplitScreenListener listener) {
-        if (mSplitScreen != null) {
-            try {
-                mSplitScreen.unregisterSplitScreenListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call unregisterSplitScreenListener");
-            }
-        }
-        mSplitScreenListener = null;
-    }
-
-    public void registerSplitSelectListener(ISplitSelectListener listener) {
-        if (mSplitScreen != null) {
-            try {
-                mSplitScreen.registerSplitSelectListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call registerSplitSelectListener");
-            }
-        }
-        mSplitSelectListener = listener;
-    }
-
-    public void unregisterSplitSelectListener(ISplitSelectListener listener) {
-        if (mSplitScreen != null) {
-            try {
-                mSplitScreen.unregisterSplitSelectListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call unregisterSplitSelectListener");
-            }
-        }
-        mSplitSelectListener = null;
-    }
-
-    /** Start multiple tasks in split-screen simultaneously. */
-    public void startTasks(int taskId1, Bundle options1, int taskId2, Bundle options2,
-            @StagePosition int splitPosition, @PersistentSnapPosition int snapPosition,
-            RemoteTransition remoteTransition, InstanceId instanceId) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSplitScreen.startTasks(taskId1, options1, taskId2, options2, splitPosition,
-                        snapPosition, remoteTransition, instanceId);
-            } catch (RemoteException e) {
-                Log.w(TAG, splitFailureMessage("startTasks", "RemoteException"), e);
-            }
-        }
-    }
-
-    public void startIntentAndTask(PendingIntent pendingIntent, int userId1, Bundle options1,
-            int taskId, Bundle options2, @StagePosition int splitPosition,
-            @PersistentSnapPosition int snapPosition, RemoteTransition remoteTransition,
-            InstanceId instanceId) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSplitScreen.startIntentAndTask(pendingIntent, userId1, options1, taskId, options2,
-                        splitPosition, snapPosition, remoteTransition, instanceId);
-            } catch (RemoteException e) {
-                Log.w(TAG, splitFailureMessage("startIntentAndTask", "RemoteException"), e);
-            }
-        }
-    }
-
-    public void startIntents(PendingIntent pendingIntent1, int userId1,
-            @Nullable ShortcutInfo shortcutInfo1, Bundle options1, PendingIntent pendingIntent2,
-            int userId2, @Nullable ShortcutInfo shortcutInfo2, Bundle options2,
-            @StagePosition int splitPosition, @PersistentSnapPosition int snapPosition,
-            RemoteTransition remoteTransition, InstanceId instanceId) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSplitScreen.startIntents(pendingIntent1, userId1, shortcutInfo1, options1,
-                        pendingIntent2, userId2, shortcutInfo2, options2, splitPosition,
-                        snapPosition, remoteTransition, instanceId);
-            } catch (RemoteException e) {
-                Log.w(TAG, splitFailureMessage("startIntents", "RemoteException"), e);
-            }
-        }
-    }
-
-    public void startShortcutAndTask(ShortcutInfo shortcutInfo, Bundle options1, int taskId,
-            Bundle options2, @StagePosition int splitPosition,
-            @PersistentSnapPosition int snapPosition, RemoteTransition remoteTransition,
-            InstanceId instanceId) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSplitScreen.startShortcutAndTask(shortcutInfo, options1, taskId, options2,
-                        splitPosition, snapPosition, remoteTransition, instanceId);
-            } catch (RemoteException e) {
-                Log.w(TAG, splitFailureMessage("startShortcutAndTask", "RemoteException"), e);
-            }
-        }
-    }
-
-    public void startShortcut(String packageName, String shortcutId, int position,
-            Bundle options, UserHandle user, InstanceId instanceId) {
-        if (mSplitScreen != null) {
-            try {
-                mSplitScreen.startShortcut(packageName, shortcutId, position, options,
-                        user, instanceId);
-            } catch (RemoteException e) {
-                Log.w(TAG, splitFailureMessage("startShortcut", "RemoteException"), e);
-            }
-        }
-    }
-
-    public void startIntent(PendingIntent intent, int userId, Intent fillInIntent, int position,
-            Bundle options, InstanceId instanceId) {
-        if (mSplitScreen != null) {
-            try {
-                mSplitScreen.startIntent(intent, userId, fillInIntent, position, options,
-                        instanceId);
-            } catch (RemoteException e) {
-                Log.w(TAG, splitFailureMessage("startIntent", "RemoteException"), e);
-            }
-        }
-    }
-
-    //
-    // One handed
-    //
-
-    public void startOneHandedMode() {
-        if (mOneHanded != null) {
-            try {
-                mOneHanded.startOneHanded();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call startOneHandedMode", e);
-            }
-        }
-    }
-
-    public void stopOneHandedMode() {
-        if (mOneHanded != null) {
-            try {
-                mOneHanded.stopOneHanded();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call stopOneHandedMode", e);
-            }
-        }
-    }
-
-    //
-    // Remote transitions
-    //
-
-    public void registerRemoteTransition(
-            RemoteTransition remoteTransition, TransitionFilter filter) {
-        if (mShellTransitions != null) {
-            try {
-                mShellTransitions.registerRemote(filter, remoteTransition);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call registerRemoteTransition");
-            }
-        }
-        if (!mRemoteTransitions.containsKey(remoteTransition)) {
-            mRemoteTransitions.put(remoteTransition, filter);
-        }
-    }
-
-    public void unregisterRemoteTransition(RemoteTransition remoteTransition) {
-        if (mShellTransitions != null) {
-            try {
-                mShellTransitions.unregisterRemote(remoteTransition);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call registerRemoteTransition");
-            }
-        }
-        mRemoteTransitions.remove(remoteTransition);
-    }
-
-    public HomeVisibilityState getHomeVisibilityState() {
-        return mHomeVisibilityState;
-    }
-
-    public FocusState getFocusState() {
-        return mFocusState;
-    }
-
-    /**
-     * Returns a surface which can be used to attach overlays to home task or null if
-     * the task doesn't exist or sysui is not connected
-     */
-    @Nullable
-    public SurfaceControl getHomeTaskOverlayContainer() {
-        // Use a local reference as this method can be called on a worker thread, which can lead
-        // to NullPointer exceptions if mShellTransitions is modified on the main thread.
-        IShellTransitions shellTransitions = mShellTransitions;
-        if (shellTransitions != null) {
-            try {
-                return mShellTransitions.getHomeTaskOverlayContainer();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call getOverlayContainerForTask", e);
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Use SystemUI's transaction-queue instead of Launcher's independent one. This is necessary
-     * if Launcher and SystemUI need to coordinate transactions (eg. for shell transitions).
-     */
-    public void shareTransactionQueue() {
-        if (mOriginalTransactionToken == null) {
-            mOriginalTransactionToken = SurfaceControl.Transaction.getDefaultApplyToken();
-        }
-        setupTransactionQueue();
-    }
-
-    /**
-     * Switch back to using Launcher's independent transaction queue.
-     */
-    public void unshareTransactionQueue() {
-        if (mOriginalTransactionToken == null) {
-            return;
-        }
-        SurfaceControl.Transaction.setDefaultApplyToken(mOriginalTransactionToken);
-        mOriginalTransactionToken = null;
-    }
-
-    private void setupTransactionQueue() {
-        if (mOriginalTransactionToken == null) {
-            return;
-        }
-        if (mShellTransitions == null) {
-            SurfaceControl.Transaction.setDefaultApplyToken(mOriginalTransactionToken);
-            return;
-        }
-        final IBinder shellApplyToken;
-        try {
-            shellApplyToken = mShellTransitions.getShellApplyToken();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error getting Shell's apply token", e);
-            return;
-        }
-        if (shellApplyToken == null) {
-            Log.e(TAG, "Didn't receive apply token from Shell");
-            return;
-        }
-        SurfaceControl.Transaction.setDefaultApplyToken(shellApplyToken);
-    }
-
-    //
-    // Starting window
-    //
-
-    /**
-     * Sets listener to get callbacks when launching a task.
-     */
-    public void setStartingWindowListener(IStartingWindowListener listener) {
-        if (mStartingWindow != null) {
-            try {
-                mStartingWindow.setStartingWindowListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setStartingWindowListener", e);
-            }
-        }
-        mStartingWindowListener = listener;
-    }
-
-    //
-    // SmartSpace transitions
-    //
-
-    /**
-     * Sets the instance of {@link ILauncherUnlockAnimationController} that System UI should use to
-     * control the launcher side of the unlock animation. This will also cause us to dispatch the
-     * current state of the smartspace to System UI (this will subsequently happen if the state
-     * changes).
-     */
-    public void setLauncherUnlockAnimationController(
-            String activityClass, ILauncherUnlockAnimationController controller) {
-        if (mSysuiUnlockAnimationController != null) {
-            try {
-                mSysuiUnlockAnimationController.setLauncherUnlockController(
-                        activityClass, controller);
-                if (controller != null) {
-                    controller.dispatchSmartspaceStateToSysui();
-                }
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setLauncherUnlockAnimationController", e);
-            }
-        }
-        mLauncherActivityClass = activityClass;
-        mLauncherUnlockAnimationController = controller;
-    }
-
-    /**
-     * Tells System UI that the Launcher's smartspace state has been updated, so that it can prepare
-     * the unlock animation accordingly.
-     */
-    public void notifySysuiSmartspaceStateUpdated(SmartspaceState state) {
-        if (mSysuiUnlockAnimationController != null) {
-            try {
-                mSysuiUnlockAnimationController.onLauncherSmartspaceStateUpdated(state);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call notifySysuiSmartspaceStateUpdated", e);
-                e.printStackTrace();
-            }
-        }
-    }
-
-    //
-    // Recents
-    //
-
-    public void registerRecentTasksListener(IRecentTasksListener listener) {
-        if (mRecentTasks != null) {
-            try {
-                mRecentTasks.registerRecentTasksListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call registerRecentTasksListener", e);
-            }
-        }
-        mRecentTasksListener = listener;
-    }
-
-    public void unregisterRecentTasksListener(IRecentTasksListener listener) {
-        if (mRecentTasks != null) {
-            try {
-                mRecentTasks.unregisterRecentTasksListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call unregisterRecentTasksListener");
-            }
-        }
-        mRecentTasksListener = null;
-    }
-
-    //
-    // Back navigation transitions
-    //
-
-    /** Sets the launcher {@link android.window.IOnBackInvokedCallback} to shell */
-    public void setBackToLauncherCallback(IOnBackInvokedCallback callback,
-            IRemoteAnimationRunner runner) {
-        mBackToLauncherCallback = callback;
-        mBackToLauncherRunner = runner;
-        if (mBackAnimation == null || mBackToLauncherCallback == null) {
-            return;
-        }
-        try {
-            mBackAnimation.setBackToLauncherCallback(callback, runner);
-        } catch (RemoteException | SecurityException e) {
-            Log.e(TAG, "Failed call setBackToLauncherCallback", e);
-        }
-    }
-
-    /** Clears the previously registered {@link IOnBackInvokedCallback}.
-     *
-     * @param callback The previously registered callback instance.
-     */
-    public void clearBackToLauncherCallback(IOnBackInvokedCallback callback) {
-        if (mBackToLauncherCallback != callback) {
-            return;
-        }
-        mBackToLauncherCallback = null;
-        mBackToLauncherRunner = null;
-        if (mBackAnimation == null) {
-            return;
-        }
-        try {
-            mBackAnimation.clearBackToLauncherCallback();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed call clearBackToLauncherCallback", e);
-        }
-    }
-
-    /**
-     * Called when the status bar color needs to be customized when back navigation.
-     */
-    public void customizeStatusBarAppearance(AppearanceRegion appearance) {
-        if (mBackAnimation == null) {
-            return;
-        }
-        try {
-            mBackAnimation.customizeStatusBarAppearance(appearance);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed call useLauncherSysBarFlags", e);
-        }
-    }
-
-    public static class GetRecentTasksException extends Exception {
-        public GetRecentTasksException(String message) {
-            super(message);
-        }
-
-        public GetRecentTasksException(String message, Throwable cause) {
-            super(message, cause);
-        }
-    }
-
-    /**
-     * Retrieves a list of Recent tasks from ActivityManager.
-     * @throws GetRecentTasksException if IRecentTasks is not initialized, or when we get
-     * RemoteException from server side
-     */
-    public ArrayList<GroupedTaskInfo> getRecentTasks(int numTasks,
-            int userId) throws GetRecentTasksException {
-        if (mRecentTasks == null) {
-            Log.e(TAG, "getRecentTasks() failed due to null mRecentTasks");
-            throw new GetRecentTasksException("null mRecentTasks");
-        }
-        try {
-            final GroupedTaskInfo[] rawTasks =
-                    mRecentTasks.getRecentTasks(numTasks, RECENT_IGNORE_UNAVAILABLE, userId);
-            if (rawTasks == null) {
-                return new ArrayList<>();
-            }
-            return new ArrayList<>(Arrays.asList(rawTasks));
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed call getRecentTasks", e);
-            throw new GetRecentTasksException("Failed call getRecentTasks", e);
-        }
-    }
-
-    /**
-     * Gets the set of running tasks.
-     */
-    public ArrayList<ActivityManager.RunningTaskInfo> getRunningTasks(int numTasks) {
-        if (mRecentTasks != null && shouldEnableRunningTasksForDesktopMode()) {
-            try {
-                return new ArrayList<>(Arrays.asList(mRecentTasks.getRunningTasks(numTasks)));
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call getRunningTasks", e);
-            }
-        }
-        return new ArrayList<>();
-    }
-
-    private boolean shouldEnableRunningTasksForDesktopMode() {
-        return DesktopModeStatus.canEnterDesktopMode(mContext)
-                && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS.isTrue();
-    }
-
-    private boolean handleMessageAsync(Message msg) {
-        switch (msg.what) {
-            case MSG_SET_SHELF_HEIGHT:
-                setShelfHeightAsync(msg.arg1, msg.arg2);
-                return true;
-            case MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT:
-                setLauncherKeepClearAreaHeight(msg.arg1, msg.arg2);
-                return true;
-        }
-
-        return false;
-    }
-
-    //
-    // Desktop Mode
-    //
-
-    /** Call shell to show all apps active on the desktop */
-    public void showDesktopApps(int displayId, @Nullable RemoteTransition transition) {
-        if (mDesktopMode != null) {
-            try {
-                mDesktopMode.showDesktopApps(displayId, transition);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call showDesktopApps", e);
-            }
-        }
-    }
-
-    /**
-     * If task with the given id is on the desktop, bring it to front
-     */
-    public void showDesktopApp(int taskId, @Nullable RemoteTransition transition) {
-        if (mDesktopMode != null) {
-            try {
-                mDesktopMode.showDesktopApp(taskId, transition);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call showDesktopApp", e);
-            }
-        }
-    }
-
-    /** Call shell to get number of visible freeform tasks */
-    public int getVisibleDesktopTaskCount(int displayId) {
-        if (mDesktopMode != null) {
-            try {
-                return mDesktopMode.getVisibleTaskCount(displayId);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call getVisibleDesktopTaskCount", e);
-            }
-        }
-        return 0;
-    }
-
-    /** Set a listener on shell to get updates about desktop task state */
-    public void setDesktopTaskListener(@Nullable IDesktopTaskListener listener) {
-        mDesktopTaskListener = listener;
-        if (mDesktopMode != null) {
-            try {
-                mDesktopMode.setTaskListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setDesktopTaskListener", e);
-            }
-        }
-    }
-
-    /** Perform cleanup transactions after animation to split select is complete */
-    public void onDesktopSplitSelectAnimComplete(ActivityManager.RunningTaskInfo taskInfo) {
-        if (mDesktopMode != null) {
-            try {
-                mDesktopMode.onDesktopSplitSelectAnimComplete(taskInfo);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onDesktopSplitSelectAnimComplete", e);
-            }
-        }
-    }
-
-    /** Call shell to move a task with given `taskId` to desktop  */
-    public void moveToDesktop(int taskId, DesktopModeTransitionSource transitionSource,
-            @Nullable RemoteTransition transition) {
-        if (mDesktopMode != null) {
-            try {
-                mDesktopMode.moveToDesktop(taskId, transitionSource, transition);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call moveToDesktop", e);
-            }
-        }
-    }
-
-    /** Call shell to remove the desktop that is on given `displayId` */
-    public void removeDesktop(int displayId) {
-        if (mDesktopMode != null) {
-            try {
-                mDesktopMode.removeDesktop(displayId);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call removeDesktop", e);
-            }
-        }
-    }
-
-    /** Call shell to move a task with given `taskId` to external display. */
-    public void moveToExternalDisplay(int taskId) {
-        if (mDesktopMode != null) {
-            try {
-                mDesktopMode.moveToExternalDisplay(taskId);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call moveToExternalDisplay", e);
-            }
-        }
-    }
-
-    //
-    // Unfold transition
-    //
-
-    /** Sets the unfold animation lister to sysui. */
-    public void setUnfoldAnimationListener(IUnfoldTransitionListener callback) {
-        mUnfoldAnimationListener = callback;
-        if (mUnfoldAnimation == null) {
-            return;
-        }
-        try {
-            Log.d(TAG, "Registering unfold animation receiver");
-            mUnfoldAnimation.setListener(callback);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed call setUnfoldAnimationListener", e);
-        }
-    }
-
-    @Nullable
-    public ProxyUnfoldTransitionProvider getUnfoldTransitionProvider() {
-        return mUnfoldTransitionProvider;
-    }
-
-    //
-    // Recents
-    //
-
-    /**
-     * Starts the recents activity. The caller should manage the thread on which this is called.
-     */
-    public boolean startRecentsActivity(Intent intent, ActivityOptions options,
-            RecentsAnimationListener listener, boolean useSyntheticRecentsTransition) {
-        if (mRecentTasks == null) {
-            ActiveGestureProtoLogProxy.logRecentTasksMissing();
-            return false;
-        }
-        final IRecentsAnimationRunner runner = new IRecentsAnimationRunner.Stub() {
-            @Override
-            public void onAnimationStart(IRecentsAnimationController controller,
-                    RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
-                    Rect homeContentInsets, Rect minimizedHomeBounds, Bundle extras) {
-                // Aidl bundles need to explicitly set class loader
-                // https://developer.android.com/guide/components/aidl#Bundles
-                if (extras != null) {
-                    extras.setClassLoader(SplitBounds.class.getClassLoader());
-                }
-                listener.onAnimationStart(new RecentsAnimationControllerCompat(controller), apps,
-                        wallpapers, homeContentInsets, minimizedHomeBounds, extras);
-            }
-
-            @Override
-            public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots) {
-                listener.onAnimationCanceled(
-                        ThumbnailData.wrap(taskIds, taskSnapshots));
-            }
-
-            @Override
-            public void onTasksAppeared(RemoteAnimationTarget[] apps) {
-                listener.onTasksAppeared(apps);
-            }
-        };
-        final Bundle optsBundle = options.toBundle();
-        if (useSyntheticRecentsTransition) {
-            optsBundle.putBoolean("is_synthetic_recents_transition", true);
-        }
-        try {
-            mRecentTasks.startRecentsTransition(mRecentsPendingIntent, intent, optsBundle,
-                    mContext.getIApplicationThread(), runner);
-            return true;
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error starting recents via shell", e);
-            return false;
-        }
-    }
-
-    //
-    // Drag and drop
-    //
-
-    /**
-     * For testing purposes.  Returns `true` only if the shell drop target has shown and
-     * drawn and is ready to handle drag events and the subsequent drop.
-     */
-    public boolean isDragAndDropReady() {
-        if (mDragAndDrop == null) {
-            return false;
-        }
-        try {
-            return mDragAndDrop.isReadyToHandleDrag();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error querying drag state", e);
-            return false;
-        }
-    }
-
-    public void dump(PrintWriter pw) {
-        pw.println(TAG + ":");
-
-        pw.println("\tmSystemUiProxy=" + mSystemUiProxy);
-        pw.println("\tmPip=" + mPip);
-        pw.println("\tmPipAnimationListener=" + mPipAnimationListener);
-        pw.println("\tmBubbles=" + mBubbles);
-        pw.println("\tmBubblesListener=" + mBubblesListener);
-        pw.println("\tmSplitScreen=" + mSplitScreen);
-        pw.println("\tmSplitScreenListener=" + mSplitScreenListener);
-        pw.println("\tmSplitSelectListener=" + mSplitSelectListener);
-        pw.println("\tmOneHanded=" + mOneHanded);
-        pw.println("\tmShellTransitions=" + mShellTransitions);
-        pw.println("\tmHomeVisibilityState=" + mHomeVisibilityState);
-        pw.println("\tmFocusState=" + mFocusState);
-        pw.println("\tmStartingWindow=" + mStartingWindow);
-        pw.println("\tmStartingWindowListener=" + mStartingWindowListener);
-        pw.println("\tmSysuiUnlockAnimationController=" + mSysuiUnlockAnimationController);
-        pw.println("\tmLauncherActivityClass=" + mLauncherActivityClass);
-        pw.println("\tmLauncherUnlockAnimationController=" + mLauncherUnlockAnimationController);
-        pw.println("\tmRecentTasks=" + mRecentTasks);
-        pw.println("\tmRecentTasksListener=" + mRecentTasksListener);
-        pw.println("\tmBackAnimation=" + mBackAnimation);
-        pw.println("\tmBackToLauncherCallback=" + mBackToLauncherCallback);
-        pw.println("\tmBackToLauncherRunner=" + mBackToLauncherRunner);
-        pw.println("\tmDesktopMode=" + mDesktopMode);
-        pw.println("\tmDesktopTaskListener=" + mDesktopTaskListener);
-        pw.println("\tmUnfoldAnimation=" + mUnfoldAnimation);
-        pw.println("\tmUnfoldAnimationListener=" + mUnfoldAnimationListener);
-        pw.println("\tmDragAndDrop=" + mDragAndDrop);
-    }
-
-    /**
-     * Adds all interfaces held by this proxy to the bundle
-     */
-    @VisibleForTesting
-    public void addAllInterfaces(Bundle out) {
-        QuickStepContract.addInterface(mSystemUiProxy, out);
-        QuickStepContract.addInterface(mPip, out);
-        QuickStepContract.addInterface(mBubbles, out);
-        QuickStepContract.addInterface(mSysuiUnlockAnimationController, out);
-        QuickStepContract.addInterface(mSplitScreen, out);
-        QuickStepContract.addInterface(mOneHanded, out);
-        QuickStepContract.addInterface(mShellTransitions, out);
-        QuickStepContract.addInterface(mStartingWindow, out);
-        QuickStepContract.addInterface(mRecentTasks, out);
-        QuickStepContract.addInterface(mBackAnimation, out);
-        QuickStepContract.addInterface(mDesktopMode, out);
-        QuickStepContract.addInterface(mUnfoldAnimation, out);
-        QuickStepContract.addInterface(mDragAndDrop, out);
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.kt b/quickstep/src/com/android/quickstep/SystemUiProxy.kt
new file mode 100644
index 0000000..75694af
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.kt
@@ -0,0 +1,1270 @@
+/*
+ * Copyright (C) 2019 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.quickstep
+
+import android.app.ActivityManager
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.ActivityOptions
+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.ShortcutInfo
+import android.graphics.Point
+import android.graphics.Rect
+import android.os.Bundle
+import android.os.Handler
+import android.os.IBinder
+import android.os.Message
+import android.os.RemoteException
+import android.os.UserHandle
+import android.util.Log
+import android.view.IRemoteAnimationRunner
+import android.view.KeyEvent
+import android.view.MotionEvent
+import android.view.RemoteAnimationTarget
+import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
+import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS
+import android.window.IOnBackInvokedCallback
+import android.window.RemoteTransition
+import android.window.TaskSnapshot
+import android.window.TransitionFilter
+import androidx.annotation.MainThread
+import androidx.annotation.VisibleForTesting
+import androidx.annotation.WorkerThread
+import com.android.internal.logging.InstanceId
+import com.android.internal.util.ScreenshotRequest
+import com.android.internal.view.AppearanceRegion
+import com.android.launcher3.Flags
+import com.android.launcher3.dagger.ApplicationContext
+import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppSingleton
+import com.android.launcher3.util.DaggerSingletonObject
+import com.android.launcher3.util.Executors
+import com.android.launcher3.util.Preconditions
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition
+import com.android.quickstep.util.ActiveGestureProtoLogProxy
+import com.android.quickstep.util.ContextualSearchInvoker
+import com.android.quickstep.util.unfold.ProxyUnfoldTransitionProvider
+import com.android.systemui.shared.recents.ISystemUiProxy
+import com.android.systemui.shared.recents.model.ThumbnailData.Companion.wrap
+import com.android.systemui.shared.system.QuickStepContract
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags
+import com.android.systemui.shared.system.RecentsAnimationControllerCompat
+import com.android.systemui.shared.system.RecentsAnimationListener
+import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
+import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController
+import com.android.systemui.shared.system.smartspace.SmartspaceState
+import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
+import com.android.systemui.unfold.progress.IUnfoldAnimation
+import com.android.systemui.unfold.progress.IUnfoldTransitionListener
+import com.android.wm.shell.back.IBackAnimation
+import com.android.wm.shell.bubbles.IBubbles
+import com.android.wm.shell.bubbles.IBubblesListener
+import com.android.wm.shell.common.pip.IPip
+import com.android.wm.shell.common.pip.IPipAnimationListener
+import com.android.wm.shell.desktopmode.IDesktopMode
+import com.android.wm.shell.desktopmode.IDesktopTaskListener
+import com.android.wm.shell.draganddrop.IDragAndDrop
+import com.android.wm.shell.onehanded.IOneHanded
+import com.android.wm.shell.recents.IRecentTasks
+import com.android.wm.shell.recents.IRecentTasksListener
+import com.android.wm.shell.recents.IRecentsAnimationController
+import com.android.wm.shell.recents.IRecentsAnimationRunner
+import com.android.wm.shell.shared.GroupedTaskInfo
+import com.android.wm.shell.shared.IShellTransitions
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.UpdateSource
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
+import com.android.wm.shell.shared.split.SplitBounds
+import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition
+import com.android.wm.shell.splitscreen.ISplitScreen
+import com.android.wm.shell.splitscreen.ISplitScreenListener
+import com.android.wm.shell.splitscreen.ISplitSelectListener
+import com.android.wm.shell.startingsurface.IStartingWindow
+import com.android.wm.shell.startingsurface.IStartingWindowListener
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/** Holds the reference to SystemUI. */
+@LauncherAppSingleton
+class SystemUiProxy @Inject constructor(@ApplicationContext private val context: Context) :
+    NavHandle {
+
+    private var systemUiProxy: ISystemUiProxy? = null
+    private var pip: IPip? = null
+    private var bubbles: IBubbles? = null
+    private var sysuiUnlockAnimationController: ISysuiUnlockAnimationController? = null
+    private var splitScreen: ISplitScreen? = null
+    private var oneHanded: IOneHanded? = null
+    private var shellTransitions: IShellTransitions? = null
+    private var startingWindow: IStartingWindow? = null
+    private var recentTasks: IRecentTasks? = null
+    private var backAnimation: IBackAnimation? = null
+    private var desktopMode: IDesktopMode? = null
+    private var unfoldAnimation: IUnfoldAnimation? = null
+
+    private val systemUiProxyDeathRecipient =
+        IBinder.DeathRecipient { Executors.MAIN_EXECUTOR.execute { clearProxy() } }
+
+    // Save the listeners passed into the proxy since OverviewProxyService may not have been bound
+    // yet, and we'll need to set/register these listeners with SysUI when they do.  Note that it is
+    // up to the caller to clear the listeners to prevent leaks as these can be held indefinitely
+    // in case SysUI needs to rebind.
+    private var pipAnimationListener: IPipAnimationListener? = null
+    private var bubblesListener: IBubblesListener? = null
+    private var splitScreenListener: ISplitScreenListener? = null
+    private var splitSelectListener: ISplitSelectListener? = null
+    private var startingWindowListener: IStartingWindowListener? = null
+    private var launcherUnlockAnimationController: ILauncherUnlockAnimationController? = null
+    private var launcherActivityClass: String? = null
+    private var recentTasksListener: IRecentTasksListener? = null
+    private var unfoldAnimationListener: IUnfoldTransitionListener? = null
+    private var desktopTaskListener: IDesktopTaskListener? = null
+    private val remoteTransitions = LinkedHashMap<RemoteTransition, TransitionFilter>()
+
+    private val stateChangeCallbacks: MutableList<Runnable> = ArrayList()
+
+    private var originalTransactionToken: IBinder? = null
+    private var backToLauncherCallback: IOnBackInvokedCallback? = null
+    private var backToLauncherRunner: IRemoteAnimationRunner? = null
+    private var dragAndDrop: IDragAndDrop? = null
+    val homeVisibilityState = HomeVisibilityState()
+    private val focusState = FocusState()
+
+    // Used to dedupe calls to SystemUI
+    private var lastShelfHeight = 0
+    private var lastShelfVisible = false
+
+    // Used to dedupe calls to SystemUI
+    private var lastLauncherKeepClearAreaHeight = 0
+    private var lastLauncherKeepClearAreaHeightVisible = false
+
+    private val asyncHandler =
+        Handler(Executors.UI_HELPER_EXECUTOR.looper) { handleMessageAsync(it) }
+
+    // TODO(141886704): Find a way to remove this
+    @SystemUiStateFlags var lastSystemUiStateFlags: Long = 0
+
+    /**
+     * This is a singleton pending intent that is used to start recents via Shell (which is a
+     * different process). It is bare-bones, so it's expected that the component and options will be
+     * provided via fill-in intent.
+     */
+    private val recentsPendingIntent =
+        PendingIntent.getActivity(
+            context,
+            0,
+            Intent().setPackage(context.packageName),
+            PendingIntent.FLAG_MUTABLE or
+                PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or
+                Intent.FILL_IN_COMPONENT,
+            ActivityOptions.makeBasic()
+                .setPendingIntentCreatorBackgroundActivityStartMode(
+                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+                )
+                .toBundle(),
+        )
+
+    val unfoldTransitionProvider: ProxyUnfoldTransitionProvider? =
+        if ((Flags.enableUnfoldStateAnimation() && ResourceUnfoldTransitionConfig().isEnabled))
+            ProxyUnfoldTransitionProvider()
+        else null
+
+    private inline fun executeWithErrorLog(
+        errorMsg: () -> String,
+        tag: String = TAG,
+        callback: () -> Any?,
+    ) {
+        try {
+            callback.invoke()
+        } catch (e: RemoteException) {
+            Log.w(tag, errorMsg.invoke(), e)
+        }
+    }
+
+    fun onBackEvent(backEvent: KeyEvent?) =
+        executeWithErrorLog({ "Failed call onBackPressed" }) {
+            systemUiProxy?.onBackEvent(backEvent)
+        }
+
+    fun onImeSwitcherPressed() =
+        executeWithErrorLog({ "Failed call onImeSwitcherPressed" }) {
+            systemUiProxy?.onImeSwitcherPressed()
+        }
+
+    fun onImeSwitcherLongPress() =
+        executeWithErrorLog({ "Failed call onImeSwitcherLongPress" }) {
+            systemUiProxy?.onImeSwitcherLongPress()
+        }
+
+    fun updateContextualEduStats(isTrackpadGesture: Boolean, gestureType: String) =
+        executeWithErrorLog({ "Failed call updateContextualEduStats" }) {
+            systemUiProxy?.updateContextualEduStats(isTrackpadGesture, gestureType)
+        }
+
+    fun setHomeRotationEnabled(enabled: Boolean) =
+        executeWithErrorLog({ "Failed call setHomeRotationEnabled" }) {
+            systemUiProxy?.setHomeRotationEnabled(enabled)
+        }
+
+    /**
+     * Sets proxy state, including death linkage, various listeners, and other configuration objects
+     */
+    @MainThread
+    fun setProxy(
+        proxy: ISystemUiProxy?,
+        pip: IPip?,
+        bubbles: IBubbles?,
+        splitScreen: ISplitScreen?,
+        oneHanded: IOneHanded?,
+        shellTransitions: IShellTransitions?,
+        startingWindow: IStartingWindow?,
+        recentTasks: IRecentTasks?,
+        sysuiUnlockAnimationController: ISysuiUnlockAnimationController?,
+        backAnimation: IBackAnimation?,
+        desktopMode: IDesktopMode?,
+        unfoldAnimation: IUnfoldAnimation?,
+        dragAndDrop: IDragAndDrop?,
+    ) {
+        Preconditions.assertUIThread()
+        unlinkToDeath()
+        systemUiProxy = proxy
+        this.pip = pip
+        this.bubbles = bubbles
+        this.splitScreen = splitScreen
+        this.oneHanded = oneHanded
+        this.shellTransitions = shellTransitions
+        this.startingWindow = startingWindow
+        this.sysuiUnlockAnimationController = sysuiUnlockAnimationController
+        this.recentTasks = recentTasks
+        this.backAnimation = backAnimation
+        this.desktopMode = desktopMode
+        this.unfoldAnimation = if (Flags.enableUnfoldStateAnimation()) null else unfoldAnimation
+        this.dragAndDrop = dragAndDrop
+        linkToDeath()
+        // re-attach the listeners once missing due to setProxy has not been initialized yet.
+        setPipAnimationListener(pipAnimationListener)
+        setBubblesListener(bubblesListener)
+        registerSplitScreenListener(splitScreenListener)
+        registerSplitSelectListener(splitSelectListener)
+        homeVisibilityState.init(this.shellTransitions)
+        focusState.init(this.shellTransitions)
+        setStartingWindowListener(startingWindowListener)
+        setLauncherUnlockAnimationController(
+            launcherActivityClass,
+            launcherUnlockAnimationController,
+        )
+        LinkedHashMap(remoteTransitions).forEach { (remoteTransition, filter) ->
+            registerRemoteTransition(remoteTransition, filter)
+        }
+        setupTransactionQueue()
+        registerRecentTasksListener(recentTasksListener)
+        setBackToLauncherCallback(backToLauncherCallback, backToLauncherRunner)
+        setUnfoldAnimationListener(unfoldAnimationListener)
+        setDesktopTaskListener(desktopTaskListener)
+        setAssistantOverridesRequested(
+            ContextualSearchInvoker(context).getSysUiAssistOverrideInvocationTypes()
+        )
+        stateChangeCallbacks.forEach { it.run() }
+
+        if (unfoldTransitionProvider != null) {
+            if (unfoldAnimation != null) {
+                try {
+                    unfoldAnimation.setListener(unfoldTransitionProvider)
+                    unfoldTransitionProvider.isActive = true
+                } catch (e: RemoteException) {
+                    // Ignore
+                }
+            } else {
+                unfoldTransitionProvider.isActive = false
+            }
+        }
+    }
+
+    /**
+     * Clear the proxy to release held resources and turn the majority of its operations into no-ops
+     */
+    @MainThread
+    fun clearProxy() =
+        setProxy(null, null, null, null, null, null, null, null, null, null, null, null, null)
+
+    /** Adds a callback to be notified whenever the active state changes */
+    fun addOnStateChangeListener(callback: Runnable) = stateChangeCallbacks.add(callback)
+
+    /** Removes a previously added state change callback */
+    fun removeOnStateChangeListener(callback: Runnable) = stateChangeCallbacks.remove(callback)
+
+    fun isActive() = systemUiProxy != null
+
+    private fun linkToDeath() =
+        executeWithErrorLog({ "Failed to link sysui proxy death recipient" }) {
+            systemUiProxy?.asBinder()?.linkToDeath(systemUiProxyDeathRecipient, 0 /* flags */)
+        }
+
+    private fun unlinkToDeath() =
+        systemUiProxy?.asBinder()?.unlinkToDeath(systemUiProxyDeathRecipient, 0 /* flags */)
+
+    fun startScreenPinning(taskId: Int) =
+        executeWithErrorLog({ "Failed call startScreenPinning" }) {
+            systemUiProxy?.startScreenPinning(taskId)
+        }
+
+    fun onOverviewShown(fromHome: Boolean, tag: String = TAG) =
+        executeWithErrorLog(
+            { "Failed call onOverviewShown from: ${(if (fromHome) "home" else "app")}" },
+            tag = tag,
+        ) {
+            systemUiProxy?.onOverviewShown(fromHome)
+        }
+
+    @MainThread
+    fun onStatusBarTouchEvent(event: MotionEvent) {
+        Preconditions.assertUIThread()
+        executeWithErrorLog({ "Failed call onStatusBarTouchEvent with arg: $event" }) {
+            systemUiProxy?.onStatusBarTouchEvent(event)
+        }
+    }
+
+    fun onStatusBarTrackpadEvent(event: MotionEvent) =
+        executeWithErrorLog({ "Failed call onStatusBarTrackpadEvent with arg: $event" }) {
+            systemUiProxy?.onStatusBarTrackpadEvent(event)
+        }
+
+    fun onAssistantProgress(progress: Float) =
+        executeWithErrorLog({ "Failed call onAssistantProgress with progress: $progress" }) {
+            systemUiProxy?.onAssistantProgress(progress)
+        }
+
+    fun onAssistantGestureCompletion(velocity: Float) =
+        executeWithErrorLog({ "Failed call onAssistantGestureCompletion" }) {
+            systemUiProxy?.onAssistantGestureCompletion(velocity)
+        }
+
+    fun startAssistant(args: Bundle) =
+        executeWithErrorLog({ "Failed call startAssistant" }) {
+            systemUiProxy?.startAssistant(args)
+        }
+
+    fun setAssistantOverridesRequested(invocationTypes: IntArray) =
+        executeWithErrorLog({ "Failed call setAssistantOverridesRequested" }) {
+            systemUiProxy?.setAssistantOverridesRequested(invocationTypes)
+        }
+
+    override fun animateNavBarLongPress(isTouchDown: Boolean, shrink: Boolean, durationMs: Long) =
+        executeWithErrorLog({ "Failed call animateNavBarLongPress" }) {
+            systemUiProxy?.animateNavBarLongPress(isTouchDown, shrink, durationMs)
+        }
+
+    fun setOverrideHomeButtonLongPress(duration: Long, slopMultiplier: Float, haptic: Boolean) =
+        executeWithErrorLog({ "Failed call setOverrideHomeButtonLongPress" }) {
+            systemUiProxy?.setOverrideHomeButtonLongPress(duration, slopMultiplier, haptic)
+        }
+
+    fun notifyAccessibilityButtonClicked(displayId: Int) =
+        executeWithErrorLog({ "Failed call notifyAccessibilityButtonClicked" }) {
+            systemUiProxy?.notifyAccessibilityButtonClicked(displayId)
+        }
+
+    fun notifyAccessibilityButtonLongClicked() =
+        executeWithErrorLog({ "Failed call notifyAccessibilityButtonLongClicked" }) {
+            systemUiProxy?.notifyAccessibilityButtonLongClicked()
+        }
+
+    fun stopScreenPinning() =
+        executeWithErrorLog({ "Failed call stopScreenPinning" }) {
+            systemUiProxy?.stopScreenPinning()
+        }
+
+    fun notifyPrioritizedRotation(rotation: Int) =
+        executeWithErrorLog({ "Failed call notifyPrioritizedRotation with arg: $rotation" }) {
+            systemUiProxy?.notifyPrioritizedRotation(rotation)
+        }
+
+    fun notifyTaskbarStatus(visible: Boolean, stashed: Boolean) =
+        executeWithErrorLog({ "Failed call notifyTaskbarStatus with arg: $visible, $stashed" }) {
+            systemUiProxy?.notifyTaskbarStatus(visible, stashed)
+        }
+
+    /**
+     * NOTE: If called to suspend, caller MUST call this method to also un-suspend. [suspend] should
+     * be `true` to stop auto-hide, `false` to resume normal behavior
+     */
+    fun notifyTaskbarAutohideSuspend(suspend: Boolean) =
+        executeWithErrorLog({ "Failed call notifyTaskbarAutohideSuspend with arg: $suspend" }) {
+            systemUiProxy?.notifyTaskbarAutohideSuspend(suspend)
+        }
+
+    fun takeScreenshot(request: ScreenshotRequest) =
+        executeWithErrorLog({ "Failed call takeScreenshot" }) {
+            systemUiProxy?.takeScreenshot(request)
+        }
+
+    fun expandNotificationPanel() =
+        executeWithErrorLog({ "Failed call expandNotificationPanel" }) {
+            systemUiProxy?.expandNotificationPanel()
+        }
+
+    fun toggleNotificationPanel() =
+        executeWithErrorLog({ "Failed call toggleNotificationPanel" }) {
+            systemUiProxy?.toggleNotificationPanel()
+        }
+
+    fun toggleQuickSettingsPanel() =
+        executeWithErrorLog({ "Failed call toggleQuickSettingsPanel" }) {
+            systemUiProxy?.toggleQuickSettingsPanel()
+        }
+
+    //
+    // Pip
+    //
+    /** Sets the shelf height. */
+    fun setShelfHeight(visible: Boolean, shelfHeight: Int) =
+        Message.obtain(asyncHandler, MSG_SET_SHELF_HEIGHT, if (visible) 1 else 0, shelfHeight)
+            .sendToTarget()
+
+    @WorkerThread
+    private fun setShelfHeightAsync(visibleInt: Int, shelfHeight: Int) {
+        val visible = visibleInt != 0
+        val changed = visible != lastShelfVisible || shelfHeight != lastShelfHeight
+        val pip = pip
+        if (pip != null && changed) {
+            lastShelfVisible = visible
+            lastShelfHeight = shelfHeight
+            executeWithErrorLog({
+                "Failed call setShelfHeight visible: $visible height: $shelfHeight"
+            }) {
+                pip.setShelfHeight(visible, shelfHeight)
+            }
+        }
+    }
+
+    /**
+     * Sets the height of the keep clear area that is going to be reported by the Launcher for the
+     * Hotseat.
+     */
+    fun setLauncherKeepClearAreaHeight(visible: Boolean, height: Int) =
+        Message.obtain(
+                asyncHandler,
+                MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT,
+                if (visible) 1 else 0,
+                height,
+            )
+            .sendToTarget()
+
+    @WorkerThread
+    private fun setLauncherKeepClearAreaHeight(visibleInt: Int, height: Int) {
+        val visible = visibleInt != 0
+        val changed =
+            visible != lastLauncherKeepClearAreaHeightVisible ||
+                height != lastLauncherKeepClearAreaHeight
+        val pip = pip
+        if (pip != null && changed) {
+            lastLauncherKeepClearAreaHeightVisible = visible
+            lastLauncherKeepClearAreaHeight = height
+            executeWithErrorLog({
+                "Failed call setLauncherKeepClearAreaHeight visible: $visible height: $height"
+            }) {
+                pip.setLauncherKeepClearAreaHeight(visible, height)
+            }
+        }
+    }
+
+    /** Sets listener to get pip animation callbacks. */
+    fun setPipAnimationListener(listener: IPipAnimationListener?) {
+        executeWithErrorLog({ "Failed call setPinnedStackAnimationListener" }) {
+            pip?.setPipAnimationListener(listener)
+        }
+        pipAnimationListener = listener
+    }
+
+    /** @return Destination bounds of auto-pip animation, `null` if the animation is not ready. */
+    fun startSwipePipToHome(
+        componentName: ComponentName?,
+        activityInfo: ActivityInfo?,
+        pictureInPictureParams: PictureInPictureParams?,
+        launcherRotation: Int,
+        hotseatKeepClearArea: Rect?,
+    ): Rect? {
+        executeWithErrorLog({ "Failed call startSwipePipToHome" }) {
+            return pip?.startSwipePipToHome(
+                componentName,
+                activityInfo,
+                pictureInPictureParams,
+                launcherRotation,
+                hotseatKeepClearArea,
+            )
+        }
+        return null
+    }
+
+    /**
+     * Notifies WM Shell that launcher has finished the preparation of the animation for swipe to
+     * home. WM Shell can choose to fade out the overlay when entering PIP is finished, and WM Shell
+     * should be responsible for cleaning up the overlay.
+     */
+    fun stopSwipePipToHome(
+        taskId: Int,
+        componentName: ComponentName?,
+        destinationBounds: Rect?,
+        overlay: SurfaceControl?,
+        appBounds: Rect?,
+        sourceRectHint: Rect?,
+    ) =
+        executeWithErrorLog({ "Failed call stopSwipePipToHome" }) {
+            pip?.stopSwipePipToHome(
+                taskId,
+                componentName,
+                destinationBounds,
+                overlay,
+                appBounds,
+                sourceRectHint,
+            )
+        }
+
+    /**
+     * Notifies WM Shell that launcher has aborted all the animation for swipe to home. WM Shell can
+     * use this callback to clean up its internal states.
+     */
+    fun abortSwipePipToHome(taskId: Int, componentName: ComponentName?) =
+        executeWithErrorLog({ "Failed call abortSwipePipToHome" }) {
+            pip?.abortSwipePipToHome(taskId, componentName)
+        }
+
+    /** Sets the next pip animation type to be the alpha animation. */
+    fun setPipAnimationTypeToAlpha() =
+        executeWithErrorLog({ "Failed call setPipAnimationTypeToAlpha" }) {
+            pip?.setPipAnimationTypeToAlpha()
+        }
+
+    /** Sets the app icon size in pixel used by Launcher all apps. */
+    fun setLauncherAppIconSize(iconSizePx: Int) =
+        executeWithErrorLog({ "Failed call setLauncherAppIconSize" }) {
+            pip?.setLauncherAppIconSize(iconSizePx)
+        }
+
+    //
+    // Bubbles
+    //
+    /** Sets the listener to be notified of bubble state changes. */
+    fun setBubblesListener(listener: IBubblesListener?) {
+        executeWithErrorLog({ "Failed call registerBubblesListener" }) {
+            bubbles?.apply {
+                bubblesListener?.let { unregisterBubbleListener(it) }
+                listener?.let { registerBubbleListener(it) }
+            }
+        }
+        bubblesListener = listener
+    }
+
+    /**
+     * Tells SysUI to show the bubble with the provided key.
+     *
+     * @param key the key of the bubble to show.
+     * @param top top coordinate of bubble bar on screen
+     */
+    fun showBubble(key: String?, top: Int) =
+        executeWithErrorLog({ "Failed call showBubble" }) { bubbles?.showBubble(key, top) }
+
+    /** Tells SysUI to remove all bubbles. */
+    fun removeAllBubbles() =
+        executeWithErrorLog({ "Failed call removeAllBubbles" }) { bubbles?.removeAllBubbles() }
+
+    /** Tells SysUI to collapse the bubbles. */
+    fun collapseBubbles() =
+        executeWithErrorLog({ "Failed call collapseBubbles" }) { bubbles?.collapseBubbles() }
+
+    /**
+     * Tells SysUI when the bubble is being dragged. Should be called only when the bubble bar is
+     * expanded.
+     *
+     * @param bubbleKey key of the bubble being dragged
+     */
+    fun startBubbleDrag(bubbleKey: String?) =
+        executeWithErrorLog({ "Failed call startBubbleDrag" }) {
+            bubbles?.startBubbleDrag(bubbleKey)
+        }
+
+    /**
+     * Tells SysUI when the bubble stops being dragged. Should be called only when the bubble bar is
+     * expanded.
+     *
+     * @param location location of the bubble bar
+     * @param top new top coordinate for bubble bar on screen
+     */
+    fun stopBubbleDrag(location: BubbleBarLocation?, top: Int) =
+        executeWithErrorLog({ "Failed call stopBubbleDrag" }) {
+            bubbles?.stopBubbleDrag(location, top)
+        }
+
+    /**
+     * Tells SysUI to dismiss the bubble with the provided key.
+     *
+     * @param key the key of the bubble to dismiss.
+     * @param timestamp the timestamp when the removal happened.
+     */
+    fun dragBubbleToDismiss(key: String?, timestamp: Long) =
+        executeWithErrorLog({ "Failed call dragBubbleToDismiss" }) {
+            bubbles?.dragBubbleToDismiss(key, timestamp)
+        }
+
+    /**
+     * Tells SysUI to show user education relative to the reference point provided.
+     *
+     * @param position the bubble bar top center position in Screen coordinates.
+     */
+    fun showUserEducation(position: Point) =
+        executeWithErrorLog({ "Failed call showUserEducation" }) {
+            bubbles?.showUserEducation(position.x, position.y)
+        }
+
+    /**
+     * Tells SysUI to update the bubble bar location to the new location.
+     *
+     * @param location new location for the bubble bar
+     * @param source what triggered the location update
+     */
+    fun setBubbleBarLocation(location: BubbleBarLocation?, @UpdateSource source: Int) =
+        executeWithErrorLog({ "Failed call setBubbleBarLocation" }) {
+            bubbles?.setBubbleBarLocation(location, source)
+        }
+
+    /**
+     * Tells SysUI the top coordinate of bubble bar on screen
+     *
+     * @param topOnScreen top coordinate for bubble bar on screen
+     */
+    fun updateBubbleBarTopOnScreen(topOnScreen: Int) =
+        executeWithErrorLog({ "Failed call updateBubbleBarTopOnScreen" }) {
+            bubbles?.updateBubbleBarTopOnScreen(topOnScreen)
+        }
+
+    /**
+     * Tells SysUI to show a shortcut bubble.
+     *
+     * @param info the shortcut info used to create or identify the bubble.
+     */
+    fun showShortcutBubble(info: ShortcutInfo?) =
+        executeWithErrorLog({ "Failed call showShortcutBubble" }) {
+            bubbles?.showShortcutBubble(info)
+        }
+
+    /**
+     * Tells SysUI to show a bubble of an app.
+     *
+     * @param intent the intent used to create the bubble.
+     */
+    fun showAppBubble(intent: Intent?) =
+        executeWithErrorLog({ "Failed call showAppBubble" }) { bubbles?.showAppBubble(intent) }
+
+    /** Tells SysUI to show the expanded view. */
+    fun showExpandedView() =
+        executeWithErrorLog({ "Failed call showExpandedView" }) { bubbles?.showExpandedView() }
+
+    //
+    // Splitscreen
+    //
+    fun registerSplitScreenListener(listener: ISplitScreenListener?) {
+        executeWithErrorLog({ "Failed call registerSplitScreenListener" }) {
+            splitScreen?.registerSplitScreenListener(listener)
+        }
+        splitScreenListener = listener
+    }
+
+    fun unregisterSplitScreenListener(listener: ISplitScreenListener?) {
+        executeWithErrorLog({ "Failed call unregisterSplitScreenListener" }) {
+            splitScreen?.unregisterSplitScreenListener(listener)
+        }
+        splitScreenListener = null
+    }
+
+    fun registerSplitSelectListener(listener: ISplitSelectListener?) {
+        executeWithErrorLog({ "Failed call registerSplitSelectListener" }) {
+            splitScreen?.registerSplitSelectListener(listener)
+        }
+        splitSelectListener = listener
+    }
+
+    fun unregisterSplitSelectListener(listener: ISplitSelectListener?) {
+        executeWithErrorLog({ "Failed call unregisterSplitSelectListener" }) {
+            splitScreen?.unregisterSplitSelectListener(listener)
+        }
+        splitSelectListener = null
+    }
+
+    /** Start multiple tasks in split-screen simultaneously. */
+    fun startTasks(
+        taskId1: Int,
+        options1: Bundle?,
+        taskId2: Int,
+        options2: Bundle?,
+        @StagePosition splitPosition: Int,
+        @PersistentSnapPosition snapPosition: Int,
+        remoteTransition: RemoteTransition?,
+        instanceId: InstanceId?,
+    ) =
+        executeWithErrorLog({ "Failed call startTasks" }) {
+            splitScreen?.startTasks(
+                taskId1,
+                options1,
+                taskId2,
+                options2,
+                splitPosition,
+                snapPosition,
+                remoteTransition,
+                instanceId,
+            )
+        }
+
+    fun startIntentAndTask(
+        pendingIntent: PendingIntent?,
+        userId1: Int,
+        options1: Bundle?,
+        taskId: Int,
+        options2: Bundle?,
+        @StagePosition splitPosition: Int,
+        @PersistentSnapPosition snapPosition: Int,
+        remoteTransition: RemoteTransition?,
+        instanceId: InstanceId?,
+    ) =
+        executeWithErrorLog({ "Failed call startIntentAndTask" }) {
+            splitScreen?.startIntentAndTask(
+                pendingIntent,
+                userId1,
+                options1,
+                taskId,
+                options2,
+                splitPosition,
+                snapPosition,
+                remoteTransition,
+                instanceId,
+            )
+        }
+
+    fun startIntents(
+        pendingIntent1: PendingIntent?,
+        userId1: Int,
+        shortcutInfo1: ShortcutInfo?,
+        options1: Bundle?,
+        pendingIntent2: PendingIntent?,
+        userId2: Int,
+        shortcutInfo2: ShortcutInfo?,
+        options2: Bundle?,
+        @StagePosition splitPosition: Int,
+        @PersistentSnapPosition snapPosition: Int,
+        remoteTransition: RemoteTransition?,
+        instanceId: InstanceId?,
+    ) =
+        executeWithErrorLog({ "Failed call startIntents" }) {
+            splitScreen?.startIntents(
+                pendingIntent1,
+                userId1,
+                shortcutInfo1,
+                options1,
+                pendingIntent2,
+                userId2,
+                shortcutInfo2,
+                options2,
+                splitPosition,
+                snapPosition,
+                remoteTransition,
+                instanceId,
+            )
+        }
+
+    fun startShortcutAndTask(
+        shortcutInfo: ShortcutInfo?,
+        options1: Bundle?,
+        taskId: Int,
+        options2: Bundle?,
+        @StagePosition splitPosition: Int,
+        @PersistentSnapPosition snapPosition: Int,
+        remoteTransition: RemoteTransition?,
+        instanceId: InstanceId?,
+    ) =
+        executeWithErrorLog({ "Failed call startShortcutAndTask" }) {
+            splitScreen?.startShortcutAndTask(
+                shortcutInfo,
+                options1,
+                taskId,
+                options2,
+                splitPosition,
+                snapPosition,
+                remoteTransition,
+                instanceId,
+            )
+        }
+
+    fun startShortcut(
+        packageName: String?,
+        shortcutId: String?,
+        position: Int,
+        options: Bundle?,
+        user: UserHandle?,
+        instanceId: InstanceId?,
+    ) =
+        executeWithErrorLog({ "Failed call startShortcut" }) {
+            splitScreen?.startShortcut(packageName, shortcutId, position, options, user, instanceId)
+        }
+
+    fun startIntent(
+        intent: PendingIntent?,
+        userId: Int,
+        fillInIntent: Intent?,
+        position: Int,
+        options: Bundle?,
+        instanceId: InstanceId?,
+    ) =
+        executeWithErrorLog({ "Failed call startIntent" }) {
+            splitScreen?.startIntent(intent, userId, fillInIntent, position, options, instanceId)
+        }
+
+    //
+    // One handed
+    //
+    fun startOneHandedMode() =
+        executeWithErrorLog({ "Failed call startOneHandedMode" }) { oneHanded?.startOneHanded() }
+
+    fun stopOneHandedMode() =
+        executeWithErrorLog({ "Failed call stopOneHandedMode" }) { oneHanded?.stopOneHanded() }
+
+    //
+    // Remote transitions
+    //
+    fun registerRemoteTransition(remoteTransition: RemoteTransition?, filter: TransitionFilter) {
+        remoteTransition ?: return
+        executeWithErrorLog({ "Failed call registerRemoteTransition" }) {
+            shellTransitions?.registerRemote(filter, remoteTransition)
+        }
+        remoteTransitions.putIfAbsent(remoteTransition, filter)
+    }
+
+    fun unregisterRemoteTransition(remoteTransition: RemoteTransition?) {
+        executeWithErrorLog({ "Failed call unregisterRemoteTransition" }) {
+            shellTransitions?.unregisterRemote(remoteTransition)
+        }
+        remoteTransitions.remove(remoteTransition)
+    }
+
+    /**
+     * Returns a surface which can be used to attach overlays to home task or null if the task
+     * doesn't exist or sysui is not connected
+     */
+    fun getHomeTaskOverlayContainer(): SurfaceControl? {
+        executeWithErrorLog({ "Failed call getHomeTaskOverlayContainer" }) {
+            return shellTransitions?.homeTaskOverlayContainer
+        }
+        return null
+    }
+
+    /**
+     * Use SystemUI's transaction-queue instead of Launcher's independent one. This is necessary if
+     * Launcher and SystemUI need to coordinate transactions (eg. for shell transitions).
+     */
+    fun shareTransactionQueue() {
+        if (originalTransactionToken == null) {
+            originalTransactionToken = Transaction.getDefaultApplyToken()
+        }
+        setupTransactionQueue()
+    }
+
+    /** Switch back to using Launcher's independent transaction queue. */
+    fun unshareTransactionQueue() {
+        if (originalTransactionToken == null) {
+            return
+        }
+        Transaction.setDefaultApplyToken(originalTransactionToken)
+        originalTransactionToken = null
+    }
+
+    private fun setupTransactionQueue() =
+        executeWithErrorLog({ "Error getting Shell's apply token" }) {
+            val token: IBinder =
+                shellTransitions?.shellApplyToken ?: originalTransactionToken ?: return
+            Transaction.setDefaultApplyToken(token)
+        }
+
+    //
+    // Starting window
+    //
+    /** Sets listener to get callbacks when launching a task. */
+    fun setStartingWindowListener(listener: IStartingWindowListener?) {
+        executeWithErrorLog({ "Failed call setStartingWindowListener" }) {
+            startingWindow?.setStartingWindowListener(listener)
+        }
+        startingWindowListener = listener
+    }
+
+    //
+    // SmartSpace transitions
+    //
+    /**
+     * Sets the instance of [ILauncherUnlockAnimationController] that System UI should use to
+     * control the launcher side of the unlock animation. This will also cause us to dispatch the
+     * current state of the smartspace to System UI (this will subsequently happen if the state
+     * changes).
+     */
+    fun setLauncherUnlockAnimationController(
+        activityClass: String?,
+        controller: ILauncherUnlockAnimationController?,
+    ) {
+        executeWithErrorLog({ "Failed call setLauncherUnlockAnimationController" }) {
+            sysuiUnlockAnimationController?.apply {
+                setLauncherUnlockController(activityClass, controller)
+                controller?.dispatchSmartspaceStateToSysui()
+            }
+        }
+        launcherActivityClass = activityClass
+        launcherUnlockAnimationController = controller
+    }
+
+    /**
+     * Tells System UI that the Launcher's smartspace state has been updated, so that it can prepare
+     * the unlock animation accordingly.
+     */
+    fun notifySysuiSmartspaceStateUpdated(state: SmartspaceState?) =
+        executeWithErrorLog({ "Failed call notifySysuiSmartspaceStateUpdated" }) {
+            sysuiUnlockAnimationController?.onLauncherSmartspaceStateUpdated(state)
+        }
+
+    //
+    // Recents
+    //
+    fun registerRecentTasksListener(listener: IRecentTasksListener?) {
+        executeWithErrorLog({ "Failed call registerRecentTasksListener" }) {
+            recentTasks?.registerRecentTasksListener(listener)
+        }
+        recentTasksListener = listener
+    }
+
+    fun unregisterRecentTasksListener(listener: IRecentTasksListener?) {
+        executeWithErrorLog({ "Failed call unregisterRecentTasksListener" }) {
+            recentTasks?.unregisterRecentTasksListener(listener)
+        }
+        recentTasksListener = null
+    }
+
+    //
+    // Back navigation transitions
+    //
+    /** Sets the launcher [android.window.IOnBackInvokedCallback] to shell */
+    fun setBackToLauncherCallback(
+        callback: IOnBackInvokedCallback?,
+        runner: IRemoteAnimationRunner?,
+    ) {
+        backToLauncherCallback = callback
+        backToLauncherRunner = runner
+        if (callback == null) return
+        try {
+            backAnimation?.setBackToLauncherCallback(callback, runner)
+        } catch (e: RemoteException) {
+            Log.e(TAG, "Failed call setBackToLauncherCallback", e)
+        } catch (e: SecurityException) {
+            Log.e(TAG, "Failed call setBackToLauncherCallback", e)
+        }
+    }
+
+    /**
+     * Clears the previously registered [IOnBackInvokedCallback].
+     *
+     * @param callback The previously registered callback instance.
+     */
+    fun clearBackToLauncherCallback(callback: IOnBackInvokedCallback) {
+        if (backToLauncherCallback !== callback) {
+            return
+        }
+        backToLauncherCallback = null
+        backToLauncherRunner = null
+        executeWithErrorLog({ "Failed call clearBackToLauncherCallback" }) {
+            backAnimation?.clearBackToLauncherCallback()
+        }
+    }
+
+    /** Called when the status bar color needs to be customized when back navigation. */
+    fun customizeStatusBarAppearance(appearance: AppearanceRegion?) =
+        executeWithErrorLog({ "Failed call customizeStatusBarAppearance" }) {
+            backAnimation?.customizeStatusBarAppearance(appearance)
+        }
+
+    class GetRecentTasksException : Exception {
+        constructor(message: String?) : super(message)
+
+        constructor(message: String?, cause: Throwable?) : super(message, cause)
+    }
+
+    /**
+     * Retrieves a list of Recent tasks from ActivityManager.
+     *
+     * @throws GetRecentTasksException if IRecentTasks is not initialized, or when we get
+     *   RemoteException from server side
+     */
+    @Throws(GetRecentTasksException::class)
+    fun getRecentTasks(numTasks: Int, userId: Int): ArrayList<GroupedTaskInfo> {
+        if (recentTasks == null) {
+            Log.e(TAG, "getRecentTasks() failed due to null mRecentTasks")
+            throw GetRecentTasksException("null mRecentTasks")
+        }
+        try {
+            val rawTasks =
+                recentTasks?.getRecentTasks(
+                    numTasks,
+                    ActivityManager.RECENT_IGNORE_UNAVAILABLE,
+                    userId,
+                ) ?: return ArrayList()
+            return ArrayList(rawTasks.asList())
+        } catch (e: RemoteException) {
+            Log.e(TAG, "Failed call getRecentTasks", e)
+            throw GetRecentTasksException("Failed call getRecentTasks", e)
+        }
+    }
+
+    /** Gets the set of running tasks. */
+    fun getRunningTasks(numTasks: Int): List<RunningTaskInfo> {
+        if (!shouldEnableRunningTasksForDesktopMode()) return emptyList()
+        executeWithErrorLog({ "Failed call getRunningTasks" }) {
+            return recentTasks?.getRunningTasks(numTasks)?.asList() ?: emptyList()
+        }
+        return emptyList()
+    }
+
+    private fun shouldEnableRunningTasksForDesktopMode(): Boolean =
+        DesktopModeStatus.canEnterDesktopMode(context) &&
+            ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS.isTrue
+
+    private fun handleMessageAsync(msg: Message): Boolean {
+        return when (msg.what) {
+            MSG_SET_SHELF_HEIGHT -> {
+                setShelfHeightAsync(msg.arg1, msg.arg2)
+                true
+            }
+
+            MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT -> {
+                setLauncherKeepClearAreaHeight(msg.arg1, msg.arg2)
+                true
+            }
+
+            else -> false
+        }
+    }
+
+    //
+    // Desktop Mode
+    //
+    /** Call shell to show all apps active on the desktop */
+    fun showDesktopApps(displayId: Int, transition: RemoteTransition?) =
+        executeWithErrorLog({ "Failed call showDesktopApps" }) {
+            desktopMode?.showDesktopApps(displayId, transition)
+        }
+
+    /** If task with the given id is on the desktop, bring it to front */
+    fun showDesktopApp(taskId: Int, transition: RemoteTransition?) =
+        executeWithErrorLog({ "Failed call showDesktopApp" }) {
+            desktopMode?.showDesktopApp(taskId, transition)
+        }
+
+    /** Call shell to get number of visible freeform tasks */
+    fun getVisibleDesktopTaskCount(displayId: Int): Int {
+        executeWithErrorLog({ "Failed call getVisibleDesktopTaskCount" }) {
+            return desktopMode?.getVisibleTaskCount(displayId) ?: 0
+        }
+        return 0
+    }
+
+    /** Set a listener on shell to get updates about desktop task state */
+    fun setDesktopTaskListener(listener: IDesktopTaskListener?) {
+        desktopTaskListener = listener
+        executeWithErrorLog({ "Failed call setDesktopTaskListener" }) {
+            desktopMode?.setTaskListener(listener)
+        }
+    }
+
+    /** Perform cleanup transactions after animation to split select is complete */
+    fun onDesktopSplitSelectAnimComplete(taskInfo: RunningTaskInfo?) =
+        executeWithErrorLog({ "Failed call onDesktopSplitSelectAnimComplete" }) {
+            desktopMode?.onDesktopSplitSelectAnimComplete(taskInfo)
+        }
+
+    /** Call shell to move a task with given `taskId` to desktop */
+    fun moveToDesktop(
+        taskId: Int,
+        transitionSource: DesktopModeTransitionSource?,
+        transition: RemoteTransition?,
+    ) =
+        executeWithErrorLog({ "Failed call moveToDesktop" }) {
+            desktopMode?.moveToDesktop(taskId, transitionSource, transition)
+        }
+
+    /** Call shell to remove the desktop that is on given `displayId` */
+    fun removeDesktop(displayId: Int) =
+        executeWithErrorLog({ "Failed call removeDesktop" }) {
+            desktopMode?.removeDesktop(displayId)
+        }
+
+    /** Call shell to move a task with given `taskId` to external display. */
+    fun moveToExternalDisplay(taskId: Int) =
+        executeWithErrorLog({ "Failed call moveToExternalDisplay" }) {
+            desktopMode?.moveToExternalDisplay(taskId)
+        }
+
+    //
+    // Unfold transition
+    //
+    /** Sets the unfold animation lister to sysui. */
+    fun setUnfoldAnimationListener(callback: IUnfoldTransitionListener?) {
+        unfoldAnimationListener = callback
+        executeWithErrorLog({ "Failed call setUnfoldAnimationListener" }) {
+            unfoldAnimation?.setListener(callback)
+        }
+    }
+
+    //
+    // Recents
+    //
+    /** Starts the recents activity. The caller should manage the thread on which this is called. */
+    fun startRecentsActivity(
+        intent: Intent?,
+        options: ActivityOptions,
+        listener: RecentsAnimationListener,
+        useSyntheticRecentsTransition: Boolean,
+    ): Boolean {
+        executeWithErrorLog({ "Error starting recents via shell" }) {
+            recentTasks?.startRecentsTransition(
+                recentsPendingIntent,
+                intent,
+                options.toBundle().apply {
+                    if (useSyntheticRecentsTransition) {
+                        putBoolean("is_synthetic_recents_transition", true)
+                    }
+                },
+                context.iApplicationThread,
+                RecentsAnimationListenerStub(listener),
+            )
+                ?: run {
+                    ActiveGestureProtoLogProxy.logRecentTasksMissing()
+                    return false
+                }
+            return true
+        }
+        return false
+    }
+
+    private class RecentsAnimationListenerStub(val listener: RecentsAnimationListener) :
+        IRecentsAnimationRunner.Stub() {
+        override fun onAnimationStart(
+            controller: IRecentsAnimationController,
+            apps: Array<RemoteAnimationTarget>?,
+            wallpapers: Array<RemoteAnimationTarget>?,
+            homeContentInsets: Rect?,
+            minimizedHomeBounds: Rect?,
+            extras: Bundle?,
+        ) =
+            listener.onAnimationStart(
+                RecentsAnimationControllerCompat(controller),
+                apps,
+                wallpapers,
+                homeContentInsets,
+                minimizedHomeBounds,
+                extras?.apply {
+                    // Aidl bundles need to explicitly set class loader
+                    // https://developer.android.com/guide/components/aidl#Bundles
+                    classLoader = SplitBounds::class.java.classLoader
+                },
+            )
+
+        override fun onAnimationCanceled(taskIds: IntArray?, taskSnapshots: Array<TaskSnapshot>?) =
+            listener.onAnimationCanceled(wrap(taskIds, taskSnapshots))
+
+        override fun onTasksAppeared(apps: Array<RemoteAnimationTarget>?) =
+            listener.onTasksAppeared(apps)
+    }
+
+    //
+    // Drag and drop
+    //
+    /**
+     * For testing purposes. Returns `true` only if the shell drop target has shown and drawn and is
+     * ready to handle drag events and the subsequent drop.
+     */
+    fun isDragAndDropReady(): Boolean {
+        executeWithErrorLog({ "Error querying drag state" }) {
+            return dragAndDrop?.isReadyToHandleDrag ?: false
+        }
+        return false
+    }
+
+    fun dump(pw: PrintWriter) {
+        pw.println("$TAG:")
+
+        pw.println("\tmSystemUiProxy=$systemUiProxy")
+        pw.println("\tmPip=$pip")
+        pw.println("\tmPipAnimationListener=$pipAnimationListener")
+        pw.println("\tmBubbles=$bubbles")
+        pw.println("\tmBubblesListener=$bubblesListener")
+        pw.println("\tmSplitScreen=$splitScreen")
+        pw.println("\tmSplitScreenListener=$splitScreenListener")
+        pw.println("\tmSplitSelectListener=$splitSelectListener")
+        pw.println("\tmOneHanded=$oneHanded")
+        pw.println("\tmShellTransitions=$shellTransitions")
+        pw.println("\tmHomeVisibilityState=" + homeVisibilityState)
+        pw.println("\tmFocusState=" + focusState)
+        pw.println("\tmStartingWindow=$startingWindow")
+        pw.println("\tmStartingWindowListener=$startingWindowListener")
+        pw.println("\tmSysuiUnlockAnimationController=$sysuiUnlockAnimationController")
+        pw.println("\tmLauncherActivityClass=$launcherActivityClass")
+        pw.println("\tmLauncherUnlockAnimationController=$launcherUnlockAnimationController")
+        pw.println("\tmRecentTasks=$recentTasks")
+        pw.println("\tmRecentTasksListener=$recentTasksListener")
+        pw.println("\tmBackAnimation=$backAnimation")
+        pw.println("\tmBackToLauncherCallback=$backToLauncherCallback")
+        pw.println("\tmBackToLauncherRunner=$backToLauncherRunner")
+        pw.println("\tmDesktopMode=$desktopMode")
+        pw.println("\tmDesktopTaskListener=$desktopTaskListener")
+        pw.println("\tmUnfoldAnimation=$unfoldAnimation")
+        pw.println("\tmUnfoldAnimationListener=$unfoldAnimationListener")
+        pw.println("\tmDragAndDrop=$dragAndDrop")
+    }
+
+    /** Adds all interfaces held by this proxy to the bundle */
+    @VisibleForTesting
+    fun addAllInterfaces(out: Bundle) {
+        QuickStepContract.addInterface(systemUiProxy, out)
+        QuickStepContract.addInterface(pip, out)
+        QuickStepContract.addInterface(bubbles, out)
+        QuickStepContract.addInterface(sysuiUnlockAnimationController, out)
+        QuickStepContract.addInterface(splitScreen, out)
+        QuickStepContract.addInterface(oneHanded, out)
+        QuickStepContract.addInterface(shellTransitions, out)
+        QuickStepContract.addInterface(startingWindow, out)
+        QuickStepContract.addInterface(recentTasks, out)
+        QuickStepContract.addInterface(backAnimation, out)
+        QuickStepContract.addInterface(desktopMode, out)
+        QuickStepContract.addInterface(unfoldAnimation, out)
+        QuickStepContract.addInterface(dragAndDrop, out)
+    }
+
+    companion object {
+        private const val TAG = "SystemUiProxy"
+
+        @JvmField val INSTANCE = DaggerSingletonObject(LauncherAppComponent::getSystemUiProxy)
+
+        private const val MSG_SET_SHELF_HEIGHT = 1
+        private const val MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT = 2
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 2fcfa36..e0d4ddd 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -47,7 +47,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.taskbar.TaskbarUIController;
 import com.android.launcher3.util.DisplayController;
-import com.android.quickstep.fallback.window.RecentsWindowFactory;
+import com.android.quickstep.fallback.window.RecentsDisplayModel;
 import com.android.quickstep.fallback.window.RecentsWindowManager;
 import com.android.quickstep.util.ActiveGestureProtoLogProxy;
 import com.android.quickstep.util.SystemUiFlagUtils;
@@ -66,7 +66,6 @@
             SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
 
     private final Context mCtx;
-    private RecentsWindowFactory mRecentsWindowFactory;
     private RecentsAnimationController mController;
     private RecentsAnimationCallbacks mCallbacks;
     private RecentsAnimationTargets mTargets;
@@ -103,11 +102,9 @@
         }
     };
 
-    TaskAnimationManager(Context ctx, RecentsWindowFactory recentsWindowFactory,
-            RecentsAnimationDeviceState deviceState) {
+    TaskAnimationManager(Context ctx, RecentsAnimationDeviceState deviceState) {
         mCtx = ctx;
         mDeviceState = deviceState;
-        mRecentsWindowFactory = recentsWindowFactory;
     }
     SystemUiProxy getSystemUiProxy() {
         return SystemUiProxy.INSTANCE.get(mCtx);
@@ -303,7 +300,8 @@
                         || Flags.enableLauncherOverviewInWindow())) {
             mRecentsAnimationStartPending = getSystemUiProxy().startRecentsActivity(intent, options,
                     mCallbacks, gestureState.useSyntheticRecentsTransition());
-            mRecentsWindowFactory.create(mDeviceState.getDisplayId())
+            RecentsDisplayModel.getINSTANCE().get(mCtx)
+                    .getRecentsWindowManager(mDeviceState.getDisplayId())
                     .startRecentsWindow(mCallbacks);
         } else {
             options.setPendingIntentBackgroundActivityStartMode(
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 4ddbcdb..f5cc518 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -90,7 +90,7 @@
 import com.android.launcher3.util.ScreenOnTracker;
 import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.OverviewCommandHelper.CommandType;
-import com.android.quickstep.fallback.window.RecentsWindowFactory;
+import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
 import com.android.quickstep.fallback.window.RecentsWindowSwipeHandler;
 import com.android.quickstep.inputconsumers.BubbleBarInputConsumer;
 import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer;
@@ -104,7 +104,6 @@
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.statusbar.phone.BarTransitions;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.InputMonitorCompat;
@@ -125,7 +124,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
-import java.util.HashSet;
 import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -151,8 +149,6 @@
 
         private final WeakReference<TouchInteractionService> mTis;
 
-        private final Set<Runnable> mOnOverviewTargetChangeListeners = new HashSet<>();
-
         private TISBinder(TouchInteractionService tis) {
             mTis = new WeakReference<>(tis);
         }
@@ -309,10 +305,10 @@
         @BinderThread
         @Override
         public void updateWallpaperVisibility(int displayId, boolean visible) {
-            MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis ->
-                    executeForTaskbarManager(
-                            taskbarManager -> taskbarManager.setWallpaperVisible(visible))
-            ));
+            MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(
+                    tis -> executeForTaskbarManager(
+                            taskbarManager -> taskbarManager.setWallpaperVisible(displayId,
+                                    visible))));
         }
 
         @BinderThread
@@ -499,28 +495,11 @@
                     tis -> tis.mDeviceState.setGestureBlockingTaskId(taskId));
         }
 
-        /** Registers a listener to be run on Overview Target updates. */
-        public void registerOverviewTargetChangeListener(@NonNull Runnable listener) {
-            mOnOverviewTargetChangeListeners.add(listener);
-        }
-
-        /** Unregisters an OverviewTargetChange listener. */
-        public void unregisterOverviewTargetChangeListener(@NonNull Runnable listener) {
-            mOnOverviewTargetChangeListeners.remove(listener);
-        }
-
-        protected void onOverviewTargetChange() {
-            Set<Runnable> listeners = new HashSet<>(mOnOverviewTargetChangeListeners);
-            for (Runnable listener : listeners) {
-                listener.run();
-            }
-        }
-
         /** Refreshes the current overview target. */
         public void refreshOverviewTarget() {
             executeForTouchInteractionService(tis -> {
                 tis.mAllAppsActionManager.onDestroy();
-                tis.onOverviewTargetChange(tis.mOverviewComponentObserver.isHomeAndOverviewSame());
+                tis.onOverviewTargetChanged(tis.mOverviewComponentObserver.isHomeAndOverviewSame());
             });
         }
     }
@@ -602,6 +581,7 @@
     private final Runnable mUserUnlockedRunnable = this::onUserUnlocked;
 
     private final ScreenOnTracker.ScreenOnListener mScreenOnListener = this::onScreenOnChanged;
+    private final OverviewChangeListener mOverviewChangeListener = this::onOverviewTargetChanged;
 
     private final TaskbarNavButtonCallbacks mNavCallbacks = new TaskbarNavButtonCallbacks() {
         @Override
@@ -613,9 +593,13 @@
         public void onToggleOverview() {
             mOverviewCommandHelper.addCommand(CommandType.TOGGLE);
         }
+
+        @Override
+        public void onHideOverview() {
+            mOverviewCommandHelper.addCommand(CommandType.HIDE);
+        }
     };
 
-    private ActivityManagerWrapper mAM;
     private OverviewCommandHelper mOverviewCommandHelper;
     private OverviewComponentObserver mOverviewComponentObserver;
     private InputConsumerController mInputConsumer;
@@ -632,7 +616,6 @@
     private InputEventReceiver mInputEventReceiver;
 
     private TaskbarManager mTaskbarManager;
-    private RecentsWindowFactory mRecentsWindowFactory;
     private Function<GestureState, AnimatedFloat> mSwipeUpProxyProvider = i -> null;
     private AllAppsActionManager mAllAppsActionManager;
     private InputManager mInputManager;
@@ -651,7 +634,6 @@
         // Initialize anything here that is needed in direct boot mode.
         // Everything else should be initialized in onUserUnlocked() below.
         mMainChoreographer = Choreographer.getInstance();
-        mAM = ActivityManagerWrapper.getInstance();
         mDeviceState = new RecentsAnimationDeviceState(this, true);
         mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
         mAllAppsActionManager = new AllAppsActionManager(
@@ -669,9 +651,6 @@
         mDesktopAppLaunchTransitionManager =
                 new DesktopAppLaunchTransitionManager(this, SystemUiProxy.INSTANCE.get(this));
         mDesktopAppLaunchTransitionManager.registerTransitions();
-        if (Flags.enableLauncherOverviewInWindow() || Flags.enableFallbackOverviewInWindow()) {
-            mRecentsWindowFactory = new RecentsWindowFactory(this);
-        }
         mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
 
         // Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized.
@@ -723,8 +702,8 @@
     public void onUserUnlocked() {
         Log.d(TAG, "onUserUnlocked: userId=" + getUserId()
                 + " instance=" + System.identityHashCode(this));
-        mTaskAnimationManager = new TaskAnimationManager(this, mRecentsWindowFactory, mDeviceState);
-        mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
+        mTaskAnimationManager = new TaskAnimationManager(this, mDeviceState);
+        mOverviewComponentObserver = OverviewComponentObserver.INSTANCE.get(this);
         mOverviewCommandHelper = new OverviewCommandHelper(this,
                 mOverviewComponentObserver, mTaskAnimationManager);
         mResetGestureInputConsumer = new ResetGestureInputConsumer(
@@ -740,8 +719,8 @@
         // new ModelPreload().start(this);
         resetHomeBounceSeenOnQuickstepEnabledFirstTime();
 
-        mOverviewComponentObserver.setOverviewChangeListener(this::onOverviewTargetChange);
-        onOverviewTargetChange(mOverviewComponentObserver.isHomeAndOverviewSame());
+        mOverviewComponentObserver.addOverviewChangeListener(mOverviewChangeListener);
+        onOverviewTargetChanged(mOverviewComponentObserver.isHomeAndOverviewSame());
 
         mTaskbarManager.onUserUnlocked();
     }
@@ -766,7 +745,7 @@
         }
     }
 
-    private void onOverviewTargetChange(boolean isHomeAndOverviewSame) {
+    private void onOverviewTargetChanged(boolean isHomeAndOverviewSame) {
         mAllAppsActionManager.setHomeAndOverviewSame(isHomeAndOverviewSame);
         RecentsViewContainer newOverviewContainer =
                 mOverviewComponentObserver.getContainerInterface().getCreatedContainer();
@@ -778,7 +757,6 @@
                 mTaskbarManager.setRecentsViewContainer(newOverviewContainer);
             }
         }
-        mTISBinder.onOverviewTargetChange();
     }
 
     private PendingIntent createAllAppsPendingIntent() {
@@ -797,7 +775,7 @@
         if (LockedUserState.get(this).isUserUnlocked()) {
             long systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
             SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
-            mOverviewComponentObserver.onSystemUiStateChanged();
+            mOverviewComponentObserver.setHomeDisabled(mDeviceState.isHomeDisabled());
             mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags);
             mTaskAnimationManager.onSystemUiFlagsChanged(lastSysUIFlags, systemUiStateFlags);
         }
@@ -818,7 +796,8 @@
         sIsInitialized = false;
         if (LockedUserState.get(this).isUserUnlocked()) {
             mInputConsumer.unregisterInputConsumer();
-            mOverviewComponentObserver.onDestroy();
+            mOverviewComponentObserver.setHomeDisabled(false);
+            mOverviewComponentObserver.removeOverviewChangeListener(mOverviewChangeListener);
         }
         disposeEventHandlers("TouchInteractionService onDestroy()");
         mDeviceState.destroy();
@@ -830,10 +809,6 @@
         mTrackpadsConnected.clear();
 
         mTaskbarManager.destroy();
-
-        if (mRecentsWindowFactory != null) {
-            mRecentsWindowFactory.destroy();
-        }
         if (mDesktopAppLaunchTransitionManager != null) {
             mDesktopAppLaunchTransitionManager.unregisterTransitions();
         }
@@ -1289,6 +1264,6 @@
             GestureState gestureState, long touchTimeMs) {
         return new RecentsWindowSwipeHandler(this, mDeviceState, mTaskAnimationManager,
                 gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
-                mInputConsumer, mRecentsWindowFactory, MSDLPlayerWrapper.INSTANCE.get(this));
+                mInputConsumer, MSDLPlayerWrapper.INSTANCE.get(this));
     }
 }
diff --git a/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
index b2670e8..20a66dd 100644
--- a/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
+++ b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
@@ -19,7 +19,9 @@
 import com.android.launcher3.dagger.LauncherAppComponent;
 import com.android.launcher3.dagger.LauncherBaseAppComponent;
 import com.android.launcher3.model.WellbeingModel;
+import com.android.quickstep.OverviewComponentObserver;
 import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.fallback.window.RecentsDisplayModel;
 import com.android.quickstep.util.AsyncClockEventDelegate;
 
 /**
@@ -37,4 +39,8 @@
     AsyncClockEventDelegate getAsyncClockEventDelegate();
 
     SystemUiProxy getSystemUiProxy();
+
+    RecentsDisplayModel getRecentsDisplayModel();
+
+    OverviewComponentObserver getOverviewComponentObserver();
 }
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 980dee4..9625d29 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -43,9 +43,9 @@
 import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
 import com.android.quickstep.BaseContainerInterface;
 import com.android.quickstep.FallbackActivityInterface;
-import com.android.quickstep.FallbackWindowInterface;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.RotationTouchHelper;
+import com.android.quickstep.fallback.window.RecentsDisplayModel;
 import com.android.quickstep.util.GroupTask;
 import com.android.quickstep.util.SplitSelectStateController;
 import com.android.quickstep.util.TaskViewSimulator;
@@ -80,8 +80,8 @@
     @Override
     public BaseContainerInterface<RecentsState, ?> getContainerInterface(int displayId) {
         return (Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow())
-                ? FallbackWindowInterface.getInstance(displayId)
-                : FallbackActivityInterface.INSTANCE;
+                ? RecentsDisplayModel.getINSTANCE().get(mContext)
+                .getFallbackWindowInterface(displayId) : FallbackActivityInterface.INSTANCE;
     }
 
     @Override
@@ -264,13 +264,13 @@
         }
 
         setFreezeViewVisibility(true);
-        if (mContainer.getDesktopVisibilityController() != null) {
-            mContainer.getDesktopVisibilityController().onLauncherStateChanged(toState);
-        }
     }
 
     @Override
     public void onStateTransitionComplete(RecentsState finalState) {
+        if (mContainer.getDesktopVisibilityController() != null) {
+            mContainer.getDesktopVisibilityController().onLauncherStateChanged(finalState);
+        }
         if (!finalState.isRecentsViewVisible()) {
             // Clean-up logic that occurs when recents is no longer in use/visible.
             reset();
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
new file mode 100644
index 0000000..a9259d9
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2024 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.quickstep.fallback.window
+
+import android.content.Context
+import android.os.Handler
+import android.util.Log
+import android.view.Display
+import com.android.launcher3.Flags
+import com.android.launcher3.dagger.ApplicationContext
+import com.android.launcher3.dagger.LauncherAppSingleton
+import com.android.launcher3.util.DaggerSingletonObject
+import com.android.quickstep.DisplayModel
+import com.android.quickstep.FallbackWindowInterface
+import com.android.quickstep.dagger.QuickstepBaseAppComponent
+import com.android.quickstep.fallback.window.RecentsDisplayModel.RecentsDisplayResource
+import javax.inject.Inject
+
+@LauncherAppSingleton
+class RecentsDisplayModel @Inject constructor(@ApplicationContext context: Context) :
+    DisplayModel<RecentsDisplayResource>(context) {
+
+    companion object {
+        private const val TAG = "RecentsDisplayModel"
+        private const val DEBUG = false
+
+        @JvmStatic
+        val INSTANCE: DaggerSingletonObject<RecentsDisplayModel> =
+            DaggerSingletonObject<RecentsDisplayModel>(
+                QuickstepBaseAppComponent::getRecentsDisplayModel
+            )
+    }
+
+    init {
+        if (Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow()) {
+            displayManager.registerDisplayListener(displayListener, Handler.getMain())
+            createDisplayResource(Display.DEFAULT_DISPLAY)
+        }
+    }
+
+    override fun createDisplayResource(displayId: Int) {
+        if (DEBUG) Log.d(TAG, "create: displayId=$displayId")
+        getDisplayResource(displayId)?.let {
+            return
+        }
+        val display = displayManager.getDisplay(displayId)
+        displayResourceArray[displayId] =
+            RecentsDisplayResource(displayId, context.createDisplayContext(display))
+    }
+
+    fun getRecentsWindowManager(displayId: Int): RecentsWindowManager? {
+        return getDisplayResource(displayId)?.recentsWindowManager
+    }
+
+    fun getFallbackWindowInterface(displayId: Int): FallbackWindowInterface? {
+        return getDisplayResource(displayId)?.fallbackWindowInterface
+    }
+
+    data class RecentsDisplayResource(var displayId: Int, var displayContext: Context) :
+        DisplayResource() {
+        val recentsWindowManager = RecentsWindowManager(displayContext)
+        val fallbackWindowInterface: FallbackWindowInterface =
+            FallbackWindowInterface(recentsWindowManager)
+
+        override fun cleanup() {
+            recentsWindowManager.destroy()
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowFactory.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowFactory.kt
deleted file mode 100644
index ac0593e..0000000
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowFactory.kt
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2024 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.quickstep.fallback.window
-
-import android.content.Context
-import android.hardware.display.DisplayManager
-import android.os.Handler
-import android.util.Log
-import android.util.SparseArray
-import android.view.Display
-import androidx.core.util.valueIterator
-
-
-/**
- * Factory for creating [RecentsWindowManager] instances based on context per display.
- */
-class RecentsWindowFactory(private val context: Context) {
-
-    companion object {
-        private const val TAG = "RecentsWindowFactory"
-        private const val DEBUG = false
-    }
-
-    private val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
-    private val managerArray = SparseArray<RecentsWindowManager>()
-
-    private val displayListener: DisplayManager.DisplayListener =
-        (object : DisplayManager.DisplayListener {
-            override fun onDisplayAdded(displayId: Int) {
-                if (DEBUG) Log.d(TAG, "onDisplayAdded: displayId=$displayId")
-                create(displayId)
-            }
-
-            override fun onDisplayRemoved(displayId: Int) {
-                if (DEBUG) Log.d(TAG, "onDisplayRemoved: displayId=$displayId")
-                delete(displayId)
-            }
-
-            override fun onDisplayChanged(displayId: Int) {
-                if (DEBUG) Log.d(TAG, "onDisplayChanged: displayId=$displayId")
-            }
-        })
-
-    init {
-        create(Display.DEFAULT_DISPLAY) // create manager for first display early.
-        displayManager.registerDisplayListener(displayListener, Handler.getMain())
-    }
-
-    fun destroy() {
-        managerArray.valueIterator().forEach { manager ->
-            manager.destroy()
-        }
-        managerArray.clear()
-        displayManager.unregisterDisplayListener(displayListener)
-    }
-
-    fun get(displayId: Int): RecentsWindowManager? {
-        if (DEBUG) Log.d(TAG, "get: displayId=$displayId")
-        return managerArray[displayId]
-    }
-
-    fun delete(displayId: Int) {
-        if (DEBUG) Log.d(TAG, "delete: displayId=$displayId")
-        get(displayId)?.destroy()
-        managerArray.remove(displayId)
-    }
-
-    fun create(displayId: Int): RecentsWindowManager {
-        if (DEBUG) Log.d(TAG, "create: displayId=$displayId")
-        get(displayId)?.let {
-            return it
-        }
-        val display = displayManager.getDisplay(displayId)
-        val displayContext = context.createDisplayContext(display)
-        val recentsWindowManager = RecentsWindowManager(displayId, displayContext)
-        managerArray[displayId] = recentsWindowManager
-        return recentsWindowManager
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
index b3cc3e9..9bd7a19 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
@@ -50,7 +50,6 @@
 import com.android.launcher3.util.SystemUiController
 import com.android.launcher3.views.BaseDragLayer
 import com.android.launcher3.views.ScrimView
-import com.android.quickstep.FallbackWindowInterface
 import com.android.quickstep.OverviewComponentObserver
 import com.android.quickstep.RecentsAnimationCallbacks
 import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener
@@ -89,7 +88,7 @@
  * To add new protologs, see [RecentsWindowProtoLogProxy]. To enable logging to logcat, see
  * [QuickstepProtoLogGroup.Constants.DEBUG_RECENTS_WINDOW]
  */
-class RecentsWindowManager(private val displayId: Int, context: Context) :
+class RecentsWindowManager(context: Context) :
     RecentsWindowContext(context), RecentsViewContainer, StatefulContainer<RecentsState> {
 
     companion object {
@@ -98,6 +97,9 @@
 
         class RecentsWindowTracker : ContextTracker<RecentsWindowManager?>() {
             override fun isHomeStarted(context: RecentsWindowManager?): Boolean {
+                // if we need to change this block to use context in some way, we will need to
+                // refactor RecentsWindowTracker to be an instance (instead of a singleton) managed
+                // by RecentsDisplayModel. Otherwise bad things will occur.
                 return true
             }
         }
@@ -152,14 +154,12 @@
         }
 
     init {
-        FallbackWindowInterface.init(displayId, this)
         TaskStackChangeListeners.getInstance().registerTaskStackListener(taskStackChangeListener)
     }
 
     override fun destroy() {
         super.destroy()
         cleanupRecentsWindow()
-        FallbackWindowInterface.getInstance(displayId)?.destroy()
         TaskStackChangeListeners.getInstance().unregisterTaskStackListener(taskStackChangeListener)
         callbacks?.removeListener(recentsAnimationListener)
         recentsWindowTracker.onContextDestroyed(this)
@@ -322,10 +322,6 @@
         return taskbarUIController
     }
 
-    override fun getTISBindHelper(): TISBindHelper {
-        return tisBindHelper
-    }
-
     fun registerInitListener(onInitListener: Predicate<Boolean>) {
         this.onInitListener = onInitListener
     }
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
index 4a08d12..afc8879 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
@@ -101,6 +101,8 @@
     private static StaticMessageReceiver sMessageReceiver = null;
 
     private FallbackHomeAnimationFactory mActiveAnimationFactory;
+    private final RecentsDisplayModel mRecentsDisplayModel;
+
     private final boolean mRunningOverHome;
 
     private final Matrix mTmpMatrix = new Matrix();
@@ -111,10 +113,11 @@
     public RecentsWindowSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
             TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
             boolean continuingLastGesture, InputConsumerController inputConsumer,
-            RecentsWindowFactory recentsWindowFactory, MSDLPlayerWrapper msdlPlayerWrapper) {
+            MSDLPlayerWrapper msdlPlayerWrapper) {
         super(context, deviceState, taskAnimationManager, gestureState, touchTimeMs,
-                continuingLastGesture, inputConsumer, recentsWindowFactory, msdlPlayerWrapper);
+                continuingLastGesture, inputConsumer, msdlPlayerWrapper);
 
+        mRecentsDisplayModel = RecentsDisplayModel.getINSTANCE().get(context);
         mRunningOverHome = mGestureState.getRunningTask() != null
                 && mGestureState.getRunningTask().isHomeTask();
 
@@ -160,7 +163,8 @@
         boolean fromHomeToHome = mRunningOverHome
                 && endTarget == GestureState.GestureEndTarget.HOME;
         if (fromHomeToHome) {
-            RecentsWindowManager manager = mRecentsWindowFactory.get(mDeviceState.getDisplayId());
+            RecentsWindowManager manager =
+                    mRecentsDisplayModel.getRecentsWindowManager(mDeviceState.getDisplayId());
             if (manager != null) {
                 manager.startHome(/* finishRecentsAnimation= */ false);
             }
@@ -225,7 +229,7 @@
             recentsCallback = () -> {
                 callback.run();
                 RecentsWindowManager manager =
-                        mRecentsWindowFactory.get(mDeviceState.getDisplayId());
+                        mRecentsDisplayModel.getRecentsWindowManager(mDeviceState.getDisplayId());
                 if (manager != null) {
                     manager.startHome();
                 }
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index 99c2c1c..4995e77 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -70,6 +70,8 @@
 import com.android.launcher3.taskbar.TaskbarManager;
 import com.android.launcher3.util.Executors;
 import com.android.quickstep.GestureState;
+import com.android.quickstep.OverviewComponentObserver;
+import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
 import com.android.quickstep.TouchInteractionService.TISBinder;
 import com.android.quickstep.util.LottieAnimationColorUtils;
 import com.android.quickstep.util.TISBindHelper;
@@ -121,7 +123,7 @@
 
     private TextView mHintView;
 
-    private final Runnable mOverviewTargetChangeRunnable = this::onOverviewTargetChanged;
+    private final OverviewChangeListener mOverviewChangeListener = this::onOverviewTargetChange;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -197,6 +199,9 @@
 
         setUpBackgroundAnimation(getDP().isTablet);
         getIDP().addOnChangeListener(mOnIDPChangeListener);
+
+        OverviewComponentObserver.INSTANCE.get(this)
+                .addOverviewChangeListener(mOverviewChangeListener);
     }
 
     private InvariantDeviceProfile getIDP() {
@@ -286,7 +291,6 @@
     private void onTISConnected(TISBinder binder) {
         setSetupUIVisible(isResumed());
         binder.setSwipeUpProxy(isResumed() ? this::createSwipeUpProxy : null);
-        binder.registerOverviewTargetChangeListener(mOverviewTargetChangeRunnable);
         binder.preloadOverviewForSUWAllSet();
         TaskbarManager taskbarManager = binder.getTaskbarManager();
         if (taskbarManager != null) {
@@ -294,11 +298,10 @@
         }
     }
 
-    private void onOverviewTargetChanged() {
+    private void onOverviewTargetChange(boolean isHomeAndOverviewSame) {
         TISBinder binder = mTISBindHelper.getBinder();
         if (binder != null) {
             binder.preloadOverviewForSUWAllSet();
-            binder.unregisterOverviewTargetChangeListener(mOverviewTargetChangeRunnable);
         }
     }
 
@@ -318,7 +321,6 @@
         if (binder != null) {
             setSetupUIVisible(false);
             binder.setSwipeUpProxy(null);
-            binder.unregisterOverviewTargetChangeListener(mOverviewTargetChangeRunnable);
         }
     }
 
@@ -346,6 +348,8 @@
         if (!isChangingConfigurations()) {
             dispatchLauncherAnimStartEnd();
         }
+        OverviewComponentObserver.INSTANCE.get(this)
+                .removeOverviewChangeListener(mOverviewChangeListener);
     }
 
     private AnimatedFloat createSwipeUpProxy(GestureState state) {
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index be7f8e5..7fe4278 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -65,11 +65,6 @@
     }
 
     @Override
-    public int getSpokenIntroductionSubtitle() {
-        return R.string.back_gesture_spoken_intro_subtitle;
-    }
-
-    @Override
     public int getSuccessFeedbackTitle() {
         return R.string.gesture_tutorial_nice;
     }
diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java
index 700fbf8..72bff7f 100644
--- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java
@@ -29,9 +29,9 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.testing.shared.ResourceUtils;
-import com.android.launcher3.util.DisplayController;
 
 /**
  * Utility class to handle edge swipes for back gestures.
@@ -45,6 +45,7 @@
             "gestures.back_timeout", 250);
 
     private final Context mContext;
+    private final DeviceProfile mDeviceProfile;
 
     private final Point mDisplaySize = new Point();
 
@@ -89,9 +90,10 @@
                 }
             };
 
-    EdgeBackGestureHandler(Context context) {
+    EdgeBackGestureHandler(Context context, DeviceProfile deviceProfile) {
         final Resources res = context.getResources();
         mContext = context;
+        mDeviceProfile = deviceProfile;
 
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         mLongPressTimeout = Math.min(MAX_LONG_PRESS_TIMEOUT,
@@ -116,8 +118,7 @@
             // Add a nav bar panel window.
             mEdgeBackPanel = new EdgeBackGesturePanel(mContext, parent, createLayoutParams());
             mEdgeBackPanel.setBackCallback(mBackCallback);
-            Point currentSize = DisplayController.INSTANCE.get(mContext).getInfo().currentSize;
-            mDisplaySize.set(currentSize.x, currentSize.y);
+            mDisplaySize.set(mDeviceProfile.widthPx, mDeviceProfile.heightPx);
             mEdgeBackPanel.setDisplaySize(mDisplaySize);
         }
     }
diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index bc5cc15..0365f89 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -115,22 +115,13 @@
 
     private void initWindowInsets() {
         View root = findViewById(android.R.id.content);
-        root.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
-            @Override
-            public void onLayoutChange(View v, int left, int top, int right, int bottom,
-                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
-                updateExclusionRects(root);
-            }
-        });
+        root.addOnLayoutChangeListener(
+                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
+                        updateExclusionRects(root));
 
         // Return CONSUMED if you don't want want the window insets to keep being
         // passed down to descendant views.
-        root.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
-            @Override
-            public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
-                return WindowInsets.CONSUMED;
-            }
-        });
+        root.setOnApplyWindowInsetsListener((v, insets) -> WindowInsets.CONSUMED);
     }
 
     private void updateExclusionRects(View rootView) {
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
index bf4eaf2..b059695 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
@@ -58,11 +58,6 @@
     }
 
     @Override
-    public int getSpokenIntroductionSubtitle() {
-        return R.string.home_gesture_spoken_intro_subtitle;
-    }
-
-    @Override
     public int getSuccessFeedbackTitle() {
         return R.string.home_gesture_tutorial_success;
     }
diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
index c00f508..fdbd509 100644
--- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
@@ -33,6 +33,7 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.NavigationMode;
@@ -57,15 +58,16 @@
     @Nullable
     private NavBarGestureAttemptCallback mGestureCallback;
 
-    NavBarGestureHandler(Context context) {
+    NavBarGestureHandler(Context context, DeviceProfile deviceProfile) {
         mContext = context;
-        DisplayController.Info displayInfo = DisplayController.INSTANCE.get(mContext).getInfo();
-        Point currentSize = displayInfo.currentSize;
-        mDisplaySize.set(currentSize.x, currentSize.y);
-        mSwipeUpTouchTracker =
-                new TriggerSwipeUpTouchTracker(context, true /*disableHorizontalSwipe*/,
-                        new NavBarPosition(NavigationMode.NO_BUTTON, displayInfo),
-                        this);
+        mDisplaySize.set(deviceProfile.widthPx, deviceProfile.heightPx);
+        mSwipeUpTouchTracker = new TriggerSwipeUpTouchTracker(
+                context,
+                /* disableHorizontalSwipe= */ true,
+                new NavBarPosition(
+                        NavigationMode.NO_BUTTON,
+                        DisplayController.INSTANCE.get(mContext).getInfo()),
+                /* onSwipeUp= */ this);
         mMotionPauseDetector = new MotionPauseDetector(context);
 
         final Resources resources = context.getResources();
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index e45f8d8..ff0d6d1 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -73,11 +73,6 @@
     }
 
     @Override
-    public int getSpokenIntroductionSubtitle() {
-        return R.string.overview_gesture_spoken_intro_subtitle;
-    }
-
-    @Override
     public int getSuccessFeedbackTitle() {
         return R.string.overview_gesture_tutorial_success;
     }
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index e462706..1c4e7a7 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -86,10 +86,8 @@
     SwipeUpGestureTutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) {
         super(tutorialFragment, tutorialType);
         RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(mContext);
-        OverviewComponentObserver observer = new OverviewComponentObserver(mContext, deviceState);
         mTaskViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, deviceState,
-                new GestureState(observer, -1));
-        observer.onDestroy();
+                new GestureState(OverviewComponentObserver.INSTANCE.get(mContext), -1));
         deviceState.destroy();
 
         DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext)
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 9510a05..7d14a3e 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -318,11 +318,6 @@
     }
 
     @StringRes
-    public int getSpokenIntroductionSubtitle() {
-        return NO_ID;
-    }
-
-    @StringRes
     public int getSuccessFeedbackSubtitle() {
         return NO_ID;
     }
@@ -401,17 +396,13 @@
                 isGestureSuccessful
                         ? getSuccessFeedbackTitle() : R.string.gesture_tutorial_try_again,
                 subtitleResId,
-                NO_ID,
-                isGestureSuccessful,
-                false);
+                isGestureSuccessful);
     }
 
     void showFeedback(
             int titleResId,
             int subtitleResId,
-            int spokenSubtitleResId,
-            boolean isGestureSuccessful,
-            boolean useGestureAnimationDelay) {
+            boolean isGestureSuccessful) {
         mFeedbackTitleView.removeCallbacks(mTitleViewCallback);
         if (mFeedbackViewCallback != null) {
             mFeedbackView.removeCallbacks(mFeedbackViewCallback);
@@ -512,7 +503,6 @@
             if (mTutorialType == TutorialType.BACK_NAVIGATION) {
                 resetViewsForBackGesture();
             }
-
         }
 
         mGestureCompleted = false;
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index 2ff2c83..8174e13 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -48,6 +48,7 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
@@ -175,8 +176,10 @@
         Bundle args = savedInstanceState != null ? savedInstanceState : getArguments();
         mTutorialType = (TutorialType) args.getSerializable(KEY_TUTORIAL_TYPE);
         mGestureComplete = args.getBoolean(KEY_GESTURE_COMPLETE, false);
-        mEdgeBackGestureHandler = new EdgeBackGestureHandler(getContext());
-        mNavBarGestureHandler = new NavBarGestureHandler(getContext());
+        DeviceProfile deviceProfile = LauncherAppState.getInstance(getContext())
+                .getInvariantDeviceProfile().getDeviceProfile(getContext());
+        mEdgeBackGestureHandler = new EdgeBackGestureHandler(getContext(), deviceProfile);
+        mNavBarGestureHandler = new NavBarGestureHandler(getContext(), deviceProfile);
 
         mDeviceProfile = InvariantDeviceProfile.INSTANCE.get(getContext())
                 .getDeviceProfile(getContext());
@@ -212,7 +215,6 @@
     public View onCreateView(
             @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         super.onCreateView(inflater, container, savedInstanceState);
-
         mRootView = (RootSandboxLayout) inflater.inflate(
                 R.layout.redesigned_gesture_tutorial_fragment,
                 container,
@@ -268,9 +270,7 @@
             mTutorialController.showFeedback(
                     introTitleResId,
                     introSubtitleResId,
-                    mTutorialController.getSpokenIntroductionSubtitle(),
-                    false,
-                    true);
+                    /* isGestureSuccessful= */ false);
             mIntroductionShown = true;
         }
     }
diff --git a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
index 8c26d8f..a315775 100644
--- a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
@@ -51,17 +51,32 @@
     override fun getAllTaskData(forceRefresh: Boolean): Flow<List<Task>> {
         if (forceRefresh) {
             recentsModel.getTasks { result ->
-                tasks.value =
-                    MapForStateFlow(
-                        result
-                            .flatMap { groupTask -> groupTask.tasks }
-                            .associateBy { it.key.id }
-                            .also {
-                                // Clean tasks that are not in the latest group tasks list.
-                                val tasksNoLongerVisible = it.keys.subtract(tasks.value.keys)
-                                removeTasks(tasksNoLongerVisible)
+                val recentTasks =
+                    result
+                        .flatMap { groupTask -> groupTask.tasks }
+                        .associateBy { it.key.id }
+                        .also { hashMap ->
+                            // Clean tasks that are not in the latest group tasks list.
+                            val tasksNoLongerVisible = hashMap.keys.subtract(tasks.value.keys)
+                            removeTasks(tasksNoLongerVisible)
+
+                            // Use pre-loaded thumbnail data and icon from the previous list.
+                            // This reduces the Thumbnail loading time in the Overview and prevent
+                            // empty thumbnail and icon.
+                            val cache =
+                                taskRequests.keys
+                                    .mapNotNull { key ->
+                                        val task = tasks.value[key] ?: return@mapNotNull null
+                                        key to Pair(task.thumbnail, task.icon)
+                                    }
+                                    .toMap()
+
+                            hashMap.values.forEach { task ->
+                                task.thumbnail = task.thumbnail ?: cache[task.key.id]?.first
+                                task.icon = task.icon ?: cache[task.key.id]?.second
                             }
-                    )
+                        }
+                tasks.value = MapForStateFlow(recentTasks)
             }
         }
         return tasks.map { it.values.toList() }
diff --git a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
index 95ecbe9..9d8fc4f 100644
--- a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
+++ b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
@@ -243,13 +243,17 @@
         private const val DEFAULT_SCOPE_ID = "RecentsDependencies::GlobalScope"
         private const val TAG = "RecentsDependencies"
         private const val DEBUG = false
+        private var activeRecentsCount = 0
 
         @Volatile private lateinit var instance: RecentsDependencies
 
         fun initialize(view: View): RecentsDependencies = initialize(view.context)
 
         fun initialize(context: Context): RecentsDependencies {
-            synchronized(this) { instance = RecentsDependencies(context.applicationContext) }
+            synchronized(this) {
+                activeRecentsCount++
+                instance = RecentsDependencies(context.applicationContext)
+            }
             return instance
         }
 
@@ -263,9 +267,29 @@
             return instance
         }
 
+        @JvmStatic
         fun destroy() {
-            instance.scopes.clear()
-            instance.startDefaultScope(instance.appContext)
+            // When Launcher Activity restarts, the old view's RecentsView.onDetachedFromWindow
+            // happens after the new view's creation. This means that destroy can be called after a
+            // new initialisation. This check prevents a newly initialised tree from being
+            // destroyed. Ideally we would have 1 instance of the dependency tree for each
+            // RecentsView.
+            //
+            // This check is sufficient to avoid a leak of the dependency tree after the Activity is
+            // destroyed while also allowing Launcher auto-restarts (production behaviour) to easily
+            // reinitialise the dependency tree.
+            //
+            // TODO(b/353917593): Better lifecycle decisions will be implemented in this bug or when
+            //  replacing with Dagger (b/371370483).
+            activeRecentsCount--
+            if (activeRecentsCount == 0) {
+                instance.scopes.clear()
+            } else {
+                instance.log(
+                    "RecentsDependencies was not destroyed. " +
+                        "There is still an active RecentsView instance."
+                )
+            }
         }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt b/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
index 3be8ea6..724fa40 100644
--- a/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
+++ b/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
@@ -34,7 +34,6 @@
 import com.android.quickstep.BaseContainerInterface
 import com.android.quickstep.DeviceConfigWrapper
 import com.android.quickstep.OverviewComponentObserver
-import com.android.quickstep.RecentsAnimationDeviceState
 import com.android.quickstep.SystemUiProxy
 import com.android.quickstep.TopTaskTracker
 import com.android.quickstep.views.RecentsView
@@ -212,14 +211,7 @@
 
     @VisibleForTesting
     fun getRecentsContainerInterface(): BaseContainerInterface<*, *>? {
-        val rads = RecentsAnimationDeviceState(context)
-        val observer = OverviewComponentObserver(context, rads)
-        try {
-            return observer.containerInterface
-        } finally {
-            observer.onDestroy()
-            rads.destroy()
-        }
+        return OverviewComponentObserver.INSTANCE.get(context).containerInterface
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 1eb91ae..c524286 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -583,10 +583,8 @@
     /**
      * Init {@code SplitFromDesktopController}
      */
-    public void initSplitFromDesktopController(QuickstepLauncher launcher,
-            OverviewComponentObserver overviewComponentObserver) {
-        initSplitFromDesktopController(
-                new SplitFromDesktopController(launcher, overviewComponentObserver));
+    public void initSplitFromDesktopController(QuickstepLauncher launcher) {
+        initSplitFromDesktopController(new SplitFromDesktopController(launcher));
     }
 
     @VisibleForTesting
@@ -853,10 +851,9 @@
         private DesktopSplitSelectListenerImpl mSplitSelectListener;
         private Drawable mAppIcon;
 
-        public SplitFromDesktopController(QuickstepLauncher launcher,
-                OverviewComponentObserver overviewComponentObserver) {
+        public SplitFromDesktopController(QuickstepLauncher launcher) {
             mLauncher = launcher;
-            mOverviewComponentObserver = overviewComponentObserver;
+            mOverviewComponentObserver = OverviewComponentObserver.INSTANCE.get(launcher);
             mSplitPlaceholderSize = mLauncher.getResources().getDimensionPixelSize(
                     R.dimen.split_placeholder_size);
             mSplitPlaceholderInset = mLauncher.getResources().getDimensionPixelSize(
diff --git a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
index 744c08c..0ba4083 100644
--- a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
@@ -63,12 +63,11 @@
 
     public SplitWithKeyboardShortcutController(QuickstepLauncher launcher,
             SplitSelectStateController controller,
-            OverviewComponentObserver overviewComponentObserver,
             RecentsAnimationDeviceState deviceState) {
         mLauncher = launcher;
         mController = controller;
         mDeviceState = deviceState;
-        mOverviewComponentObserver = overviewComponentObserver;
+        mOverviewComponentObserver = OverviewComponentObserver.INSTANCE.get(launcher);
 
         mSplitPlaceholderSize = mLauncher.getResources().getDimensionPixelSize(
                 R.dimen.split_placeholder_size);
@@ -106,7 +105,6 @@
     }
 
     public void onDestroy() {
-        mOverviewComponentObserver.onDestroy();
         mDeviceState.destroy();
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 4f823e6..b9f44fe 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -164,13 +164,14 @@
         }
 
         setFreezeViewVisibility(true);
-        if (mContainer.getDesktopVisibilityController() != null) {
-            mContainer.getDesktopVisibilityController().onLauncherStateChanged(toState);
-        }
     }
 
     @Override
     public void onStateTransitionComplete(LauncherState finalState) {
+        if (mContainer.getDesktopVisibilityController() != null) {
+            mContainer.getDesktopVisibilityController().onLauncherStateChanged(finalState);
+        }
+
         if (!finalState.isRecentsViewVisible) {
             // Clean-up logic that occurs when recents is no longer in use/visible.
             reset();
@@ -287,7 +288,7 @@
             desktopVisibilityController = mContainer.getDesktopVisibilityController();
             endTarget = mCurrentGestureEndTarget;
             if (endTarget == GestureState.GestureEndTarget.LAST_TASK
-                    && desktopVisibilityController.areDesktopTasksVisible()) {
+                    && desktopVisibilityController.areDesktopTasksVisibleAndNotInOverview()) {
                 // Recents gesture was cancelled and we are returning to the previous task.
                 // After super class has handled clean up, show desktop apps on top again
                 showDesktopApps = true;
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index fca115e..a43c686 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -252,6 +252,7 @@
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
+import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
@@ -611,7 +612,8 @@
     private int mKeyboardTaskFocusSnapAnimationDuration;
     private int mKeyboardTaskFocusIndex = INVALID_PAGE;
 
-    private int[] mDismissPrimaryTranslations;
+    private Map<TaskView, Integer> mTaskViewsDismissPrimaryTranslations =
+            new HashMap<TaskView, Integer>();
 
     /**
      * TODO: Call reloadIdNeeded in onTaskStackChanged.
@@ -855,6 +857,19 @@
     private final TaskViewsIterable mTaskViewsIterable = new TaskViewsIterable();
 
     public class TaskViewsIterable implements Iterable<TaskView> {
+
+        /**
+         * Iterates TaskViews when its index inside the RecentsView is needed.
+         */
+        public void forEachWithIndexInParent(BiConsumer<TaskView, Integer> consumer) {
+            int childCount = getChildCount();
+            for (int index = 0; index < childCount; index++) {
+                if (getChildAt(index) instanceof TaskView taskView) {
+                    consumer.accept(taskView, index);
+                }
+            }
+        }
+
         @Override
         public TaskViewsIterator iterator() {
             return new TaskViewsIterator();
@@ -1296,6 +1311,7 @@
         reset();
         if (enableRefactorTaskThumbnail()) {
             mHelper.onDetachedFromWindow();
+            RecentsDependencies.destroy();
         }
     }
 
@@ -1529,6 +1545,19 @@
         return clearAllScroll + (mIsRtl ? distance : -distance);
     }
 
+    /**
+     * Launch running task view if it is instance of DesktopTaskView.
+     * @return provides runnable list to attach runnable at end of Desktop Mode launch
+     */
+    @Nullable
+    public RunnableList launchRunningDesktopTaskView() {
+        TaskView taskView = getRunningTaskView();
+        if (taskView instanceof DesktopTaskView) {
+            return taskView.launchWithAnimation();
+        }
+        return null;
+    }
+
     /*
      * Returns if TaskView is within screen bounds defined in [screenStart, screenEnd].
      *
@@ -2518,41 +2547,39 @@
             return;
         }
 
-        int lower = 0;
-        int upper = 0;
-        int visibleStart = 0;
-        int visibleEnd = 0;
+        int lowerIndex, upperIndex, visibleStart, visibleEnd;
         if (showAsGrid()) {
             int screenStart = getPagedOrientationHandler().getPrimaryScroll(this);
             int pageOrientedSize = getPagedOrientationHandler().getMeasuredSize(this);
             // For GRID_ONLY_OVERVIEW, use +/- 1 task column as visible area for preloading
             // adjacent thumbnails, otherwise use +/-50% screen width
-            int extraWidth = enableGridOnlyOverview() ? getLastComputedTaskSize().width()
-                    + getPageSpacing() : pageOrientedSize / 2;
+            int extraWidth =
+                    enableGridOnlyOverview() ? getLastComputedTaskSize().width() + getPageSpacing()
+                            : pageOrientedSize / 2;
+            lowerIndex = upperIndex = 0;
             visibleStart = screenStart - extraWidth;
             visibleEnd = screenStart + pageOrientedSize + extraWidth;
         } else {
             int centerPageIndex = getPageNearestToCenterOfScreen();
             int numChildren = getChildCount();
-            lower = Math.max(0, centerPageIndex - 2);
-            upper = Math.min(centerPageIndex + 2, numChildren - 1);
+            lowerIndex = Math.max(0, centerPageIndex - 2);
+            upperIndex = Math.min(centerPageIndex + 2, numChildren - 1);
+            visibleStart = visibleEnd = 0;
         }
 
         List<Integer> visibleTaskIds = new ArrayList<>();
-
         // Update the task data for the in/visible children
-        for (int i = 0; i < getTaskViewCount(); i++) {
-            TaskView taskView = requireTaskViewAt(i);
+        getTaskViews().forEachWithIndexInParent((taskView, index) -> {
             List<TaskContainer> containers = taskView.getTaskContainers();
             if (containers.isEmpty()) {
-                continue;
+                return;
             }
             boolean visible;
             if (showAsGrid()) {
                 visible = isTaskViewWithinBounds(taskView, visibleStart, visibleEnd,
-                        mDismissPrimaryTranslations != null ? mDismissPrimaryTranslations[i] : 0);
+                        mTaskViewsDismissPrimaryTranslations.getOrDefault(taskView, 0));
             } else {
-                visible = lower <= i && i <= upper;
+                visible = index >= lowerIndex && index <= upperIndex;
             }
             if (visible) {
                 // Default update all non-null tasks, then remove running ones
@@ -2564,7 +2591,7 @@
                             tasksToUpdate.stream().map((task) -> task.key.id).toList());
                 }
                 if (tasksToUpdate.isEmpty()) {
-                    continue;
+                    return;
                 }
                 int visibilityChanges = 0;
                 for (Task task : tasksToUpdate) {
@@ -2598,7 +2625,7 @@
                     taskView.onTaskListVisibilityChanged(false /* visible */, visibilityChanges);
                 }
             }
-        }
+        });
         if (enableRefactorTaskThumbnail()) {
             mRecentsViewModel.updateVisibleTasks(visibleTaskIds);
         }
@@ -3921,7 +3948,7 @@
             slidingTranslation += mIsRtl ? newClearAllShortTotalWidthTranslation
                     : -newClearAllShortTotalWidthTranslation;
         }
-        mDismissPrimaryTranslations = new int[taskCount];
+        mTaskViewsDismissPrimaryTranslations.clear();
         int lastTaskViewIndex = indexOfChild(mUtils.getLastTaskView());
         for (int i = 0; i < count; i++) {
             View child = getChildAt(i);
@@ -4033,7 +4060,7 @@
                             clampToProgress(dismissInterpolator, animationStartProgress,
                                     animationEndProgress));
                     anim.add(dismissAnimator);
-                    mDismissPrimaryTranslations[i] = (int) finalTranslation;
+                    mTaskViewsDismissPrimaryTranslations.put(taskView, (int) finalTranslation);
                     distanceFromDismissedTask++;
                 }
             }
@@ -4163,7 +4190,7 @@
                                 mCurrentPageScrollDiff = primaryScroll - currentPageScroll;
                             }
                         }
-                    } else if (dismissedIndex < pageToSnapTo || pageToSnapTo == taskCount - 1) {
+                    } else if (dismissedIndex < pageToSnapTo || pageToSnapTo == lastTaskViewIndex) {
                         pageToSnapTo--;
                     }
                     boolean isHomeTaskDismissed = dismissedTaskView == getHomeTaskView();
@@ -4269,7 +4296,7 @@
                 updateCurrentTaskActionsVisibility();
                 onDismissAnimationEnds();
                 mPendingAnimation = null;
-                mDismissPrimaryTranslations = null;
+                mTaskViewsDismissPrimaryTranslations.clear();
             }
         });
     }
@@ -4344,7 +4371,7 @@
         );
 
         if (view instanceof TaskView) {
-            mDismissPrimaryTranslations[index] = scrollDiffPerPage;
+            mTaskViewsDismissPrimaryTranslations.put((TaskView) view, scrollDiffPerPage);
         }
         if (mEnableDrawingLiveTile && view instanceof TaskView
                 && ((TaskView) view).isRunningTask()) {
@@ -5212,20 +5239,16 @@
         SplitAnimationTimings timings = AnimUtils.getDeviceOverviewToSplitTimings(
                 mContainer.getDeviceProfile().isTablet);
         if (enableLargeDesktopWindowingTile()) {
-            TaskView currentPageTaskView = getCurrentPageTaskView();
-            TaskView nextPageTaskView = getTaskViewAt(mCurrentPage + 1);
-            TaskView previousPageTaskView = getTaskViewAt(mCurrentPage - 1);
-            for (TaskView taskView : getTaskViews()) {
+            getTaskViews().forEachWithIndexInParent((taskView, index) -> {
                 if (taskView instanceof DesktopTaskView) {
                     // Setting pivot to scale down from screen centre.
-                    if (taskView == previousPageTaskView || taskView == currentPageTaskView
-                            || taskView == nextPageTaskView) {
+                    if (isTaskViewVisible(taskView)) {
                         float pivotX = 0f;
-                        if (taskView == previousPageTaskView) {
+                        if (index < mCurrentPage) {
                             pivotX = mIsRtl ? taskView.getWidth() / 2f - mPageSpacing
                                     - taskView.getWidth()
                                     : taskView.getWidth() / 2f + mPageSpacing + taskView.getWidth();
-                        } else if (taskView == currentPageTaskView) {
+                        } else if (index == mCurrentPage) {
                             pivotX = taskView.getWidth() / 2f;
                         } else {
                             pivotX = mIsRtl ? taskView.getWidth() + mPageSpacing
@@ -5244,7 +5267,7 @@
                             clampToProgress(deskTopFadeInterPolator, 0f,
                                     timings.getDesktopFadeSplitAnimationEndOffset()));
                 }
-            }
+            });
         }
     }
 
@@ -5824,19 +5847,19 @@
 
     @Override
     public void addChildrenForAccessibility(ArrayList<View> outChildren) {
-        // Add children in reverse order
-        for (int i = getChildCount() - 1; i >= 0; --i) {
-            outChildren.add(getChildAt(i));
-        }
+        outChildren.addAll(getAccessibilityChildren());
+    }
+
+    public List<View> getAccessibilityChildren() {
+        return mUtils.getAccessibilityChildren();
     }
 
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
         final AccessibilityNodeInfo.CollectionInfo
-                collectionInfo = AccessibilityNodeInfo.CollectionInfo.obtain(
-                1, getTaskViewCount(), false,
-                AccessibilityNodeInfo.CollectionInfo.SELECTION_MODE_NONE);
+                collectionInfo = new AccessibilityNodeInfo.CollectionInfo(
+                1, getAccessibilityChildren().size(), false);
         info.setCollectionInfo(collectionInfo);
     }
 
@@ -5845,14 +5868,12 @@
         super.onInitializeAccessibilityEvent(event);
         event.setScrollable(hasTaskViews());
 
-        // TODO(b/379942019): Revisit the logic below to make sure it does not rely on the
-        //  `taskViewCount` to update the indices or make it indices free.
-        final int taskViewCount = getTaskViewCount();
         if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
+            final List<View> accessibilityChildren = getAccessibilityChildren();
             final int[] visibleTasks = getVisibleChildrenRange();
-            event.setFromIndex(taskViewCount - visibleTasks[1]);
-            event.setToIndex(taskViewCount - visibleTasks[0]);
-            event.setItemCount(taskViewCount);
+            event.setFromIndex(accessibilityChildren.indexOf(getChildAt(visibleTasks[1])));
+            event.setToIndex(accessibilityChildren.indexOf(getChildAt(visibleTasks[0])));
+            event.setItemCount(accessibilityChildren.size());
         }
     }
 
@@ -6156,44 +6177,34 @@
             mClearAllButton.setScrollOffsetPrimary(mIsRtl ? clearAllWidthDiff : -clearAllWidthDiff);
         }
 
-        boolean pageScrollChanged = false;
-
+        int[] oldPageScrolls = Arrays.copyOf(outPageScrolls, outPageScrolls.length);
         int clearAllIndex = indexOfChild(mClearAllButton);
         int clearAllScroll = 0;
         int clearAllWidth = getPagedOrientationHandler().getPrimarySize(mClearAllButton);
         if (clearAllIndex != -1 && clearAllIndex < outPageScrolls.length) {
             float scrollDiff = mClearAllButton.getScrollAdjustment(showAsFullscreen, showAsGrid);
-            clearAllScroll = newPageScrolls[clearAllIndex] + (int) scrollDiff;
-            if (outPageScrolls[clearAllIndex] != clearAllScroll) {
-                pageScrollChanged = true;
-                outPageScrolls[clearAllIndex] = clearAllScroll;
-            }
+            clearAllScroll = newPageScrolls[clearAllIndex] + Math.round(scrollDiff);
+            outPageScrolls[clearAllIndex] = clearAllScroll;
         }
 
         int lastTaskScroll = getLastTaskScroll(clearAllScroll, clearAllWidth);
-        for (int i = 0; i < getChildCount(); i++) {
-            TaskView taskView = getTaskViewAt(i);
-            if (taskView == null) {
-                continue;
-            }
+        getTaskViews().forEachWithIndexInParent((taskView, index) -> {
             float scrollDiff = taskView.getScrollAdjustment(showAsGrid);
-            int pageScroll = newPageScrolls[i] + Math.round(scrollDiff);
+            int pageScroll = newPageScrolls[index] + Math.round(scrollDiff);
             if ((mIsRtl && pageScroll < lastTaskScroll)
                     || (!mIsRtl && pageScroll > lastTaskScroll)) {
                 pageScroll = lastTaskScroll;
             }
-            if (outPageScrolls[i] != pageScroll) {
-                pageScrollChanged = true;
-                outPageScrolls[i] = pageScroll;
-            }
+            outPageScrolls[index] = pageScroll;
             if (DEBUG) {
-                Log.d(TAG, "getPageScrolls - outPageScrolls[" + i + "]: " + outPageScrolls[i]);
+                Log.d(TAG,
+                        "getPageScrolls - outPageScrolls[" + index + "]: " + outPageScrolls[index]);
             }
-        }
+        });
         if (DEBUG) {
             Log.d(TAG, "getPageScrolls - clearAllScroll: " + clearAllScroll);
         }
-        return pageScrollChanged;
+        return !Arrays.equals(oldPageScrolls, outPageScrolls);
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
index b04753b..a1d22fe 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
@@ -25,7 +25,6 @@
 import android.view.View;
 import android.view.Window;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.BaseActivity;
@@ -34,7 +33,6 @@
 import com.android.launcher3.taskbar.TaskbarUIController;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.ScrimView;
-import com.android.quickstep.util.TISBindHelper;
 
 /**
  * Interface to be implemented by the parent view of RecentsView
@@ -217,6 +215,4 @@
     void setTaskbarUIController(@Nullable TaskbarUIController taskbarUIController);
 
     @Nullable TaskbarUIController getTaskbarUIController();
-
-    @NonNull TISBindHelper getTISBindHelper();
 }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
index 6eeffda..ccf22ce 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
@@ -16,6 +16,8 @@
 
 package com.android.quickstep.views
 
+import android.view.View
+import androidx.core.view.children
 import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
 import com.android.quickstep.util.GroupTask
 import com.android.quickstep.views.RecentsView.RUNNING_TASK_ATTACH_ALPHA
@@ -94,6 +96,12 @@
     /** Returns the last TaskView that should be displayed as a large tile. */
     fun getLastLargeTaskView(): TaskView? = recentsView.taskViews.lastOrNull { it.isLargeTile }
 
+    /**
+     * Gets the list of accessibility children. Currently all the children of RecentsViews are
+     * added, and in the reverse order to the list.
+     */
+    fun getAccessibilityChildren(): List<View> = recentsView.children.toList().reversed()
+
     @JvmOverloads
     /** Returns the first [TaskView], with some tasks possibly hidden in the carousel. */
     fun getFirstTaskViewInCarousel(
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index 084ea4b..0dbad70 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -658,10 +658,10 @@
 
             recentsView?.let {
                 collectionItemInfo =
-                    AccessibilityNodeInfo.CollectionItemInfo.obtain(
+                    AccessibilityNodeInfo.CollectionItemInfo(
                         0,
                         1,
-                        it.taskViewCount - it.indexOfChild(this@TaskView) - 1,
+                        it.getAccessibilityChildren().indexOf(this@TaskView),
                         1,
                         false,
                     )
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendControllerTest.kt
index cfa12e2..785e585 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendControllerTest.kt
@@ -17,6 +17,7 @@
 package com.android.launcher3.taskbar
 
 import android.animation.AnimatorTestRule
+import androidx.test.core.app.ApplicationProvider
 import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
 import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING
 import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER
@@ -34,6 +35,10 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
 
 @RunWith(LauncherMultivalentJUnit::class)
 @EmulatedDevices(["pixelTablet2023"])
@@ -43,11 +48,10 @@
     val context =
         TaskbarWindowSandboxContext.create { builder ->
             builder.bindSystemUiProxy(
-                object : SystemUiProxy(this) {
-                    override fun notifyTaskbarAutohideSuspend(suspend: Boolean) {
-                        super.notifyTaskbarAutohideSuspend(suspend)
-                        latestSuspendNotification = suspend
-                    }
+                spy(SystemUiProxy(ApplicationProvider.getApplicationContext())) { proxy ->
+                    doAnswer { latestSuspendNotification = it.getArgument(0) }
+                        .whenever(proxy)
+                        .notifyTaskbarAutohideSuspend(anyOrNull())
                 }
             )
         }
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
index eb13b55..36e8a82 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
@@ -21,6 +21,7 @@
 import android.content.Intent
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.core.app.ApplicationProvider
 import com.android.launcher3.Flags.FLAG_TASKBAR_OVERFLOW
 import com.android.launcher3.R
 import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
@@ -50,6 +51,10 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
 
 @RunWith(LauncherMultivalentJUnit::class)
 @EmulatedDevices(["pixelTablet2023"])
@@ -66,10 +71,10 @@
     val context =
         TaskbarWindowSandboxContext.create { builder ->
             builder.bindSystemUiProxy(
-                object : SystemUiProxy(this) {
-                    override fun setDesktopTaskListener(listener: IDesktopTaskListener?) {
-                        desktopTaskListener = listener
-                    }
+                spy(SystemUiProxy(ApplicationProvider.getApplicationContext())) { proxy ->
+                    doAnswer { desktopTaskListener = it.getArgument(0) }
+                        .whenever(proxy)
+                        .setDesktopTaskListener(anyOrNull())
                 }
             )
         }
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt
index 4c94067..360f019 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt
@@ -20,9 +20,9 @@
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
-import android.view.KeyEvent
 import android.view.View.GONE
 import android.view.View.VISIBLE
+import androidx.test.core.app.ApplicationProvider
 import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
 import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
 import com.android.launcher3.taskbar.rules.TaskbarModeRule
@@ -44,6 +44,10 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
 
 @RunWith(LauncherMultivalentJUnit::class)
 @EmulatedDevices(["pixelTablet2023"])
@@ -53,11 +57,8 @@
     val context =
         TaskbarWindowSandboxContext.create { builder ->
             builder.bindSystemUiProxy(
-                object : SystemUiProxy(this) {
-                    override fun onBackEvent(backEvent: KeyEvent?) {
-                        super.onBackEvent(backEvent)
-                        backPressed = true
-                    }
+                spy(SystemUiProxy(ApplicationProvider.getApplicationContext())) {
+                    doAnswer { backPressed = true }.whenever(it).onBackEvent(anyOrNull())
                 }
             )
         }
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt
index 5e438bd..588c22c 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt
@@ -56,8 +56,8 @@
 import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING
 import com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_BAR
 import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.TruthJUnit.assume
 import org.junit.After
-import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -82,11 +82,6 @@
 
     private val activityContext by taskbarUnitTestRule::activityContext
 
-    // Disable hardware keyboard mode during tests.
-    @Before fun enableSoftwareIme() = TaskbarStashController.enableSoftwareImeForTests(true)
-
-    @After fun resetIme() = TaskbarStashController.enableSoftwareImeForTests(false)
-
     @After fun cancelTimeoutIfExists() = stashController.cancelTimeoutIfExists()
 
     @Test
@@ -544,6 +539,8 @@
     @Test
     @TaskbarMode(PINNED)
     fun testAnimatePinnedTaskbar_imeShown_replacesIconsWithHandle() {
+        assume().that(activityContext.isHardwareKeyboard).isFalse()
+
         getInstrumentation().runOnMainSync {
             stashController.updateStateForSysuiFlags(SYSUI_STATE_IME_SHOWING, false)
             animatorTestRule.advanceTimeBy(TASKBAR_STASH_DURATION_FOR_IME)
@@ -555,6 +552,8 @@
     @Test
     @TaskbarMode(PINNED)
     fun testAnimatePinnedTaskbar_imeHidden_replacesHandleWithIcons() {
+        assume().that(activityContext.isHardwareKeyboard).isFalse()
+
         getInstrumentation().runOnMainSync {
             stashController.updateStateForSysuiFlags(SYSUI_STATE_IME_SHOWING, true)
             animatorTestRule.advanceTimeBy(0)
@@ -571,6 +570,8 @@
     @Test
     @TaskbarMode(PINNED)
     fun testAnimatePinnedTaskbar_imeHidden_verifyAnimationDuration() {
+        assume().that(activityContext.isHardwareKeyboard).isFalse()
+
         // Start with IME shown.
         getInstrumentation().runOnMainSync {
             stashController.updateStateForSysuiFlags(SYSUI_STATE_IME_SHOWING, true)
@@ -596,6 +597,8 @@
     @Test
     @TaskbarMode(THREE_BUTTONS)
     fun testAnimateThreeButtonsTaskbar_imeShown_hidesIconsAndBg() {
+        assume().that(activityContext.isHardwareKeyboard).isFalse()
+
         getInstrumentation().runOnMainSync {
             stashController.updateStateForSysuiFlags(SYSUI_STATE_IME_SHOWING, false)
             animatorTestRule.advanceTimeBy(TASKBAR_STASH_DURATION_FOR_IME)
@@ -607,6 +610,8 @@
     @Test
     @TaskbarMode(THREE_BUTTONS)
     fun testAnimateThreeButtonsTaskbar_imeHidden_showsIconsAndBg() {
+        assume().that(activityContext.isHardwareKeyboard).isFalse()
+
         getInstrumentation().runOnMainSync {
             stashController.updateStateForSysuiFlags(SYSUI_STATE_IME_SHOWING, false)
             animatorTestRule.advanceTimeBy(TASKBAR_STASH_DURATION_FOR_IME)
@@ -625,6 +630,8 @@
     @Test
     @TaskbarMode(PINNED)
     fun testSetSystemGestureInProgress_whileImeShown_unstashesTaskbar() {
+        assume().that(activityContext.isHardwareKeyboard).isFalse()
+
         getInstrumentation().runOnMainSync {
             stashController.updateStateForSysuiFlags(SYSUI_STATE_IME_SHOWING, true)
             animatorTestRule.advanceTimeBy(0)
@@ -641,6 +648,19 @@
 
     @Test
     @TaskbarMode(PINNED)
+    fun testSysuiStateImeShowingInApp_hardwareKeyboardWithPinnedMode_notStashedForIme() {
+        assume().that(activityContext.isHardwareKeyboard).isTrue()
+
+        getInstrumentation().runOnMainSync {
+            stashController.updateStateForFlag(FLAG_IN_APP, true)
+            stashController.updateStateForSysuiFlags(SYSUI_STATE_IME_SHOWING, true)
+        }
+
+        assertThat(stashController.isStashed).isFalse()
+    }
+
+    @Test
+    @TaskbarMode(PINNED)
     fun testUnlockTransition_pinnedMode_fadesOutHandle() {
         getInstrumentation().runOnMainSync {
             stashController.updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED, true)
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
index 44070cf..5471072 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
@@ -240,6 +240,7 @@
         // verify the hide bubble animation is pending
         assertThat(animatorScheduler.delayedBlock).isNotNull()
 
+        whenever(bubbleStashController.isStashed).thenReturn(true)
         InstrumentationRegistry.getInstrumentation().runOnMainSync {
             animator.onStashStateChangingWhileAnimating()
         }
@@ -249,7 +250,7 @@
         assertThat(animator.isAnimating).isFalse()
         assertThat(bubbleBarView.scaleX).isEqualTo(1)
         assertThat(bubbleBarView.scaleY).isEqualTo(1)
-        verify(bubbleStashController).onNewBubbleAnimationInterrupted(any(), any())
+        verify(bubbleStashController).onNewBubbleAnimationInterrupted(eq(true), any())
 
         // PhysicsAnimatorTestUtils posts the cancellation to the main thread so we need to wait
         // again
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
index cd4e78b..07b32af 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
@@ -29,7 +29,6 @@
 import com.android.launcher3.taskbar.TaskbarControllers
 import com.android.launcher3.taskbar.TaskbarManager
 import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks
-import com.android.launcher3.taskbar.TaskbarViewController
 import com.android.launcher3.taskbar.bubbles.BubbleControllers
 import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
 import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
@@ -130,15 +129,12 @@
                 }
 
                 try {
-                    TaskbarViewController.enableModelLoadingForTests(false)
-
                     // Required to complete initialization.
                     instrumentation.runOnMainSync { taskbarManager.onUserUnlocked() }
 
                     base.evaluate()
                 } finally {
                     instrumentation.runOnMainSync { taskbarManager.destroy() }
-                    TaskbarViewController.enableModelLoadingForTests(true)
                 }
             }
         }
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt
index 8c51216..0b94dfd 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt
@@ -25,6 +25,7 @@
 import com.android.launcher3.FakeLauncherPrefs
 import com.android.launcher3.LauncherPrefs
 import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppModule
 import com.android.launcher3.dagger.LauncherAppSingleton
 import com.android.launcher3.util.MainThreadInitializedObject.ObjectSandbox
 import com.android.launcher3.util.SandboxApplication
@@ -116,7 +117,7 @@
 }
 
 @LauncherAppSingleton
-@Component
+@Component(modules = [LauncherAppModule::class])
 interface TaskbarSandboxComponent : LauncherAppComponent {
     @Component.Builder
     interface Builder : LauncherAppComponent.Builder {
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
index 6dce10b..c334552 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
@@ -52,7 +52,6 @@
 import android.view.ViewTreeObserver;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.launcher3.DeviceProfile;
@@ -60,9 +59,9 @@
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StatefulContainer;
+import com.android.launcher3.util.LauncherModelHelper;
 import com.android.launcher3.util.MSDLPlayerWrapper;
 import com.android.launcher3.util.SystemUiController;
-import com.android.quickstep.fallback.window.RecentsWindowFactory;
 import com.android.quickstep.util.ContextInitListener;
 import com.android.quickstep.util.MotionPauseDetector;
 import com.android.quickstep.views.RecentsView;
@@ -90,8 +89,9 @@
         SWIPE_HANDLER extends AbsSwipeUpHandler<RECENTS_CONTAINER, RECENTS_VIEW, STATE_TYPE>,
         CONTAINER_INTERFACE extends BaseContainerInterface<STATE_TYPE, RECENTS_CONTAINER>> {
 
-    protected final Context mContext =
-            InstrumentationRegistry.getInstrumentation().getTargetContext();
+    protected final LauncherModelHelper mLauncherModelHelper = new LauncherModelHelper();
+    protected final LauncherModelHelper.SandboxModelContext mContext =
+            mLauncherModelHelper.sandboxContext;
     protected final InputConsumerController mInputConsumerController =
             InputConsumerController.getRecentsAnimationInputConsumer();
     protected final ActivityManager.RunningTaskInfo mRunningTaskInfo =
@@ -180,8 +180,7 @@
 
     @Before
     public void setUpRecentsContainer() {
-        mTaskAnimationManager = new TaskAnimationManager(mContext, getRecentsWindowFactory(),
-                mRecentsAnimationDeviceState);
+        mTaskAnimationManager = new TaskAnimationManager(mContext, mRecentsAnimationDeviceState);
         RecentsViewContainer recentsContainer = getRecentsContainer();
         RECENTS_VIEW recentsView = getRecentsView();
 
@@ -383,11 +382,6 @@
         return createSwipeHandler(SystemClock.uptimeMillis(), false);
     }
 
-    @Nullable
-    protected RecentsWindowFactory getRecentsWindowFactory() {
-        return null;
-    }
-
     @NonNull
     protected abstract SWIPE_HANDLER createSwipeHandler(
             long touchTimeMs, boolean continuingLastGesture);
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/DisplayModelTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/DisplayModelTest.kt
new file mode 100644
index 0000000..a939e84
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/DisplayModelTest.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 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.quickstep
+
+import android.content.Context
+import android.view.Display
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertNull
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DisplayModelTest {
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    class TestableResource : DisplayModel.DisplayResource() {
+        var isCleanupCalled = false
+
+        override fun cleanup() {
+            isCleanupCalled = true
+        }
+    }
+
+    private val testableDisplayModel =
+        object : DisplayModel<TestableResource>(context) {
+            override fun createDisplayResource(displayId: Int) {
+                displayResourceArray.put(displayId, TestableResource())
+            }
+        }
+
+    @Test
+    fun testCreate() {
+        testableDisplayModel.createDisplayResource(Display.DEFAULT_DISPLAY)
+        val resource = testableDisplayModel.getDisplayResource(Display.DEFAULT_DISPLAY)
+        assertNotNull(resource)
+    }
+
+    @Test
+    fun testCleanAndDelete() {
+        testableDisplayModel.createDisplayResource(Display.DEFAULT_DISPLAY)
+        val resource = testableDisplayModel.getDisplayResource(Display.DEFAULT_DISPLAY)!!
+        assertNotNull(resource)
+        testableDisplayModel.deleteDisplayResource(Display.DEFAULT_DISPLAY)
+        assert(resource.isCleanupCalled)
+        assertNull(testableDisplayModel.getDisplayResource(Display.DEFAULT_DISPLAY))
+    }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt
index 37accdb..41877c9 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt
@@ -19,15 +19,13 @@
 import android.graphics.PointF
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.launcher3.Flags.enableLauncherOverviewInWindow
 import com.android.launcher3.R
 import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppModule
 import com.android.launcher3.dagger.LauncherAppSingleton
 import com.android.launcher3.util.LauncherModelHelper
 import com.android.launcher3.util.MSDLPlayerWrapper
-import com.android.quickstep.dagger.QuickStepModule
-import com.android.quickstep.fallback.window.RecentsWindowFactory
+import com.android.quickstep.fallback.window.RecentsDisplayModel
 import com.android.systemui.contextualeducation.GestureType
 import com.android.systemui.shared.system.InputConsumerController
 import dagger.BindsInstance
@@ -55,6 +53,8 @@
 
     @Mock private lateinit var systemUiProxy: SystemUiProxy
 
+    @Mock private lateinit var recentsDisplayModel: RecentsDisplayModel
+
     @Mock private lateinit var msdlPlayerWrapper: MSDLPlayerWrapper
 
     private lateinit var underTest: LauncherSwipeHandlerV2
@@ -70,21 +70,14 @@
     @Before
     fun setup() {
         sandboxContext.initDaggerComponent(
-            DaggerTestComponent.builder().bindSystemUiProxy(systemUiProxy)
+            DaggerTestComponent.builder()
+                .bindSystemUiProxy(systemUiProxy)
+                .bindRecentsDisplayModel(recentsDisplayModel)
         )
+
         val deviceState = mock(RecentsAnimationDeviceState::class.java)
         whenever(deviceState.rotationTouchHelper).thenReturn(mock(RotationTouchHelper::class.java))
-
-        if (enableLauncherOverviewInWindow()) {
-            // Initialize an instance of RecentsWindowFactory directly to simulate its
-            // initialization in TouchInteractionService#onCreate.
-            // This instance will then be used in OverviewComponentObserver.
-            InstrumentationRegistry.getInstrumentation().runOnMainSync {
-                RecentsWindowFactory(sandboxContext)
-            }
-        }
-
-        gestureState = spy(GestureState(OverviewComponentObserver(sandboxContext, deviceState), 0))
+        gestureState = spy(GestureState(OverviewComponentObserver.INSTANCE.get(sandboxContext), 0))
 
         underTest =
             LauncherSwipeHandlerV2(
@@ -123,12 +116,14 @@
 }
 
 @LauncherAppSingleton
-@Component(modules = [QuickStepModule::class])
+@Component(modules = [LauncherAppModule::class])
 interface TestComponent : LauncherAppComponent {
     @Component.Builder
     interface Builder : LauncherAppComponent.Builder {
         @BindsInstance fun bindSystemUiProxy(proxy: SystemUiProxy): Builder
 
+        @BindsInstance fun bindRecentsDisplayModel(model: RecentsDisplayModel): Builder
+
         override fun build(): TestComponent
     }
 }
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsWindowSwipeHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsWindowSwipeHandlerTestCase.java
index 3287fb5..40fefae 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsWindowSwipeHandlerTestCase.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsWindowSwipeHandlerTestCase.java
@@ -16,17 +16,16 @@
 
 package com.android.quickstep;
 
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.when;
-
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 
+import com.android.launcher3.dagger.LauncherAppComponent;
+import com.android.launcher3.dagger.LauncherAppSingleton;
 import com.android.launcher3.util.LauncherMultivalentJUnit;
+import com.android.quickstep.dagger.QuickStepModule;
 import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.fallback.RecentsState;
-import com.android.quickstep.fallback.window.RecentsWindowFactory;
+import com.android.quickstep.fallback.window.RecentsDisplayModel;
 import com.android.quickstep.fallback.window.RecentsWindowManager;
 import com.android.quickstep.fallback.window.RecentsWindowSwipeHandler;
 import com.android.quickstep.views.RecentsViewContainer;
@@ -35,6 +34,9 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 
+import dagger.BindsInstance;
+import dagger.Component;
+
 @SmallTest
 @RunWith(LauncherMultivalentJUnit.class)
 public class RecentsWindowSwipeHandlerTestCase extends AbsSwipeUpHandlerTestCase<
@@ -44,13 +46,14 @@
         RecentsWindowSwipeHandler,
         FallbackWindowInterface> {
 
-    @Mock private RecentsWindowFactory mRecentsWindowFactory;
+    @Mock private RecentsDisplayModel mRecentsDisplayModel;
     @Mock private FallbackRecentsView<RecentsWindowManager> mRecentsView;
     @Mock private RecentsWindowManager mRecentsWindowManager;
 
     @Before
-    public void setupRecentsWindowFactory() {
-        when(mRecentsWindowFactory.get(anyInt())).thenReturn(mRecentsWindowManager);
+    public void setRecentsDisplayModel() {
+        mContext.initDaggerComponent(DaggerRecentsWindowSwipeHandlerTestCase_TestComponent.builder()
+                .bindRecentsDisplayModel(mRecentsDisplayModel));
     }
 
     @NonNull
@@ -65,16 +68,9 @@
                 touchTimeMs,
                 continuingLastGesture,
                 mInputConsumerController,
-                mRecentsWindowFactory,
                 mMSDLPlayerWrapper);
     }
 
-    @Nullable
-    @Override
-    protected RecentsWindowFactory getRecentsWindowFactory() {
-        return mRecentsWindowFactory;
-    }
-
     @NonNull
     @Override
     protected RecentsViewContainer getRecentsContainer() {
@@ -86,4 +82,14 @@
     protected FallbackRecentsView<RecentsWindowManager> getRecentsView() {
         return mRecentsView;
     }
+
+    @LauncherAppSingleton
+    @Component(modules = { QuickStepModule.class })
+    interface TestComponent extends LauncherAppComponent {
+        @Component.Builder
+        interface Builder extends LauncherAppComponent.Builder {
+            @BindsInstance Builder bindRecentsDisplayModel(RecentsDisplayModel model);
+            @Override LauncherAppComponent build();
+        }
+    }
 }
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentTasksDataSource.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentTasksDataSource.kt
index eaeb513..9e99a0b 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentTasksDataSource.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentTasksDataSource.kt
@@ -20,10 +20,12 @@
 import java.util.function.Consumer
 
 class FakeRecentTasksDataSource : RecentTasksDataSource {
-    var taskList: List<GroupTask> = listOf()
+    private var taskList: List<GroupTask> = listOf()
 
     override fun getTasks(callback: Consumer<List<GroupTask>>?): Int {
-        callback?.accept(taskList)
+        // Makes a copy of the GroupTask to create a new GroupTask instance and to simulate
+        // RecentsModel::getTasks behavior.
+        callback?.accept(taskList.map { it.copy() })
         return 0
     }
 
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
index e0e7f28..624310b 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
@@ -117,6 +117,20 @@
         }
 
     @Test
+    fun whenThumbnailIsLoaded_getAllTaskData_usesPreviousLoadedThumbnailAndIcon() =
+        testScope.runTest {
+            recentsModel.seedTasks(defaultTaskList)
+            systemUnderTest.getAllTaskData(forceRefresh = true)
+            val bitmap1 = taskThumbnailDataSource.taskIdToBitmap[1]
+
+            systemUnderTest.setVisibleTasks(setOf(1))
+            assertThat(systemUnderTest.getThumbnailById(1).first()!!.thumbnail).isEqualTo(bitmap1)
+
+            systemUnderTest.getAllTaskData(forceRefresh = true)
+            assertThat(systemUnderTest.getThumbnailById(1).first()!!.thumbnail).isEqualTo(bitmap1)
+        }
+
+    @Test
     fun getCurrentThumbnailByIdReturnsThumbnailWithLoadedThumbnails() =
         testScope.runTest {
             recentsModel.seedTasks(defaultTaskList)
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/window/RecentsDisplayModelTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/window/RecentsDisplayModelTest.kt
new file mode 100644
index 0000000..d2aa6ac
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/window/RecentsDisplayModelTest.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 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.quickstep.recents.window
+
+import android.graphics.Point
+import android.hardware.display.DisplayManager
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import android.view.Display
+import android.view.DisplayAdjustments
+import android.view.Surface
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.Flags.FLAG_ENABLE_FALLBACK_OVERVIEW_IN_WINDOW
+import com.android.launcher3.Flags.FLAG_ENABLE_LAUNCHER_OVERVIEW_IN_WINDOW
+import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppModule
+import com.android.launcher3.dagger.LauncherAppSingleton
+import com.android.launcher3.util.LauncherModelHelper
+import com.android.launcher3.util.window.CachedDisplayInfo
+import com.android.quickstep.fallback.window.RecentsDisplayModel
+import dagger.BindsInstance
+import dagger.Component
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableFlags(FLAG_ENABLE_LAUNCHER_OVERVIEW_IN_WINDOW, FLAG_ENABLE_FALLBACK_OVERVIEW_IN_WINDOW)
+class RecentsDisplayModelTest {
+    @get:Rule val setFlagsRule = SetFlagsRule()
+
+    // initiate dagger components for injection
+    private val launcherModelHelper = LauncherModelHelper()
+    private val context = spy(launcherModelHelper.sandboxContext)
+    private val displayManager: DisplayManager = context.spyService(DisplayManager::class.java)
+    private val display: Display = mock()
+
+    private lateinit var recentsDisplayModel: RecentsDisplayModel
+
+    private val width = 2208
+    private val height = 1840
+
+    @Before
+    fun setup() {
+        // Mock display
+        val displayInfo = CachedDisplayInfo(Point(width, height), Surface.ROTATION_0)
+        whenever(display.rotation).thenReturn(displayInfo.rotation)
+        whenever(display.displayAdjustments).thenReturn(DisplayAdjustments())
+        whenever(context.display).thenReturn(display)
+
+        // Mock displayManager
+        whenever(displayManager.getDisplay(anyInt())).thenReturn(display)
+
+        runOnMainSync { recentsDisplayModel = RecentsDisplayModel.INSTANCE.get(context) }
+        context.initDaggerComponent(
+            DaggerRecentsDisplayModelComponent.builder()
+                .bindRecentsDisplayModel(recentsDisplayModel)
+        )
+    }
+
+    @Test
+    fun testEnsureSingleton() {
+        val recentsDisplayModel2 = RecentsDisplayModel.INSTANCE.get(context)
+        assert(recentsDisplayModel == recentsDisplayModel2)
+    }
+
+    @Test
+    fun testDefaultDisplayCreation() {
+        Assert.assertNotNull(recentsDisplayModel.getRecentsWindowManager(Display.DEFAULT_DISPLAY))
+        Assert.assertNotNull(
+            recentsDisplayModel.getFallbackWindowInterface(Display.DEFAULT_DISPLAY)
+        )
+    }
+
+    @Test
+    fun testCreateSeparateInstances() {
+        val display = Display.DEFAULT_DISPLAY + 1
+        runOnMainSync { recentsDisplayModel.createDisplayResource(display) }
+
+        val defaultManager = recentsDisplayModel.getRecentsWindowManager(Display.DEFAULT_DISPLAY)
+        val secondaryManager = recentsDisplayModel.getRecentsWindowManager(display)
+        Assert.assertNotSame(defaultManager, secondaryManager)
+
+        val defaultInterface =
+            recentsDisplayModel.getFallbackWindowInterface(Display.DEFAULT_DISPLAY)
+        val secondInterface = recentsDisplayModel.getFallbackWindowInterface(display)
+        Assert.assertNotSame(defaultInterface, secondInterface)
+    }
+
+    @Test
+    fun testDestroy() {
+        Assert.assertNotNull(recentsDisplayModel.getRecentsWindowManager(Display.DEFAULT_DISPLAY))
+        Assert.assertNotNull(
+            recentsDisplayModel.getFallbackWindowInterface(Display.DEFAULT_DISPLAY)
+        )
+        recentsDisplayModel.destroy()
+        Assert.assertNull(recentsDisplayModel.getRecentsWindowManager(Display.DEFAULT_DISPLAY))
+        Assert.assertNull(recentsDisplayModel.getFallbackWindowInterface(Display.DEFAULT_DISPLAY))
+    }
+
+    private fun runOnMainSync(f: Runnable) {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync { f.run() }
+    }
+}
+
+@LauncherAppSingleton
+@Component(modules = [LauncherAppModule::class])
+interface RecentsDisplayModelComponent : LauncherAppComponent {
+    @Component.Builder
+    interface Builder : LauncherAppComponent.Builder {
+        @BindsInstance fun bindRecentsDisplayModel(model: RecentsDisplayModel): Builder
+
+        override fun build(): RecentsDisplayModelComponent
+    }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 695211b..5bb2fad 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -64,6 +64,7 @@
 import com.android.launcher3.util.rule.TestIsolationRule;
 import com.android.launcher3.util.rule.TestStabilityRule;
 import com.android.launcher3.util.rule.ViewCaptureRule;
+import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
 import com.android.quickstep.fallback.window.RecentsWindowManager;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.RecentsViewContainer;
@@ -330,7 +331,7 @@
         return recentsViewContainer.<RecentsView>getOverviewPanel().getTaskViewCount();
     }
 
-    private class OverviewUpdateHandler {
+    private class OverviewUpdateHandler implements OverviewChangeListener {
 
         final RecentsAnimationDeviceState mRads;
         final OverviewComponentObserver mObserver;
@@ -339,19 +340,24 @@
         OverviewUpdateHandler() {
             Context ctx = getInstrumentation().getTargetContext();
             mRads = new RecentsAnimationDeviceState(ctx);
-            mObserver = new OverviewComponentObserver(ctx, mRads);
+            mObserver = OverviewComponentObserver.INSTANCE.get(ctx);
             mChangeCounter = new CountDownLatch(1);
             if (mObserver.getHomeIntent().getComponent()
                     .getPackageName().equals(mOtherLauncherActivity.packageName)) {
                 // Home already same
                 mChangeCounter.countDown();
             } else {
-                mObserver.setOverviewChangeListener(b -> mChangeCounter.countDown());
+                mObserver.addOverviewChangeListener(this);
             }
         }
 
+        @Override
+        public void onOverviewTargetChange(boolean isHomeAndOverviewSame) {
+            mChangeCounter.countDown();
+        }
+
         void destroy() {
-            mObserver.onDestroy();
+            mObserver.removeOverviewChangeListener(this);
             mRads.destroy();
         }
     }
diff --git a/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java b/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java
index 2c6b750..f522a2c 100644
--- a/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java
@@ -56,7 +56,6 @@
 import com.android.launcher3.util.LockedUserState;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.fallback.window.RecentsWindowFactory;
 import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
 import com.android.quickstep.inputconsumers.BubbleBarInputConsumer;
 import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
@@ -122,7 +121,7 @@
     @Before
     public void setupTaskAnimationManager() {
         mTaskAnimationManager = new TaskAnimationManager(
-                mContext, mock(RecentsWindowFactory.class), mDeviceState);
+                mContext, mDeviceState);
     }
 
     @Before
diff --git a/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java b/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
index b15b78e..e065dba 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
@@ -23,6 +23,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeFalse;
 
+import android.os.Process;
 import android.util.Log;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -74,8 +75,9 @@
     }
 
     private void createAndStartPrivateProfileUser() {
-        String createUserOutput = executeShellCommand("pm create-user --profileOf 0 --user-type "
-                + "android.os.usertype.profile.PRIVATE " + PRIVATE_PROFILE_NAME);
+        int myUserId = Process.myUserHandle().getIdentifier();
+        String createUserOutput = executeShellCommand("pm create-user --profileOf " + myUserId
+                + " --user-type android.os.usertype.profile.PRIVATE " + PRIVATE_PROFILE_NAME);
         updatePrivateProfileSetupSuccessful("pm create-user", createUserOutput);
         String[] tokens = createUserOutput.split("\\s+");
         mProfileUserId = Integer.parseInt(tokens[tokens.length - 1]);
diff --git a/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java b/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
index 960a467..098b417 100644
--- a/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
@@ -30,8 +30,6 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
-import com.android.quickstep.fallback.window.RecentsWindowFactory;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -45,9 +43,6 @@
             InstrumentationRegistry.getInstrumentation().getTargetContext();
 
     @Mock
-    private RecentsWindowFactory mRecentsWindowFactory;
-
-    @Mock
     private SystemUiProxy mSystemUiProxy;
 
     private TaskAnimationManager mTaskAnimationManager;
@@ -62,8 +57,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mTaskAnimationManager = new TaskAnimationManager(mContext, mRecentsWindowFactory,
-                mRecentsAnimationDeviceState) {
+        mTaskAnimationManager = new TaskAnimationManager(mContext, mRecentsAnimationDeviceState) {
             @Override
             SystemUiProxy getSystemUiProxy() {
                 return mSystemUiProxy;
diff --git a/quickstep/tests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt b/quickstep/tests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt
index cb59f7d..3148737 100644
--- a/quickstep/tests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt
@@ -70,7 +70,10 @@
         whenever(taskbarActivityContext.launcherPrefs).thenReturn(launcherPrefs)
         whenever(taskbarActivityContext.dragLayer).thenReturn(taskbarDragLayer)
         whenever(taskbarActivityContext.statsLogManager).thenReturn(statsLogManager)
-        whenever(taskbarControllers.taskbarDesktopModeController.areDesktopTasksVisible)
+        whenever(
+                taskbarControllers.taskbarDesktopModeController
+                    .areDesktopTasksVisibleAndNotInOverview
+            )
             .thenAnswer { _ -> isInDesktopMode }
         pinningController = spy(TaskbarPinningController(taskbarActivityContext))
         pinningController.init(taskbarControllers, taskbarSharedState)
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 4dd5e1d..b9a2065 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Moenie voorstel nie"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Vasspeldvoorspelling"</string>
     <string name="bubble" msgid="3072951361014076670">"Borrel"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installeer kortpaaie"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Laat \'n app toe om kortpaaie by te voeg sonder gebruikerinmenging."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"lees tuis-instellings en -kortpaaie"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index c40ef69..530aa80 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"መተግበሪያውን አይጠቁሙ"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"የፒን ግምት"</string>
     <string name="bubble" msgid="3072951361014076670">"አረፋ"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"አቋራጮችን ይጭናል"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"መተግበሪያው ያለተጠቃሚ ጣልቃ ገብነት አቋራጭ እንዲያክል ያስችለዋል።"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"የመነሻ ቅንብሮች እና አቋራጮችን ያነባል"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index bbdfebd..879c0db 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -68,10 +68,8 @@
     <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"تطبيقات العمل"</string>
     <string name="widget_category_conversations" msgid="8894438636213590446">"المحادثات"</string>
     <string name="widget_category_note_taking" msgid="3469689394504266039">"تدوين الملاحظات"</string>
-    <!-- no translation found for widget_cell_tap_to_show_add_button_label (4354194214317043581) -->
-    <skip />
-    <!-- no translation found for widget_cell_tap_to_hide_add_button_label (6117805205101555997) -->
-    <skip />
+    <string name="widget_cell_tap_to_show_add_button_label" msgid="4354194214317043581">"إظهار زر الإضافة"</string>
+    <string name="widget_cell_tap_to_hide_add_button_label" msgid="6117805205101555997">"إخفاء زر الإضافة"</string>
     <string name="widget_add_button_label" msgid="2761267068711937179">"إضافة"</string>
     <string name="widget_add_button_content_description" msgid="1810530016360039643">"إضافة التطبيق المصغّر \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\""</string>
     <string name="widgets_list_expand_button_label" msgid="7912016136574932622">"عرض الكل"</string>
@@ -102,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"عدم اقتراح التطبيق"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"تثبيت التطبيق المتوقّع"</string>
     <string name="bubble" msgid="3072951361014076670">"فقاعة"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"تثبيت اختصارات"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"للسماح لتطبيق ما بإضافة اختصارات بدون تدخل المستخدم."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"الاطلاع على الإعدادات والاختصارات على الشاشة الرئيسية"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index acce0d2..eb20bd1 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"পৰামৰ্শ নিদিব"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"পূৰ্বানুমান কৰা এপ্‌টো পিন কৰক"</string>
     <string name="bubble" msgid="3072951361014076670">"বাবল"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"শ্বৰ্টকাট ইনষ্টল কৰিব পাৰে"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ব্য়ৱহাৰকাৰীৰ হস্তক্ষেপ অবিহনেই কোনো এপক শ্বৰ্টকাটবোৰ যোগ কৰাৰ অনুমতি দিয়ে।"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"গৃহ স্ক্ৰীনত ছেটিং আৰু শ্বৰ্টকাটসমূহ পঢ়া"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 8eda426..00850f95 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Tətbiq təklif olunmasın"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Proqnozlaşdırılan tətbiqi bərkidin"</string>
     <string name="bubble" msgid="3072951361014076670">"Qabarcıq"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"qısayolları quraşdır"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Tətbiqə istifadəçi müdaxiləsi olmadan qısayolları əlavə etməyə icazə verir."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"Əsas səhifə ayarlarını və qısayollarını oxumaq"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 33941ff..3cad215 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlaži aplikaciju"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Zakači predviđanje"</string>
     <string name="bubble" msgid="3072951361014076670">"Oblačić"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliranje prečica"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Dozvoljava aplikaciji da dodaje prečice bez intervencije korisnika."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"čitanje podešavanja i prečica na početnom ekranu"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index d395006..88b89c1 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Не прапаноўваць праграму"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Замацаваць прапанаваную праграму"</string>
     <string name="bubble" msgid="3072951361014076670">"Бурбалка"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Стварэнне ярлыкоў"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Дазваляе праграмам дадаваць ярлыкі без умяшання карыстальніка."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"счытваць налады і ярлыкі на галоўным экране"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index cf298c3..b919345 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Без предлагане на приложение"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Фиксиране на предвиждането"</string>
     <string name="bubble" msgid="3072951361014076670">"Балонче"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталиране на преки пътища"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Разрешава на приложението да добавя преки пътища без намеса на потребителя."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"четене на настройките и преките пътища на началния екран"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index f2654bb..7292342 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"অ্যাপ সাজেস্ট করবেন না"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"আপনার প্রয়োজন হতে পারে এমন অ্যাপ পিন করুন"</string>
     <string name="bubble" msgid="3072951361014076670">"বাবল"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"শর্টকাটগুলি ইনস্টল করে"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"একটি অ্যাপ্লিকেশানকে ব্যবহারকারীর হস্তক্ষেপ ছাড়াই শর্টকাটগুলি যোগ করার অনুমতি দেয়৷"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"হোম স্ক্রিনে সেটিংস ও শর্টকাট পড়ুন"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 3277e22..a89b953 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlaži aplikaciju"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Zakači predviđanje"</string>
     <string name="bubble" msgid="3072951361014076670">"Oblačić"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliraj prečice"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Dopušta aplikaciji dodavanje prečica bez posredovanja korisnika."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"čita postavke na početnom ekranu i prečice"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index f8f8e28..4d89e6d 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"No suggereixis l\'aplicació"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Fixa la predicció"</string>
     <string name="bubble" msgid="3072951361014076670">"Bombolla"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instal·la dreceres"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permet que una aplicació afegeixi dreceres sense la intervenció de l\'usuari."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"llegir la configuració i les dreceres de la pantalla d\'inici"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 5fa9154..3b0895f 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Nenavrhovat aplikaci"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Připnout předpověď"</string>
     <string name="bubble" msgid="3072951361014076670">"Bublat"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalace zástupce"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Umožňuje aplikaci přidat zástupce bez zásahu uživatele."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"čtení nastavení a zkratek plochy"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 16e3473..ee1c7cb 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Foreslå ikke en app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Fastgør forslaget"</string>
     <string name="bubble" msgid="3072951361014076670">"Boble"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installere genveje"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Tillader, at en app tilføjer genveje uden brugerens indgriben."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"læs indstillinger og genveje for startskærm"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index cb64821..e3c2fdc 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"App nicht vorschlagen"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Vorgeschlagene App fixieren"</string>
     <string name="bubble" msgid="3072951361014076670">"Bubble"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Verknüpfungen installieren"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Ermöglicht einer App das Hinzufügen von Verknüpfungen ohne Eingreifen des Nutzers"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"Einstellungen und Verknüpfungen auf dem Startbildschirm lesen"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 672ba05..696a0b5 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Να μην προτείνεται"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Καρφίτσωμα πρόβλεψης"</string>
     <string name="bubble" msgid="3072951361014076670">"Συννεφάκι"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"εγκατάσταση συντομεύσεων"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Επιτρέπει σε μια εφαρμογή την προσθήκη συντομεύσεων χωρίς την παρέμβαση του χρήστη."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ανάγνωση ρυθμίσεων και συντομεύσεων αρχικής οθόνης"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index a4f88de..2027683 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Pin prediction"</string>
     <string name="bubble" msgid="3072951361014076670">"Bubble"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"read Home settings and shortcuts"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 363bb4b..0ebcd18 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -100,6 +100,7 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Pin Prediction"</string>
     <string name="bubble" msgid="3072951361014076670">"Bubble"</string>
+    <string name="pin_to_taskbar" msgid="1281337899690299038">"Pin to taskbar"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"read home settings and shortcuts"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index a4f88de..2027683 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Pin prediction"</string>
     <string name="bubble" msgid="3072951361014076670">"Bubble"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"read Home settings and shortcuts"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index a4f88de..2027683 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Pin prediction"</string>
     <string name="bubble" msgid="3072951361014076670">"Bubble"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"read Home settings and shortcuts"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 0344113..6cafd09 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"No sugerir app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Fijar predicción"</string>
     <string name="bubble" msgid="3072951361014076670">"Burbuja"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar accesos directos"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que una aplicación agregue accesos directos sin que el usuario intervenga."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"leer parámetros de configuración y accesos directos de la página principal"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index fc34acf..badd94a 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"No sugerir aplicación"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Fijar predicción"</string>
     <string name="bubble" msgid="3072951361014076670">"Burbuja"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar accesos directos"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que una aplicación añada accesos directos sin intervención del usuario."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"leer ajustes y accesos directos de la pantalla de inicio"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 5fc3b82..169a378 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ära soovita rakendust"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Kinnita ennustus"</string>
     <string name="bubble" msgid="3072951361014076670">"Mull"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installi otseteed"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Võimaldab rakendusel lisada otseteid kasutaja sekkumiseta."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"avakuva seadete ja otseteede lugemine"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 99f127a..d194482 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ez iradoki aplikazioa"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Ainguratu iragarpena"</string>
     <string name="bubble" msgid="3072951361014076670">"Burbuila"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Instalatu lasterbideak"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Erabiltzaileak ezer egin gabe lasterbideak gehitzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"irakurri hasierako pantailako ezarpenak eta lasterbideak"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 99e8718..f6e0896 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"برنامه پیشنهاد داده نشود"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"سنجاق کردن پیشنهاد"</string>
     <string name="bubble" msgid="3072951361014076670">"حبابک"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"نصب میان‌برها"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"به برنامه اجازه می‌دهد میان‌برها را بدون دخالت کاربر اضافه کند."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"خواندن تنظیمات و میان‌برهای صفحه اصلی"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 6beb593..64d7b5e 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Älä ehdota sovellusta"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Kiinnitä sovellus"</string>
     <string name="bubble" msgid="3072951361014076670">"Kupla"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"asenna pikakuvakkeita"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Antaa sovelluksen lisätä pikakuvakkeita itsenäisesti ilman käyttäjän valintaa."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"lukea aloitusnäytön asetuksia ja pikakuvakkeita"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 696d8af..9f6efe2 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne pas suggérer d\'appli"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Épingler la prédiction"</string>
     <string name="bubble" msgid="3072951361014076670">"Bulle"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installer des raccourcis"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permet à une appli d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"lire les paramètres et les raccourcis de la page d\'accueil"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index c870afa..e91cd03 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne pas suggérer d\'appli"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Épingler la prédiction"</string>
     <string name="bubble" msgid="3072951361014076670">"Bulle"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installer des raccourcis"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permettre à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"Lire les paramètres et les raccourcis de la page d\'accueil"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 5bcf2fd..7bfa617 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Non suxerir app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Fixar predición"</string>
     <string name="bubble" msgid="3072951361014076670">"Burbulla"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atallos"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a unha aplicación engadir atallos sen intervención do usuario."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ler a configuración e os atallos da pantalla de inicio"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 1480ea9..4992dd3 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ઍપ સૂચવશો નહીં"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"પૂર્વાનુમાનને પિન કરો"</string>
     <string name="bubble" msgid="3072951361014076670">"બબલ"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"શૉર્ટકટ ઇન્સ્ટૉલ કરો"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"એપ્લિકેશનને વપરાશકર્તા હસ્તક્ષેપ વગર શોર્ટકટ્સ ઉમેરવાની મંજૂરી આપે છે."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"હોમ સેટિંગ અને શૉર્ટકટ વાંચો"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 32a2a96..0398a27 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ऐप्लिकेशन का सुझाव न दें"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"सुझाए गए ऐप पिन करें"</string>
     <string name="bubble" msgid="3072951361014076670">"बबल"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"शॉर्टकट इंस्‍टॉल करें"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ऐप को उपयोगकर्ता के हस्‍तक्षेप के बिना शॉर्टकट जोड़ने देती है."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"होम स्क्रीन की सेटिंग और शॉर्टकट पढ़ने की अनुमति"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 726efe8..ab22e7a 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlaži aplikaciju"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Prikvači predviđenu apl."</string>
     <string name="bubble" msgid="3072951361014076670">"Oblačić"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliranje prečaca"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Aplikaciji omogućuje dodavanje prečaca bez intervencije korisnika."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"čitati postavke i prečace početnog zaslona"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index b85d3df..ea72c1f 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne javasoljon appot"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Várható kitűzése"</string>
     <string name="bubble" msgid="3072951361014076670">"Buborék"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"parancsikonok telepítése"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Lehetővé teszi egy alkalmazás számára, hogy felhasználói beavatkozás nélkül adjon hozzá parancsikonokat."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"kezdőképernyő beállításainak és parancsikonjainak olvasása"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 36e89b4..46d4b35 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Չառաջարկել"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Ամրացնել առաջարկվող հավելվածը"</string>
     <string name="bubble" msgid="3072951361014076670">"Ամպիկ"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Դյուրանցումների տեղադրում"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Հավելվածին թույլ է տալիս ավելացնել դյուրանցումներ՝ առանց օգտագործողի միջամտության:"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"կարդալ հիմնական էկրանի կարգավորումներն ու դյուրանցումները"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 0d5a819..a9cc4fe 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Jangan sarankan apl"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Pin Prediksi"</string>
     <string name="bubble" msgid="3072951361014076670">"Balon"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"memasang pintasan"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Mengizinkan aplikasi menambahkan pintasan tanpa campur tangan pengguna."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"membaca setelan dan pintasan layar utama"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 8f3c604..764435c 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ekki fá tillögu að forriti"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Festa tillögu"</string>
     <string name="bubble" msgid="3072951361014076670">"Blaðra"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"setja upp flýtileiðir"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Leyfir forriti að bæta við flýtileiðum án íhlutunar notanda."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"lesa stillingar og flýtileiðir heimaskjás"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index f125816..47fb620 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Non suggerire app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Blocca previsione"</string>
     <string name="bubble" msgid="3072951361014076670">"Fumetto"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Aggiunta di scorciatoie"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Consente a un\'app di aggiungere scorciatoie automaticamente."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"leggere le impostazioni e le scorciatoie nella schermata Home"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index f7fd83a..7aa7b1a 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"בלי להציע את האפליקציה"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"הצמדת החיזוי"</string>
     <string name="bubble" msgid="3072951361014076670">"בועה"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"התקנת קיצורי דרך"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"מאפשר לאפליקציה להוסיף קיצורי דרך ללא התערבות המשתמש."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"קריאת ההגדרות וקיצורי הדרך בדף הבית"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 9b012c5..93bfb37 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"アプリを表示しない"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"アプリの候補を固定"</string>
     <string name="bubble" msgid="3072951361014076670">"ふきだし"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ショートカットのインストール"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ユーザー操作なしでショートカットを追加することをアプリに許可します。"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ホームの設定とショートカットの読み取り"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 955d65f..e116476 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"არ შემომთავაზო აპი"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"ჩამაგრების პროგნოზირება"</string>
     <string name="bubble" msgid="3072951361014076670">"ბუშტი"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"მალსახმობების დაყენება"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"აპისთვის მალსახმობების დამოუკიდებლად დამატების უფლების მიცემა."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"მთავარი ეკრანის პარამეტრებისა და მალსახმობების წაკითხვა"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index c50d007..6de095e 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Қолданба ұсынбау"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Болжамды бекіту"</string>
     <string name="bubble" msgid="3072951361014076670">"Қалқыма терезе"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"таңбаша орнату"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Қолданбаға пайдаланушының қатысуынсыз төте пернелерді қосу мүмкіндігін береді."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"негізгі экран параметрлері мен таңбашаларын оқу"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 7a73e69..2a60788 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"កុំណែនាំកម្មវិធី"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"ខ្ទាស់ការ​ព្យាករ"</string>
     <string name="bubble" msgid="3072951361014076670">"ពពុះ"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ដំឡើង​ផ្លូវកាត់"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"អនុញ្ញាត​ឲ្យ​កម្មវិធី​បន្ថែម​ផ្លូវកាត់​ ដោយ​មិន​ចាំបាច់​​អំពើ​ពី​អ្នក​ប្រើ។"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"អានការកំណត់ និងផ្លូវកាត់របស់អេក្រង់ដើម"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 8c988bf..31b95db 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ಆ್ಯಪ್ ಅನ್ನು ಸೂಚಿಸಬೇಡಿ"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"ಮುನ್ನೋಟ ಪಿನ್ ಮಾಡಿ"</string>
     <string name="bubble" msgid="3072951361014076670">"ಬಬಲ್"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಸ್ಥಾಪಿಸಿ"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ಬಳಕೆದಾರರ ಹಸ್ತಕ್ಷೇಪವಿಲ್ಲದೆ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಸೇರಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ಹೋಮ್ ಸ್ಕ್ರೀನ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಓದಿ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 9f7217b..38cf0db 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"앱 제안 받지 않음"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"예상 앱 고정"</string>
     <string name="bubble" msgid="3072951361014076670">"풍선"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"바로가기 설치"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"앱이 사용자의 작업 없이 바로가기를 추가할 수 있도록 합니다."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"홈 설정 및 바로가기 읽기"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index b7ce9d7..407c036 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Cунушталбасын"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Божомолдонгон колдонмону кадап коюу"</string>
     <string name="bubble" msgid="3072951361014076670">"Көбүкчө"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"тез чакырмаларды орнотуу"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Колдонмого колдонуучуга кайрылбастан тез чакырма кошууга уруксат берет."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"үйдүн параметрлерин жана ыкчам баскычтарын окуу"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 33fff3d..cdb3eab 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ຢ່າແນະນຳແອັບ"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"ປັກໝຸດການຄາດເດົາ"</string>
     <string name="bubble" msgid="3072951361014076670">"ຟອງ"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ຕິດຕັ້ງທາງລັດ"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ອະນຸຍາດໃຫ້ແອັບຯ ເພີ່ມທາງລັດໂດຍບໍ່ຕ້ອງຮັບການຢືນຢັນຈາກຜູ່ໃຊ້."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ອ່ານການຕັ້ງຄ່າໜ້າຫຼັກ ແລະ ທາງລັດ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 0d0f5db..78322c6 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Nesiūlyti programos"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Prisegti numatymą"</string>
     <string name="bubble" msgid="3072951361014076670">"Debesėlis"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"įdiegti sparčiuosius klavišus"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Programai leidžiama pridėti sparčiuosius klavišus be naudotojo įsikišimo."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"skaityti pagrindinio ekrano nustatymus ir sparčiuosius klavišus"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index c86c6bb..ff3e105 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Neieteikt lietotni"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Piespraust prognozēto lietotni"</string>
     <string name="bubble" msgid="3072951361014076670">"Burbulis"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalēt saīsnes"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Ļauj lietotnei pievienot saīsnes, nejautājot lietotājam."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"sākuma ekrāna iestatījumu un saīšņu lasīšana"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 2685c60..02de271 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -68,10 +68,8 @@
     <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Работни"</string>
     <string name="widget_category_conversations" msgid="8894438636213590446">"Разговори"</string>
     <string name="widget_category_note_taking" msgid="3469689394504266039">"Фаќање белешки"</string>
-    <!-- no translation found for widget_cell_tap_to_show_add_button_label (4354194214317043581) -->
-    <skip />
-    <!-- no translation found for widget_cell_tap_to_hide_add_button_label (6117805205101555997) -->
-    <skip />
+    <string name="widget_cell_tap_to_show_add_button_label" msgid="4354194214317043581">"Прикажи го копчето за додавање"</string>
+    <string name="widget_cell_tap_to_hide_add_button_label" msgid="6117805205101555997">"Скриј го копчето за додавање"</string>
     <string name="widget_add_button_label" msgid="2761267068711937179">"Додај"</string>
     <string name="widget_add_button_content_description" msgid="1810530016360039643">"Додај го виџетот <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list_expand_button_label" msgid="7912016136574932622">"Прикажи ги сите"</string>
@@ -102,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Не предлагај апл."</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Закачи го предвидувањето"</string>
     <string name="bubble" msgid="3072951361014076670">"Балонче"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталирање кратенки"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Овозможува апликацијата да додава кратенки без интервенција на корисникот."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"да чита поставки и кратенки на почетна страница"</string>
@@ -203,7 +203,7 @@
     <string name="remote_action_failed" msgid="1383965239183576790">"Не успеа: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Приватен простор"</string>
     <string name="private_space_secondary_label" msgid="9203933341714508907">"Допрете за да поставите или отворите"</string>
-    <string name="ps_container_title" msgid="4391796149519594205">"Приватен простор"</string>
+    <string name="ps_container_title" msgid="4391796149519594205">"Приватен"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Поставки за „Приватен простор“"</string>
     <string name="ps_container_unlock_button_content_description" msgid="9181551784092204234">"Приватно, отклучено."</string>
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Приватно, заклучено."</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 2c82585..567559e 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ആപ്പ് നിർദ്ദേശിക്കേണ്ട"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"പ്രവചനം പിൻ ചെയ്യുക"</string>
     <string name="bubble" msgid="3072951361014076670">"ബബിൾ"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"കുറുക്കുവഴികൾ ഇൻസ്റ്റാളുചെയ്യുക"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ഉപയോക്തൃ ഇടപെടൽ ഇല്ലാതെ കുറുക്കുവഴികൾ ചേർക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ഹോം ക്രമീകരണവും കുറുക്കുവഴികളും വായിക്കുക"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 434a731..3ff8a7b 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Апп бүү санал болго"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Таамаглалыг бэхлэх"</string>
     <string name="bubble" msgid="3072951361014076670">"Бөмбөлөг"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"товчлол суулгах"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Апп нь хэрэглэгчийн оролцоогүйгээр товчлолыг нэмэж чадна"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"нүүрний тохиргоо болон товчлолыг унших"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index c872cc6..bc29467 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ॲप सुचवू नका"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"पूर्वानुमान पिन करा"</string>
     <string name="bubble" msgid="3072951361014076670">"बबल"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"शॉर्टकट इंस्टॉल करा"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"वापरकर्ता हस्तक्षेपाशिवाय शॉर्टकट जोडण्यास अ‍ॅप ला अनुमती देते."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"होम सेटिंग्ज आणि शॉर्टकट वाचा"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index f5dca93..016d4b1 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Jangan cadangkan apl"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Sematkan Ramalan"</string>
     <string name="bubble" msgid="3072951361014076670">"Gelembung"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"pasang pintasan"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Membenarkan apl menambah pintasan tanpa campur tangan pengguna."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"membaca tetapan dan pintasan skrin utama"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 24f4435..e26a4dc 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"အက်ပ်အကြံမပြုပါနှင့်"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"ခန့်မှန်းချက်ကို ပင်ထိုးရန်"</string>
     <string name="bubble" msgid="3072951361014076670">"ပူဖောင်းကွက်"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ဖြတ်လမ်းလင့်ခ်များ ထည့်သွင်းခြင်း"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"အသုံးပြုသူ လုပ်ဆောင်မှုမရှိပဲ အပ်ပလီကေးရှင်းကို အတိုကောက်မှတ်သားမှုများ ပြုလုပ်ခွင့် ပေးခြင်း"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ပင်မဆက်တင်နှင့် ဖြတ်လမ်းလင့်ခ်များ ဖတ်ခြင်း"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index a79740d..069e232 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ikke foreslå app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Fest forslaget"</string>
     <string name="bubble" msgid="3072951361014076670">"Boble"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installere snarveier"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Gir apper tillatelse til å legge til snarveier uten innblanding fra brukeren."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"lese startsideinnstillinger og -snarveier"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index f35b943..cfa4740 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"एप सिफारिस नगर्नुहोस्"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"सिफारिस गरिएको एप पिन गर्नुहोस्"</string>
     <string name="bubble" msgid="3072951361014076670">"बबल"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"सर्टकट स्थापना गर्नेहोस्"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"प्रयोगकर्ताको हस्तक्षेप बिना एउटा एपलाई सर्टकटमा थप्नको लागि अनुमति दिनुहोस्।"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"होम स्क्रिनका सेटिङ र सर्टकटहरू रिड गर्नुहोस्"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 5083976..39120f9 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Geen app voorstellen"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Voorspelling vastzetten"</string>
     <string name="bubble" msgid="3072951361014076670">"Bubbel"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Snelle links instellen"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Een app toestaan snelkoppelingen toe te voegen zonder tussenkomst van de gebruiker."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"instellingen en snelkoppelingen op startscherm lezen"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index a26a7c5..6351de1 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ଆପ ପରାମର୍ଶ ଦିଅନ୍ତୁ ନାହିଁ"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"ପୂର୍ବାନୁମାନକୁ ପିନ୍ କରନ୍ତୁ"</string>
     <string name="bubble" msgid="3072951361014076670">"ବବଲ"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ସର୍ଟକଟ୍‍ ଇନଷ୍ଟଲ୍‌ କରନ୍ତୁ"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ୟୁଜରଙ୍କ ବିନା ହସ୍ତକ୍ଷେପରେ ଶର୍ଟକଟ୍‌ ଯୋଡ଼ିବାକୁ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ହୋମ ସେଟିଂସ ଏବଂ ସର୍ଟକଟଗୁଡ଼ିକୁ ପଢ଼ନ୍ତୁ"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index feda5b5..c7c6319 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ਐਪ ਦਾ ਸੁਝਾਅ ਨਾ ਦਿਓ"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"ਪੂਰਵ-ਅਨੁਮਾਨ ਪਿੰਨ ਕਰੋ"</string>
     <string name="bubble" msgid="3072951361014076670">"ਬਬਲ"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ਸ਼ਾਰਟਕੱਟ ਸਥਾਪਤ ਕਰੋ"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ਇੱਕ ਐਪ ਨੂੰ ਵਰਤੋਂਕਾਰ ਦੇ ਦਖ਼ਲ ਤੋਂ ਬਿਨਾਂ ਸ਼ਾਰਟਕੱਟ ਸ਼ਾਮਲ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਪੜ੍ਹੋ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index bf1299d..b8cc4b8 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Nie proponuj aplikacji"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Przypnij podpowiedź"</string>
     <string name="bubble" msgid="3072951361014076670">"Dymek"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Instalowanie skrótów"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Pozwala aplikacji dodawać skróty bez interwencji użytkownika."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"Odczytuje ustawienia i skróty na ekranie głównym"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 17af437..aa6062d 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Não sugerir app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Fixar previsão"</string>
     <string name="bubble" msgid="3072951361014076670">"Balão"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atalhos"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a uma app adicionar atalhos sem a intervenção do utilizador."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ler definições e atalhos do ecrã Principal"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 4bc8e7c..2435d8d 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Não sugerir esse app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Fixar previsão"</string>
     <string name="bubble" msgid="3072951361014076670">"Balão"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atalhos"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que um app adicione atalhos sem intervenção do usuário."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ler configurações e atalhos da tela inicial"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 5763edb..d1fcefa 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Nu sugera aplicația"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Fixează predicția"</string>
     <string name="bubble" msgid="3072951361014076670">"Balon"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalează comenzi rapide"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite unei aplicații să adauge comenzi rapide fără intervenția utilizatorului."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"citește setările și comenzile rapide de pe ecranul de pornire"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index d9f0b40..dd59b78 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Не рекомендовать"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Закрепить рекомендацию"</string>
     <string name="bubble" msgid="3072951361014076670">"Подсказка"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Создание ярлыков"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Приложение сможет самостоятельно добавлять ярлыки."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"Доступ к данным о настройках и ярлыках на главном экране"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 756d6fe..d143032 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"යෙදුම යෝජනා නොකරන්න"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"පුරෝකථනය අමුණන්න"</string>
     <string name="bubble" msgid="3072951361014076670">"බුබුළ"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"කෙටිමං ස්ථාපනය කරන්න"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"පරිශීලක මැදිහත්වීමෙන් තොරව කෙටිමං එක් කිරීමට යෙදුමකට අවසර දෙයි."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"මුල් පිටු සැකසීම් සහ කෙටි මං කියවන්න"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 5fc0c41..c5b84a1 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Nenavrhovať aplikáciu"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Pripnúť predpoveď"</string>
     <string name="bubble" msgid="3072951361014076670">"Bublina"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"inštalácia odkazov"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Povoľuje aplikácii pridať odkazy bez zásahu používateľa."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"čítanie nastavení a odkazov plochy"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 5ab0106..263a7f6 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlagaj"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Predvidevanje pripenjanja"</string>
     <string name="bubble" msgid="3072951361014076670">"Mehurček"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"namestitev bližnjic"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Aplikaciji dovoli dodajanje bližnjic brez posredovanja uporabnika."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"branje nastavitev in bližnjic na začetnem zaslonu"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 995db0f..e8fd7cb 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -68,10 +68,8 @@
     <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Puna"</string>
     <string name="widget_category_conversations" msgid="8894438636213590446">"Bisedat"</string>
     <string name="widget_category_note_taking" msgid="3469689394504266039">"Mbajtja e shënimeve"</string>
-    <!-- no translation found for widget_cell_tap_to_show_add_button_label (4354194214317043581) -->
-    <skip />
-    <!-- no translation found for widget_cell_tap_to_hide_add_button_label (6117805205101555997) -->
-    <skip />
+    <string name="widget_cell_tap_to_show_add_button_label" msgid="4354194214317043581">"Shfaq butonin e shtimit"</string>
+    <string name="widget_cell_tap_to_hide_add_button_label" msgid="6117805205101555997">"Fshih butonin e shtimit"</string>
     <string name="widget_add_button_label" msgid="2761267068711937179">"Shto"</string>
     <string name="widget_add_button_content_description" msgid="1810530016360039643">"Shto miniaplikacionin <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list_expand_button_label" msgid="7912016136574932622">"Shfaq të gjitha"</string>
@@ -102,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Mos sugjero aplikacion"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Gozhdo parashikimin"</string>
     <string name="bubble" msgid="3072951361014076670">"Flluskë"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalimi i shkurtoreve"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Lejon një aplikacion të shtojë shkurtore pa ndërhyrjen e përdoruesit."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"lexo cilësimet dhe shkurtoret e ekranit bazë"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 2b2fac8..4ef941a 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Не предлажи апликацију"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Закачи предвиђање"</string>
     <string name="bubble" msgid="3072951361014076670">"Облачић"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталирање пречица"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Дозвољава апликацији да додаје пречице без интервенције корисника."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"читање подешавања и пречица на почетном екрану"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 74e35ac..26b1a3b 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Föreslå inte app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Fäst förslag"</string>
     <string name="bubble" msgid="3072951361014076670">"Bubbla"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installera genvägar"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Tillåter att en app lägger till genvägar utan åtgärd från användaren."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"läsa inställningar och genvägar på startskärmen"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index d696410..0426cc5 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Isipendekeze programu"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Bandika Utabiri"</string>
     <string name="bubble" msgid="3072951361014076670">"Kiputo"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"kuweka njia za mkato"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Huruhusu programu kuongeza njia za mkato bila mtumiaji kuingilia kati."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"kusoma mipangilio ya skrini ya kwanza na njia za mkato"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 0b94252..90f5d80 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"பரிந்துரைக்காதே"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"கணிக்கப்பட்டதைப் பின் செய்"</string>
     <string name="bubble" msgid="3072951361014076670">"குமிழ்"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"குறுக்குவழிகளை நிறுவுதல்"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"பயனரின் அனுமதி இல்லாமல் குறுக்குவழிகளைச் சேர்க்கப் ஆப்ஸை அனுமதிக்கிறது."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"முகப்புத் திரையின் அமைப்புகளையும் ஷார்ட்கட்களையும் படித்தல்"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 90ebe1e..1fc169a 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"యాప్ సూచించకు"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"సూచనను పిన్ చేయండి"</string>
     <string name="bubble" msgid="3072951361014076670">"బబుల్"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"షార్ట్‌కట్‌లను ఇన్‌స్టాల్ చేయడం"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"వినియోగదారు ప్రమేయం లేకుండా షార్ట్‌కట్‌లను జోడించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"హోమ్ సెట్టింగ్‌లు, షార్ట్‌కట్‌లను చదవండి"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 08944ed..d8484be 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ไม่ต้องแนะนำแอป"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"ปักหมุดแอปที่คาดการณ์ไว้"</string>
     <string name="bubble" msgid="3072951361014076670">"บับเบิล"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ติดตั้งทางลัด"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"อนุญาตให้แอปเพิ่มทางลัดโดยไม่ต้องให้ผู้ใช้จัดการ"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"อ่านการตั้งค่าและทางลัดในหน้าแรก"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index b504adc..5596eb5 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Huwag magmungkahi"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"I-pin ang Hula"</string>
     <string name="bubble" msgid="3072951361014076670">"Bubble"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"i-install ang mga shortcut"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Pinapayagan ang isang app na magdagdag ng mga shortcut nang walang panghihimasok ng user."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"basahin ang mga setting at shortcut ng home"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 094b597..ca96d8e 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Uygulamayı önerme"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Tahmini Sabitle"</string>
     <string name="bubble" msgid="3072951361014076670">"Balon"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"kısayolları yükle"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Uygulamaya, kullanıcı müdahalesi olmadan kısayol ekleme izni verir."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ana ekran ayarlarını ve kısayollarını oku"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index f90998d..9b02400 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Не пропонувати додаток"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Закріпити передбачений додаток"</string>
     <string name="bubble" msgid="3072951361014076670">"Повідомлення"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"створення ярликів"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Дозволяє програмі самостійно додавати ярлики."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"читати налаштування та ярлики головного екрана"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 1a90602..a9f7510 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ایپ تجویز نہ کریں"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"پیشگوئی پن کریں"</string>
     <string name="bubble" msgid="3072951361014076670">"بلبلہ"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"شارٹ کٹس انسٹال کریں"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"کسی ایپ کو صارف کی مداخلت کے بغیر شارٹ کٹس شامل کرنے کی اجازت دیتا ہے۔"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ہوم ترتیبات اور شارٹ کٹس کو پڑھیں"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 62fede8..3c800ae 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Tavsiya qilinmasin"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Tavsiyani mahkamlash"</string>
     <string name="bubble" msgid="3072951361014076670">"Pufaklar"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"yorliqlar yaratish"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Ilovalarga foydalanuvchidan so‘ramasdan yorliqlar qo‘shishga ruxsat beradi."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"Bosh ekrandagi sozlamalar va yorliqlarni koʻrish"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 8e5f75c..2daa1db 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Không gợi ý ứng dụng"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Ghim ứng dụng dự đoán"</string>
     <string name="bubble" msgid="3072951361014076670">"Bong bóng"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"cài đặt lối tắt"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Cho phép ứng dụng thêm lối tắt mà không cần sự can thiệp của người dùng."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"đọc lối tắt và các chế độ cài đặt trên màn hình chính"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index edc5646..01426df 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"不要推荐此应用"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"固定预测的应用"</string>
     <string name="bubble" msgid="3072951361014076670">"气泡框"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"安装快捷方式"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"允许应用自行添加快捷方式。"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"读取主屏幕设置和快捷方式"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 7f4ac59..0e7a44d 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"不要提供應用程式建議"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"固定預測"</string>
     <string name="bubble" msgid="3072951361014076670">"氣泡"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"安裝捷徑"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"允許應用程式無需使用者許可也可新增捷徑。"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"讀取主畫面設定和捷徑"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index f527a2a..56fb080 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"不要建議此應用程式"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"固定預測的應用程式"</string>
     <string name="bubble" msgid="3072951361014076670">"泡泡"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"安裝捷徑"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"允許應用程式自動新增捷徑。"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"讀取主畫面設定和捷徑"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 3c3a75c..9f479b9 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -100,6 +100,8 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ungaphakamisi uhlelo lokusebenza"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Ukubikezela Iphinikhodi"</string>
     <string name="bubble" msgid="3072951361014076670">"Ibhamuza"</string>
+    <!-- no translation found for pin_to_taskbar (1281337899690299038) -->
+    <skip />
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"faka izinqamuleli"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Ivumela uhlelo lokusebenza ukufaka izinqamuleli ngaphandle kokungenelela komsebenzisi."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"funda amasethingi wasekhaya nezinqamuleli"</string>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2fc039e..6f293b6 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -190,6 +190,8 @@
     <!-- Label for the header text of the All Apps section in All Apps view, used to separate Predicted Apps and Actions section from All Apps section. [CHAR_LIMIT=50] -->
     <string name="all_apps_label">All apps</string>
 
+    <string name="all_apps_list_label">Apps list</string>
+
     <!-- Popup items -->
     <!-- Text to display as the header above notifications. [CHAR_LIMIT=30] -->
     <string name="notifications_header">Notifications</string>
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 1fc14ec..c78666e 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -743,7 +743,7 @@
         appTitleBounds = new RectF((tmpRect.width() - titleLength) / 2.f - getCompoundPaddingLeft(),
                 0, (tmpRect.width() + titleLength) / 2.f + getCompoundPaddingRight(),
                 (int) Math.ceil(fm.bottom - fm.top));
-        appTitleBounds.inset(mRoundRectPadding * 2, 0);
+        appTitleBounds.inset((mAppTitleHorizontalPadding) * 2, 0);
 
 
         if (mIcon != null) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0530ff4..c37b56c 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -58,6 +58,7 @@
 import static com.android.launcher3.LauncherConstants.TraceEvents.ON_NEW_INTENT_EVT;
 import static com.android.launcher3.LauncherConstants.TraceEvents.ON_RESUME_EVT;
 import static com.android.launcher3.LauncherConstants.TraceEvents.ON_START_EVT;
+import static com.android.launcher3.LauncherPrefs.FIXED_LANDSCAPE_MODE;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
 import static com.android.launcher3.LauncherState.ALL_APPS;
@@ -785,9 +786,13 @@
             return;
         }
         // When the flag oneGridSpecs is on we want to disable ALLOW_ROTATION which is replaced
-        // by FIXED_LANDSCAPE_MODE, ALLOW_ROTATION will only be used on Tablets afterwards.
-        if (getDeviceProfile().isPhone || getDeviceProfile().isTwoPanels) {
+        // by FIXED_LANDSCAPE_MODE, ALLOW_ROTATION will only be used on Tablets and foldables
+        // afterwards.
+        if (getDeviceProfile().isPhone) {
             LauncherPrefs.get(this).put(LauncherPrefs.ALLOW_ROTATION, false);
+        } else if (getDeviceProfile().isTablet) {
+            // Tablet do not use fixed landscape mode, make sure it can't be activated by mistake
+            LauncherPrefs.get(this).put(FIXED_LANDSCAPE_MODE, false);
         }
         getRotationHelper().setFixedLandscape(
                 Objects.requireNonNull(mDeviceProfile.inv).isFixedLandscape
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 68a6e62..ba6ed66 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -24,6 +24,7 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.ButtonDropTarget;
 import com.android.launcher3.CellLayout;
@@ -48,7 +49,6 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.ArrowPopup;
 import com.android.launcher3.popup.PopupContainerWithArrow;
-import com.android.launcher3.shortcuts.DeepShortcutTextView;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.IntArray;
@@ -405,6 +405,11 @@
      */
     public boolean addToWorkspace(ItemInfo item, boolean accessibility,
             @Nullable Consumer<Boolean> finishCallback) {
+        // Dismiss widget resize frame if it is showing. The frame marks its cells as unoccupied
+        // while it is showing, so findSpaceOnWorkspace may try to use those cells.
+        AbstractFloatingView.closeOpenViews(mContext, /* animate= */ false,
+                AbstractFloatingView.TYPE_WIDGET_RESIZE_FRAME);
+
         final int[] coordinates = new int[2];
         final int screenId = findSpaceOnWorkspace(item, coordinates);
         if (screenId == -1) {
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 29b9e77..9afe06c 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -261,12 +261,14 @@
         writer.println(prefix + "\tAllAppsStore Apps[] size: " + mApps.length);
         for (int i = 0; i < mApps.length; i++) {
             writer.println(String.format(Locale.getDefault(),
-                    "%s\tPackage index, name, class, and description: %d/%s:%s, %s",
+                    "%s\tPackage index, name, class, description, bitmap flag: %d/%s:%s, %s, %s+%s",
                     prefix,
                     i,
                     mApps[i].componentName.getPackageName(),
                     mApps[i].componentName.getClassName(),
-                    mApps[i].contentDescription));
+                    mApps[i].contentDescription,
+                    Integer.toBinaryString(mApps[i].bitmap.flags),
+                    Integer.toBinaryString(mApps[i].bitmap.creationFlags)));
         }
     }
 }
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index 7367f2e..eaca6c5 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -139,7 +139,7 @@
 
         switch (path) {
             case KEY_SHAPE_OPTIONS: {
-                if (Flags.newCustomizationPickerUi()) {
+                if (Flags.newCustomizationPickerUi() && Flags.enableLauncherIconShapes()) {
                     MatrixCursor cursor = new MatrixCursor(new String[]{
                             KEY_SHAPE_KEY, KEY_SHAPE_TITLE, KEY_PATH, KEY_IS_DEFAULT});
                     List<AppShape> shapes =  AppShapesProvider.INSTANCE.getShapes();
@@ -355,7 +355,7 @@
                     renderer.hideBottomRow(message.getData().getBoolean(KEY_HIDE_BOTTOM_ROW));
                     break;
                 case MESSAGE_ID_UPDATE_SHAPE:
-                    if (Flags.newCustomizationPickerUi()) {
+                    if (Flags.newCustomizationPickerUi() && Flags.enableLauncherIconShapes()) {
                         String shapeKey = message.getData().getString(KEY_SHAPE_KEY);
                         Optional<AppShape> optionalShape = AppShapesProvider.INSTANCE.getShapes()
                                 .stream()
diff --git a/src/com/android/launcher3/model/GridSizeMigrationLogic.kt b/src/com/android/launcher3/model/GridSizeMigrationLogic.kt
index 75fd31e..6f86ae0 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationLogic.kt
+++ b/src/com/android/launcher3/model/GridSizeMigrationLogic.kt
@@ -30,6 +30,7 @@
 import com.android.launcher3.LauncherSettings.Favorites.TMP_TABLE
 import com.android.launcher3.Utilities
 import com.android.launcher3.config.FeatureFlags
+import com.android.launcher3.logging.FileLog
 import com.android.launcher3.model.GridSizeMigrationDBController.DbReader
 import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction
 import com.android.launcher3.provider.LauncherDbUtils.copyTable
@@ -57,10 +58,10 @@
             return
         }
 
-        val isFirstLoad = get(context).get(LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE)
-        Log.d(
+        val isAfterRestore = get(context).get(LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE)
+        FileLog.d(
             TAG,
-            "Begin grid migration. First load: $isFirstLoad\n srcDeviceState: " +
+            "Begin grid migration. isAfterRestore: $isAfterRestore\nsrcDeviceState: " +
                 "$srcDeviceState\ndestDeviceState: $destDeviceState\nisDestNewDb: $isDestNewDb",
         )
 
@@ -112,7 +113,7 @@
                 t.commit()
             }
         } catch (e: Exception) {
-            Log.e(TAG, "Error during grid migration", e)
+            FileLog.e(TAG, "Error during grid migration", e)
         } finally {
             Log.v(
                 TAG,
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 4355ac4..bc0d776 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -393,7 +393,7 @@
             }
         } catch (CancellationException e) {
             // Loader stopped, ignore
-            FileLog.w(TAG, "LoaderTask cancelled:", e);
+            FileLog.w(TAG, "LoaderTask cancelled");
         } catch (Exception e) {
             memoryLogger.printLogs();
             throw e;
@@ -402,7 +402,7 @@
     }
 
     public synchronized void stopLocked() {
-        FileLog.w(TAG, "LoaderTask#stopLocked:", new Exception());
+        FileLog.w(TAG, "stopLocked: Loader stopping");
         mStopped = true;
         this.notify();
     }
diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java
index 6ff8547..e76391f 100644
--- a/src/com/android/launcher3/model/ModelDbController.java
+++ b/src/com/android/launcher3/model/ModelDbController.java
@@ -96,7 +96,7 @@
  * around it.
  */
 public class ModelDbController {
-    private static final String TAG = "LauncherProvider";
+    private static final String TAG = "ModelDbController";
 
     private static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
     public static final String EXTRA_DB_NAME = "db_name";
@@ -303,7 +303,7 @@
         if (restoreEventLogger != null) {
             sendMetricsForFailedMigration(restoreEventLogger, getDb());
         }
-        FileLog.d(TAG, "Migration failed: resetting launcher database");
+        FileLog.d(TAG, "resetLauncherDb: Migration failed: resetting launcher database");
         createEmptyDB();
         LauncherPrefs.get(mContext).putSync(
                 getEmptyDbCreatedKey(mOpenHelper.getDatabaseName()).to(true));
@@ -331,7 +331,7 @@
     private boolean isThereExistingDb() {
         if (LauncherPrefs.get(mContext).get(getEmptyDbCreatedKey())) {
             // If we already have a new DB, ignore migration
-            FileLog.d(TAG, "migrateGridIfNeeded: new DB already created, skipping migration");
+            FileLog.d(TAG, "isThereExistingDb: new DB already created, skipping migration");
             return true;
         }
         return false;
@@ -342,7 +342,7 @@
         if (GridSizeMigrationDBController.needsToMigrate(mContext, idp)) {
             return true;
         }
-        FileLog.d(TAG, "migrateGridIfNeeded: no grid migration needed");
+        FileLog.d(TAG, "isGridMigrationNecessary: no grid migration needed");
         return false;
     }
 
@@ -350,7 +350,9 @@
         InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext);
         String targetDbName = new DeviceGridState(idp).getDbFile();
         if (TextUtils.equals(targetDbName, mOpenHelper.getDatabaseName())) {
-            FileLog.e(TAG, "migrateGridIfNeeded: target db is same as current: " + targetDbName);
+            FileLog.e(TAG, "isCurrentDbSameAsTarget: target db is same as current"
+                    + " current db: " + mOpenHelper.getDatabaseName()
+                    + " target db: " + targetDbName);
             return true;
         }
         return false;
@@ -385,13 +387,12 @@
             DeviceGridState destDeviceState = new DeviceGridState(idp);
 
             boolean isDestNewDb = !existingDBs.contains(destDeviceState.getDbFile());
-
             GridSizeMigrationLogic gridSizeMigrationLogic = new GridSizeMigrationLogic();
             gridSizeMigrationLogic.migrateGrid(mContext, srcDeviceState, destDeviceState,
                     mOpenHelper, oldHelper.getWritableDatabase(), isDestNewDb);
         } catch (Exception e) {
             resetLauncherDb(restoreEventLogger);
-            throw new Exception("Failed to migrate grid", e);
+            throw new Exception("attemptMigrateDb: Failed to migrate grid", e);
         } finally {
             if (mOpenHelper != oldHelper) {
                 oldHelper.close();
@@ -415,7 +416,7 @@
                     sendMetricsForFailedMigration(restoreEventLogger, getDb());
                 }
             }
-            FileLog.d(TAG, "Migration failed: resetting launcher database");
+            FileLog.d(TAG, "tryMigrateDB: Migration failed: resetting launcher database");
             createEmptyDB();
             LauncherPrefs.get(mContext).putSync(
                     getEmptyDbCreatedKey(mOpenHelper.getDatabaseName()).to(true));
@@ -447,17 +448,17 @@
         }
         String targetDbName = new DeviceGridState(idp).getDbFile();
         if (TextUtils.equals(targetDbName, mOpenHelper.getDatabaseName())) {
-            FileLog.e(TAG, "migrateGridIfNeeded: target db is same as current: " + targetDbName);
+            FileLog.e(TAG, "migrateGridIfNeeded: target db is same as current"
+                    + " current db: " + mOpenHelper.getDatabaseName()
+                    + " target db: " + targetDbName);
             return false;
         }
         DatabaseHelper oldHelper = mOpenHelper;
-
         // We save the existing db's before creating the destination db helper so we know what logic
         // to run in grid migration based on if that grid already existed before migration or not.
         List<String> existingDBs = LauncherFiles.GRID_DB_FILES.stream()
                 .filter(dbName -> mContext.getDatabasePath(dbName).exists())
                 .toList();
-
         mOpenHelper = (mContext instanceof SandboxContext) ? oldHelper
                 : createDatabaseHelper(true /* forMigration */, targetDbName);
         try {
@@ -465,13 +466,11 @@
             DeviceGridState srcDeviceState = new DeviceGridState(mContext);
             // This is the state we want to migrate to that is given by the idp
             DeviceGridState destDeviceState = new DeviceGridState(idp);
-
             boolean isDestNewDb = !existingDBs.contains(destDeviceState.getDbFile());
-
             return GridSizeMigrationDBController.migrateGridIfNeeded(mContext, srcDeviceState,
                     destDeviceState, mOpenHelper, oldHelper.getWritableDatabase(), isDestNewDb);
         } catch (Exception e) {
-            FileLog.e(TAG, "Failed to migrate grid", e);
+            FileLog.e(TAG, "migrateGridIfNeeded: Failed to migrate grid", e);
             return false;
         } finally {
             if (mOpenHelper != oldHelper) {
diff --git a/src_no_quickstep/com/android/launcher3/dagger/LauncherAppComponent.java b/src_no_quickstep/com/android/launcher3/dagger/LauncherAppComponent.java
index 63d87e8..f4f5a08 100644
--- a/src_no_quickstep/com/android/launcher3/dagger/LauncherAppComponent.java
+++ b/src_no_quickstep/com/android/launcher3/dagger/LauncherAppComponent.java
@@ -22,7 +22,7 @@
  * Root component for Dagger injection for Launcher AOSP.
  */
 @LauncherAppSingleton
-@Component
+@Component(modules = LauncherAppModule.class)
 public interface LauncherAppComponent extends LauncherBaseAppComponent {
     /** Builder for aosp LauncherAppComponent. */
     @Component.Builder
diff --git a/src_no_quickstep/com/android/launcher3/dagger/LauncherAppModule.java b/src_no_quickstep/com/android/launcher3/dagger/LauncherAppModule.java
new file mode 100644
index 0000000..f7b8489
--- /dev/null
+++ b/src_no_quickstep/com/android/launcher3/dagger/LauncherAppModule.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 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.dagger;
+
+import dagger.Module;
+
+@Module
+public class LauncherAppModule { }
diff --git a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index c80093a..8598917 100644
--- a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -29,6 +29,7 @@
 import androidx.test.core.app.ApplicationProvider.getApplicationContext
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppModule
 import com.android.launcher3.dagger.LauncherAppSingleton
 import com.android.launcher3.testing.shared.ResourceUtils
 import com.android.launcher3.util.DisplayController
@@ -68,7 +69,7 @@
     protected lateinit var context: SandboxContext
     protected open val runningContext: Context = getApplicationContext()
     private val displayController: DisplayController = mock()
-    private val windowManagerProxy: WindowManagerProxy = mock()
+    private val windowManagerProxy: MyWmProxy = mock()
     private val launcherPrefs: LauncherPrefs = mock()
 
     @get:Rule val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT)
@@ -362,12 +363,17 @@
     }
 }
 
+class MyWmProxy : WindowManagerProxy()
+
 @LauncherAppSingleton
-@Component
+@Component(modules = [LauncherAppModule::class])
 interface AbsDPTestSandboxComponent : LauncherAppComponent {
+
+    override fun getWmProxy(): MyWmProxy
+
     @Component.Builder
     interface Builder : LauncherAppComponent.Builder {
-        @BindsInstance fun bindWMProxy(proxy: WindowManagerProxy): Builder
+        @BindsInstance fun bindWMProxy(proxy: MyWmProxy): Builder
 
         override fun build(): AbsDPTestSandboxComponent
     }
diff --git a/tests/multivalentTests/src/com/android/launcher3/AutoInstallsLayoutTest.kt b/tests/multivalentTests/src/com/android/launcher3/AutoInstallsLayoutTest.kt
index f73a9d3..fcbb94b 100644
--- a/tests/multivalentTests/src/com/android/launcher3/AutoInstallsLayoutTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/AutoInstallsLayoutTest.kt
@@ -42,6 +42,7 @@
 import com.android.launcher3.LauncherSettings.Favorites.SPANY
 import com.android.launcher3.LauncherSettings.Favorites._ID
 import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppModule
 import com.android.launcher3.dagger.LauncherAppSingleton
 import com.android.launcher3.model.data.AppInfo
 import com.android.launcher3.pm.UserCache
@@ -66,7 +67,7 @@
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 import org.mockito.kotlin.doReturn
-import org.mockito.kotlin.spy
+import org.mockito.kotlin.mock
 import org.mockito.kotlin.whenever
 
 /** Tests for [AutoInstallsLayout] */
@@ -165,7 +166,9 @@
 
     @Test
     fun work_item_added_to_home() {
-        val apiWrapperMock = spy(ApiWrapper.INSTANCE[targetContext])
+        val original = ApiWrapper.INSTANCE[targetContext]
+        val apiWrapperMock =
+            mock<MyApiWrapper>(defaultAnswer = { it.method.invoke(original, *it.arguments) })
         targetContext.initDaggerComponent(
             DaggerAutoInstallsLayoutTestComponent.builder().bindApiWrapper(apiWrapperMock)
         )
@@ -221,12 +224,17 @@
     }
 }
 
+class MyApiWrapper : ApiWrapper(null) {}
+
 @LauncherAppSingleton
-@Component
+@Component(modules = [LauncherAppModule::class])
 interface AutoInstallsLayoutTestComponent : LauncherAppComponent {
+
+    override fun getApiWrapper(): MyApiWrapper
+
     @Component.Builder
     interface Builder : LauncherAppComponent.Builder {
-        @BindsInstance fun bindApiWrapper(wrapper: ApiWrapper): Builder
+        @BindsInstance fun bindApiWrapper(wrapper: MyApiWrapper): Builder
 
         override fun build(): AutoInstallsLayoutTestComponent
     }
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt
index 32df84c..d0cf610 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt
@@ -30,6 +30,7 @@
 import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING
 import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING_IN_DESKTOP_MODE
 import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppModule
 import com.android.launcher3.dagger.LauncherAppSingleton
 import com.android.launcher3.util.DisplayController.CHANGE_DENSITY
 import com.android.launcher3.util.DisplayController.CHANGE_ROTATION
@@ -64,7 +65,7 @@
 class DisplayControllerTest {
 
     private val context = spy(SandboxModelContext())
-    private val windowManagerProxy: WindowManagerProxy = mock()
+    private val windowManagerProxy: MyWmProxy = mock()
     private val launcherPrefs: LauncherPrefs = mock()
     private lateinit var displayManager: DisplayManager
     private val display: Display = mock()
@@ -230,12 +231,17 @@
     }
 }
 
+class MyWmProxy : WindowManagerProxy()
+
 @LauncherAppSingleton
-@Component
+@Component(modules = [LauncherAppModule::class])
 interface DisplayControllerTestComponent : LauncherAppComponent {
+
+    override fun getWmProxy(): MyWmProxy
+
     @Component.Builder
     interface Builder : LauncherAppComponent.Builder {
-        @BindsInstance fun bindWMProxy(proxy: WindowManagerProxy): Builder
+        @BindsInstance fun bindWMProxy(proxy: MyWmProxy): Builder
 
         override fun build(): DisplayControllerTestComponent
     }
diff --git a/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt b/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt
index f381d4b..94b2c7e 100644
--- a/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt
+++ b/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt
@@ -104,7 +104,7 @@
             .thenReturn(listOf(expectedVerifiedSession))
         // When
         val actualSession =
-            installSessionHelper.getActiveSessionInfo(UserHandle(0), expectedAppPackage)
+            installSessionHelper.getActiveSessionInfo(myUserHandle(), expectedAppPackage)
         // Then
         assertThat(actualSession).isEqualTo(expectedVerifiedSession)
     }
diff --git a/tests/src/com/android/launcher3/popup/SystemShortcutTest.java b/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
index ae54e95..075f667 100644
--- a/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
+++ b/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
@@ -62,15 +62,12 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.PrivateProfileManager;
-import com.android.launcher3.dagger.LauncherAppComponent;
-import com.android.launcher3.dagger.LauncherAppSingleton;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.logging.StatsLogManager.StatsLogger;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.util.ApiWrapper;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext;
 import com.android.launcher3.util.LauncherMultivalentJUnit;
@@ -81,9 +78,6 @@
 import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider;
 import com.android.launcher3.widget.picker.model.data.WidgetPickerData;
 
-import dagger.BindsInstance;
-import dagger.Component;
-
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -109,7 +103,6 @@
     private AppInfo mAppInfo;
 
     @Mock UserCache mUserCache;
-    @Mock ApiWrapper mApiWrapper;
     @Mock UserIconInfo mUserIconInfo;
     @Mock LauncherActivityInfo mLauncherActivityInfo;
     @Mock ApplicationInfo mApplicationInfo;
@@ -120,9 +113,6 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mSandboxContext.initDaggerComponent(
-                DaggerSystemShortcutTest_TestComponent.builder().bindApiWrapper(
-                        ApiWrapper.INSTANCE.get(mSandboxContext)));
         mSandboxContext.putObject(UserCache.INSTANCE, mUserCache);
         mTestContext = new TestSandboxModelContextWrapper(mSandboxContext) {
             @Override
@@ -412,16 +402,4 @@
         systemShortcut.onClick(mView);
         verify(mSandboxContext).startActivity(any());
     }
-
-    @LauncherAppSingleton
-    @Component
-    interface TestComponent extends LauncherAppComponent {
-        @Component.Builder
-        interface Builder extends LauncherAppComponent.Builder {
-            @BindsInstance Builder bindApiWrapper(ApiWrapper wrapper);
-
-            @Override
-            TestComponent build();
-        }
-    }
 }
diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
index e5c5c19..d49168f 100644
--- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
+++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
@@ -1,11 +1,16 @@
 package com.android.launcher3.ui;
 
+import static com.android.launcher3.LauncherPrefs.FIXED_LANDSCAPE_MODE;
+
 import android.util.Log;
 import android.view.Surface;
 
+import com.android.launcher3.Flags;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.tapl.TestHelpers;
 import com.android.launcher3.util.rule.FailureWatcher;
+import com.android.launcher3.util.window.WindowManagerProxy;
 
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
@@ -67,9 +72,11 @@
                     Log.e(TAG, "Error", e);
                     throw e;
                 } finally {
+
                     mTest.mDevice.setOrientationNatural();
                     mTest.executeOnLauncher(launcher ->
                     {
+                        LauncherPrefs.get(launcher).put(FIXED_LANDSCAPE_MODE, false);
                         if (launcher != null) {
                             launcher.getRotationHelper().forceAllowRotationForTesting(false);
                         }
@@ -90,6 +97,13 @@
             }
 
             private void evaluateInLandscape() throws Throwable {
+                if (Flags.oneGridSpecs()
+                        && WindowManagerProxy.INSTANCE.get(mTest.mTargetContext)
+                        .isTaskbarDrawnInProcess()) {
+                    mTest.executeOnLauncher(launcher -> LauncherPrefs.get(launcher)
+                            .put(FIXED_LANDSCAPE_MODE, true)
+                    );
+                }
                 mTest.mDevice.setOrientationLeft();
                 mTest.mLauncher.setExpectedRotation(Surface.ROTATION_90);
                 AbstractLauncherUiTest.checkDetectedLeaks(mTest.mLauncher, true);