Merge "Fix activity leak" into main
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index edbea88..80d2eac 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -179,7 +179,7 @@
             </intent-filter>
         </activity>
 
-        <!-- [b/197780098] Disable eager initialization of Jetpack libraries. -->
+        <!-- Disable eager initialization of Jetpack libraries. See bug 197780098. -->
         <provider
             android:name="androidx.startup.InitializationProvider"
             android:authorities="${applicationId}.androidx-startup"
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index dd78ca4..8682e5d 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -277,3 +277,17 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+    name: "enabled_folders_in_all_apps"
+    namespace: "launcher"
+    description: "Enables folders in all apps"
+    bug: "341582436"
+}
+
+flag {
+    name: "enable_tiny_taskbar"
+    namespace: "launcher"
+    description: "Enables Taskbar on phones"
+    bug: "341784466"
+}
diff --git a/aconfig/launcher_search.aconfig b/aconfig/launcher_search.aconfig
index 31d8d34..b243922 100644
--- a/aconfig/launcher_search.aconfig
+++ b/aconfig/launcher_search.aconfig
@@ -42,3 +42,11 @@
     description: "This flag disables drag and drop for Private Space Items."
     bug: "289223923"
 }
+
+
+flag {
+    name: "private_space_floating_mask_view"
+    namespace: "launcher_search"
+    description: "This flag enables the floating mask view as part of the Private Space animation. "
+    bug: "339850589"
+}
diff --git a/go/quickstep/res/layout/overview_actions_container.xml b/go/quickstep/res/layout/overview_actions_container.xml
index df09124..b1a6202 100644
--- a/go/quickstep/res/layout/overview_actions_container.xml
+++ b/go/quickstep/res/layout/overview_actions_container.xml
@@ -121,6 +121,17 @@
             android:layout_weight="1"
             android:visibility="gone" />
 
+    </LinearLayout>
+
+    <!-- Unused. Included only for compatibility with parent class. -->
+    <LinearLayout
+        android:id="@+id/group_action_buttons"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/overview_actions_height"
+        android:layout_gravity="top|center_horizontal"
+        android:orientation="horizontal"
+        android:visibility="gone">
+
         <Button
             android:id="@+id/action_save_app_pair"
             style="@style/GoOverviewActionButton"
@@ -128,8 +139,8 @@
             android:layout_height="wrap_content"
             android:drawableStart="@drawable/ic_save_app_pair_up_down"
             android:text="@string/action_save_app_pair"
-            android:theme="@style/ThemeControlHighlightWorkspaceColor"
-            android:visibility="gone" />
+            android:theme="@style/ThemeControlHighlightWorkspaceColor" />
+
     </LinearLayout>
 
 </com.android.quickstep.views.GoOverviewActionsView>
\ No newline at end of file
diff --git a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
index 0eb8775..26ca06a 100644
--- a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
+++ b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
@@ -56,7 +56,7 @@
 import com.android.quickstep.util.AssistContentRequester;
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.views.GoOverviewActionsView;
-import com.android.quickstep.views.TaskThumbnailViewDeprecated;
+import com.android.quickstep.views.TaskView.TaskContainer;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
@@ -101,8 +101,8 @@
     /**
      * Create a new overlay instance for the given View
      */
-    public TaskOverlayGo createOverlay(TaskThumbnailViewDeprecated thumbnailView) {
-        return new TaskOverlayGo(thumbnailView, mContentRequester);
+    public TaskOverlayGo createOverlay(TaskContainer taskContainer) {
+        return new TaskOverlayGo(taskContainer, mContentRequester);
     }
 
     /**
@@ -120,9 +120,9 @@
         private OverlayDialogGo mDialog;
         private ArrowTipView mArrowTipView;
 
-        private TaskOverlayGo(TaskThumbnailViewDeprecated taskThumbnailView,
+        private TaskOverlayGo(TaskContainer taskContainer,
                 AssistContentRequester assistContentRequester) {
-            super(taskThumbnailView);
+            super(taskContainer);
             mFactoryContentRequester = assistContentRequester;
             mSharedPreferences = LauncherPrefs.getPrefs(mApplicationContext);
         }
@@ -148,7 +148,8 @@
             // Disable Overview Actions for Work Profile apps
             boolean isManagedProfileTask =
                     UserManager.get(mApplicationContext).isManagedProfile(task.key.userId);
-            boolean isAllowedByPolicy = mThumbnailView.isRealSnapshot() && !isManagedProfileTask;
+            boolean isAllowedByPolicy = mTaskContainer.getThumbnailViewDeprecated().isRealSnapshot()
+                    && !isManagedProfileTask;
             getActionsView().setCallbacks(new OverlayUICallbacksGoImpl(isAllowedByPolicy, task));
             mTaskPackageName = task.key.getPackageName();
             mSharedPreferences = LauncherPrefs.getPrefs(mApplicationContext);
@@ -162,8 +163,7 @@
             int taskId = task.key.id;
             mFactoryContentRequester.requestAssistContent(taskId, this::onAssistContentReceived);
 
-            RecentsOrientedState orientedState =
-                    mThumbnailView.getTaskView().getRecentsView().getPagedViewOrientedState();
+            RecentsOrientedState orientedState = mTaskContainer.getTaskView().getOrientedState();
             boolean isInLandscape = orientedState.getDisplayRotation() != ROTATION_0;
 
             // show tooltips in portrait mode only
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index 7c648b6..823c821 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -121,6 +121,20 @@
 }
 
 message TaskSwitcherContainer {
+  /**
+   * Indicates the current OrientationHandler in use in Overview.
+   * In fake landscape, the value will be
+   * {@link com.android.quickstep.orientation.LandscapePagedViewHandler} and in real landscape,
+   * the value will be {@link com.android.quickstep.orientation.PortraitPagedViewHandler} for
+   * example.
+   */
+  optional OrientationHandler orientation_handler = 1;
+
+  enum OrientationHandler {
+    PORTRAIT = 0;
+    LANDSCAPE = 1;
+    SEASCAPE = 2;
+  }
 }
 
 // Container for taskbar.
diff --git a/quickstep/res/drawable/bg_bubble_expanded_view_drop_target.xml b/quickstep/res/drawable/bg_bubble_expanded_view_drop_target.xml
index 98aab67..d722dd7 100644
--- a/quickstep/res/drawable/bg_bubble_expanded_view_drop_target.xml
+++ b/quickstep/res/drawable/bg_bubble_expanded_view_drop_target.xml
@@ -13,12 +13,15 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    android:shape="rectangle">
-    <corners android:radius="@dimen/bubble_expanded_view_drop_target_corner_radius" />
-    <solid android:color="@color/bubblebar_drop_target_bg_color" />
-    <stroke
-        android:width="1dp"
-        android:color="?androidprv:attr/materialColorPrimaryContainer" />
-</shape>
+    android:inset="@dimen/bubble_expanded_view_drop_target_padding">
+    <shape
+        android:shape="rectangle">
+        <corners android:radius="@dimen/bubble_expanded_view_drop_target_corner_radius" />
+        <solid android:color="@color/bubblebar_drop_target_bg_color" />
+        <stroke
+            android:width="1dp"
+            android:color="?androidprv:attr/materialColorPrimaryContainer" />
+    </shape>
+</inset>
diff --git a/quickstep/res/layout/bubble_expanded_view_drop_target.xml b/quickstep/res/layout/bubble_expanded_view_drop_target.xml
index 15ec49a..3bd5d31 100644
--- a/quickstep/res/layout/bubble_expanded_view_drop_target.xml
+++ b/quickstep/res/layout/bubble_expanded_view_drop_target.xml
@@ -16,8 +16,8 @@
 
 <!-- TODO(b/330585402): replace 600dp height with calculated value -->
 <View xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/bubble_expanded_view_drop_target_width"
-    android:layout_height="600dp"
+    android:layout_width="@dimen/bubble_expanded_view_drop_target_default_width"
+    android:layout_height="@dimen/bubble_expanded_view_drop_target_default_height"
     android:layout_margin="@dimen/bubble_expanded_view_drop_target_margin"
     android:background="@drawable/bg_bubble_expanded_view_drop_target"
     android:elevation="@dimen/bubblebar_elevation" />
\ No newline at end of file
diff --git a/quickstep/res/layout/floating_desktop_app_select.xml b/quickstep/res/layout/floating_desktop_app_select.xml
deleted file mode 100644
index 375fc44..0000000
--- a/quickstep/res/layout/floating_desktop_app_select.xml
+++ /dev/null
@@ -1,56 +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.
--->
-
-<com.android.quickstep.views.DesktopAppSelectView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="@dimen/desktop_mode_floating_app_select_height"
-    android:layout_gravity="top|center_horizontal"
-    android:background="@drawable/bg_floating_desktop_select"
-    android:elevation="@dimen/desktop_mode_floating_app_select_elevation"
-    android:gravity="center_vertical"
-    android:orientation="horizontal">
-
-    <TextView
-        android:id="@+id/desktop_app_select_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginEnd="@dimen/desktop_mode_floating_app_select_text_margin"
-        android:layout_marginStart="@dimen/desktop_mode_floating_app_select_margin"
-        android:drawablePadding="@dimen/desktop_mode_floating_app_select_text_margin"
-        android:drawableStart="@drawable/ic_desktop"
-        android:drawableTint="?androidprv:attr/materialColorOnPrimaryContainer"
-        android:fontFamily="google-sans-medium"
-        android:gravity="center_vertical"
-        android:text="@string/desktop_select_app_toast"
-        android:textColor="?androidprv:attr/materialColorOnPrimaryContainer"
-        android:textSize="@dimen/desktop_mode_floating_app_select_text_size" />
-
-    <Button
-        android:id="@+id/close_button"
-        style="@android:style/Widget.DeviceDefault.Button.Borderless"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginEnd="@dimen/desktop_mode_floating_app_select_margin"
-        android:minWidth="0dp"
-        android:fontFamily="google-sans-medium"
-        android:text="@string/desktop_button_close_app_toast"
-        android:textAllCaps="false"
-        android:textColor="?androidprv:attr/materialColorPrimary"
-        android:textSize="@dimen/desktop_mode_floating_app_select_text_size" />
-
-</com.android.quickstep.views.DesktopAppSelectView>
diff --git a/quickstep/res/layout/overview_actions_container.xml b/quickstep/res/layout/overview_actions_container.xml
index d086da4..7aaf744 100644
--- a/quickstep/res/layout/overview_actions_container.xml
+++ b/quickstep/res/layout/overview_actions_container.xml
@@ -45,14 +45,24 @@
             android:theme="@style/ThemeControlHighlightWorkspaceColor"
             android:visibility="gone" />
 
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/group_action_buttons"
+        android:layout_width="wrap_content"
+        android:layout_height="@dimen/overview_actions_height"
+        android:layout_gravity="bottom|center_horizontal"
+        android:orientation="horizontal"
+        android:visibility="gone">
+
         <Button
             android:id="@+id/action_save_app_pair"
             style="@style/OverviewActionButton"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="@string/action_save_app_pair"
-            android:theme="@style/ThemeControlHighlightWorkspaceColor"
-            android:visibility="gone" />
+            android:theme="@style/ThemeControlHighlightWorkspaceColor" />
+
     </LinearLayout>
 
 </com.android.quickstep.views.OverviewActionsView>
\ No newline at end of file
diff --git a/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml b/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml
index a1bcad0..b004dfd 100644
--- a/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml
@@ -134,11 +134,12 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_alignParentTop="true"
+        android:layout_above="@id/gesture_tutorial_fragment_action_button"
         android:layout_centerHorizontal="true"
         android:background="@android:color/transparent"
-        android:paddingEnd="24dp"
-        android:paddingStart="24dp"
-        android:paddingTop="24dp">
+        android:paddingTop="24dp"
+        android:paddingHorizontal="24dp"
+        android:layout_marginBottom="16dp">
 
         <TextView
             android:id="@+id/gesture_tutorial_fragment_feedback_title"
@@ -169,7 +170,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
 
-            app:layout_constraintBottom_toBottomOf="@id/gesture_tutorial_fragment_action_button"
+            app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent" />
@@ -193,7 +194,7 @@
             android:id="@+id/checkmark_animation"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginBottom="44dp"
+            android:layout_marginBottom="28dp"
             android:gravity="center"
             android:scaleType="centerCrop"
             app:lottie_loop="false"
@@ -204,19 +205,6 @@
             app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_fragment_feedback_subtitle"
             app:layout_constraintBottom_toBottomOf="parent" />
 
-        <Button
-            android:id="@+id/gesture_tutorial_fragment_action_button"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:background="@drawable/gesture_tutorial_action_button_background"
-            android:stateListAnimator="@null"
-            android:text="@string/gesture_tutorial_action_button_label"
-            android:visibility="invisible"
-            android:layout_marginBottom="60dp"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintTop_toBottomOf="@id/checkmark_animation" />
 
     </androidx.constraintlayout.widget.ConstraintLayout>
 
@@ -228,4 +216,18 @@
         android:src="@drawable/gesture_tutorial_finger_dot"
         android:visibility="gone" />
 
+    <Button
+        android:id="@+id/gesture_tutorial_fragment_action_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="@dimen/gesture_tutorial_done_button_end_margin"
+        android:layout_marginBottom="@dimen/gesture_tutorial_done_button_bottom_margin"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentEnd="true"
+        android:background="@drawable/gesture_tutorial_action_button_background"
+        android:stateListAnimator="@null"
+        android:text="@string/gesture_tutorial_action_button_label"
+        android:visibility="invisible"
+         />
+
 </com.android.quickstep.interaction.RootSandboxLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index 9f648a7..cc3b30e 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -28,10 +28,7 @@
     launcher:focusBorderColor="?androidprv:attr/materialColorOutline"
     launcher:hoverBorderColor="?androidprv:attr/materialColorPrimary">
 
-    <com.android.quickstep.views.TaskThumbnailViewDeprecated
-        android:id="@+id/snapshot"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"/>
+    <include layout="@layout/task_thumbnail" />
 
     <!-- Filtering affects only alpha instead of the visibility since visibility can be altered
          separately through RecentsView#resetFromSplitSelectionState() -->
diff --git a/quickstep/res/layout/task_desktop.xml b/quickstep/res/layout/task_desktop.xml
index 36d7f86..89e9b3d 100644
--- a/quickstep/res/layout/task_desktop.xml
+++ b/quickstep/res/layout/task_desktop.xml
@@ -42,10 +42,7 @@
          views that do not inherint from TaskView only or create a generic TaskView that have
          N number of tasks.
      -->
-    <com.android.quickstep.views.TaskThumbnailViewDeprecated
-        android:id="@+id/snapshot"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+    <include layout="@layout/task_thumbnail"
         android:visibility="gone" />
 
     <ViewStub
diff --git a/quickstep/res/layout/task_grouped.xml b/quickstep/res/layout/task_grouped.xml
index ec657bd..87a0f70 100644
--- a/quickstep/res/layout/task_grouped.xml
+++ b/quickstep/res/layout/task_grouped.xml
@@ -33,15 +33,10 @@
     launcher:focusBorderColor="?androidprv:attr/materialColorOutline"
     launcher:hoverBorderColor="?androidprv:attr/materialColorPrimary">
 
-    <com.android.quickstep.views.TaskThumbnailViewDeprecated
-        android:id="@+id/snapshot"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"/>
+    <include layout="@layout/task_thumbnail"/>
 
-    <com.android.quickstep.views.TaskThumbnailViewDeprecated
-        android:id="@+id/bottomright_snapshot"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"/>
+    <include layout="@layout/task_thumbnail"
+        android:id="@+id/bottomright_snapshot" />
 
     <!-- Filtering affects only alpha instead of the visibility since visibility can be altered
          separately through RecentsView#resetFromSplitSelectionState() -->
diff --git a/quickstep/res/layout/task_thumbnail.xml b/quickstep/res/layout/task_thumbnail.xml
new file mode 100644
index 0000000..f1a3d62
--- /dev/null
+++ b/quickstep/res/layout/task_thumbnail.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+     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.
+-->
+<com.android.quickstep.views.TaskThumbnailViewDeprecated
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/snapshot"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" />
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_nav_button.xml b/quickstep/res/layout/taskbar_nav_button.xml
index aea4885..8f1c904 100644
--- a/quickstep/res/layout/taskbar_nav_button.xml
+++ b/quickstep/res/layout/taskbar_nav_button.xml
@@ -19,6 +19,7 @@
     android:layout_width="@dimen/taskbar_nav_buttons_size"
     android:layout_height="@dimen/taskbar_nav_buttons_size"
     android:background="@drawable/taskbar_icon_click_feedback_roundrect"
+    android:focusable="false"
     android:scaleType="center"
     android:tint="@color/taskbar_nav_icon_light_color"
     tools:ignore="UseAppTint" />
\ No newline at end of file
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index 5097ff9..af35fc3 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -95,7 +95,7 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Einstellungen der Systemsteuerung"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Teilen"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
-    <string name="action_split" msgid="2098009717623550676">"Teilen"</string>
+    <string name="action_split" msgid="2098009717623550676">"Splitscreen"</string>
     <string name="action_save_app_pair" msgid="5974823919237645229">"App-Paar speichern"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"Für Splitscreen auf weitere App tippen"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Für Splitscreen andere App auswählen"</string>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index 159226e..0d43f1e 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -118,7 +118,7 @@
     <string name="taskbar_edu_pinning_title" msgid="210102174154211712">"Mostrar siempre la barra de tareas"</string>
     <string name="taskbar_edu_pinning_standalone" msgid="2636919474366410467">"Para mostrar siempre la barra de tareas en la parte inferior, mantén pulsada la línea divisoria"</string>
     <string name="taskbar_search_edu_title" msgid="5569194922234364530">"Mantén pulsada la tecla de acción para buscar lo que ves en pantalla"</string>
-    <string name="taskbar_edu_search_disclosure" msgid="8734536088447779686">"El producto usa la parte seleccionada de tu pantalla para hacer búsquedas. Se aplican la <xliff:g id="BEGIN_PRIVACY_LINK">&lt;a href="%1$s"&gt;</xliff:g>Política de Privacidad<xliff:g id="END_PRIVACY_LINK">&lt;/a&gt;</xliff:g> y los <xliff:g id="BEGIN_TOS_LINK">&lt;a href="%2$s"&gt;</xliff:g>Términos del Servicio<xliff:g id="END_TOS_LINK">&lt;/a&gt;</xliff:g> de Google."</string>
+    <string name="taskbar_edu_search_disclosure" msgid="8734536088447779686">"Este producto usa la parte seleccionada de tu pantalla para hacer búsquedas. Se aplican la <xliff:g id="BEGIN_PRIVACY_LINK">&lt;a href="%1$s"&gt;</xliff:g>Política de Privacidad<xliff:g id="END_PRIVACY_LINK">&lt;/a&gt;</xliff:g> y los <xliff:g id="BEGIN_TOS_LINK">&lt;a href="%2$s"&gt;</xliff:g>Términos del Servicio<xliff:g id="END_TOS_LINK">&lt;/a&gt;</xliff:g> de Google."</string>
     <string name="taskbar_edu_close" msgid="887022990168191073">"Cerrar"</string>
     <string name="taskbar_edu_done" msgid="6880178093977704569">"Hecho"</string>
     <string name="taskbar_button_home" msgid="2151398979630664652">"Inicio"</string>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index 766dabb..c445bc8 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -95,7 +95,7 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Süsteemi navigeerimisseaded"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Jaga"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Ekraanipilt"</string>
-    <string name="action_split" msgid="2098009717623550676">"Eralda"</string>
+    <string name="action_split" msgid="2098009717623550676">"Jaga pooleks"</string>
     <string name="action_save_app_pair" msgid="5974823919237645229">"Salvesta rakendusepaar"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"Jagatud ekraanikuva kasutamiseks puudutage muud rakendust"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Valige jagatud ekraanikuva jaoks muu rakendus."</string>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index b0694c1..040976d 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -95,7 +95,7 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Järjestelmän navigointiasetukset"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Jaa"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Kuvakaappaus"</string>
-    <string name="action_split" msgid="2098009717623550676">"Jaa"</string>
+    <string name="action_split" msgid="2098009717623550676">"Jaettu näyttö"</string>
     <string name="action_save_app_pair" msgid="5974823919237645229">"Tallenna pari"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"Avaa jaettu näyttö napauttamalla toista sovellusta"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Käytä jaettua näyttöä valitsemalla toinen sovellus"</string>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index 6422f8d..5f7bc70 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -95,7 +95,7 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Postavke navigacije sustavom"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Podijeli"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Snimka zaslona"</string>
-    <string name="action_split" msgid="2098009717623550676">"Podijeli"</string>
+    <string name="action_split" msgid="2098009717623550676">"Podjela"</string>
     <string name="action_save_app_pair" msgid="5974823919237645229">"Spremi par apl."</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"Dodirnite drugu aplikaciju za podijeljeni zaslon"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Odaberite drugu aplikaciju za upotrebu podijeljenog zaslona"</string>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index d35aba1..b89cd3c 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -118,7 +118,7 @@
     <string name="taskbar_edu_pinning_title" msgid="210102174154211712">"Ամրացրեք հավելվածների վահանակը"</string>
     <string name="taskbar_edu_pinning_standalone" msgid="2636919474366410467">"Հավելվածների վահանակն էկրանի ներքևում ամրացնելու համար հպեք և պահեք բաժանիչը"</string>
     <string name="taskbar_search_edu_title" msgid="5569194922234364530">"Սեղմած պահեք գործողության ստեղնը՝ էկրանին բովանդակություն որոնելու համար"</string>
-    <string name="taskbar_edu_search_disclosure" msgid="8734536088447779686">"Այս պրոդուկտն օգտագործում է էկրանի ընտրված հատվածը որոնման համար։ Կիրառվում են Google-ի <xliff:g id="BEGIN_PRIVACY_LINK">&lt;a href="%1$s"&gt;</xliff:g>գաղտնիության քաղաքականությունը<xliff:g id="END_PRIVACY_LINK">&lt;/a&gt;</xliff:g> և <xliff:g id="BEGIN_TOS_LINK">&lt;a href="%2$s"&gt;</xliff:g>օգտագործման պայմանները<xliff:g id="END_TOS_LINK">&lt;/a&gt;</xliff:g>։"</string>
+    <string name="taskbar_edu_search_disclosure" msgid="8734536088447779686">"Այս պրոդուկտն օգտագործում է էկրանի ընտրված հատվածը որոնման համար։ Կիրառվում են Google-ի <xliff:g id="BEGIN_PRIVACY_LINK">&lt;a href="%1$s"&gt;</xliff:g>Գաղտնիության քաղաքականությունը<xliff:g id="END_PRIVACY_LINK">&lt;/a&gt;</xliff:g> և <xliff:g id="BEGIN_TOS_LINK">&lt;a href="%2$s"&gt;</xliff:g>Օգտագործման պայմանները<xliff:g id="END_TOS_LINK">&lt;/a&gt;</xliff:g>։"</string>
     <string name="taskbar_edu_close" msgid="887022990168191073">"Փակել"</string>
     <string name="taskbar_edu_done" msgid="6880178093977704569">"Պատրաստ է"</string>
     <string name="taskbar_button_home" msgid="2151398979630664652">"Սկիզբ"</string>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index e696faf..ecde5a3 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -95,7 +95,7 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"စနစ် လမ်းညွှန် ဆက်တင်များ"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"မျှဝေရန်"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ဖန်သားပြင်ဓာတ်ပုံ"</string>
-    <string name="action_split" msgid="2098009717623550676">"ခွဲထုတ်ရန်"</string>
+    <string name="action_split" msgid="2098009717623550676">"ခွဲရန်"</string>
     <string name="action_save_app_pair" msgid="5974823919237645229">"အက်ပ်အတွဲ သိမ်းရန်"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"မျက်နှာပြင် ခွဲ၍ပြသရန် အက်ပ်နောက်တစ်ခုကို တို့ပါ"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းသုံးရန် နောက်အက်ပ်တစ်ခုရွေးပါ"</string>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index 0b40961..1737f07 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -117,7 +117,7 @@
     <string name="taskbar_edu_features" msgid="3320337287472848162">"Panel aplikácií vám ponúka ďalšie možnosti"</string>
     <string name="taskbar_edu_pinning_title" msgid="210102174154211712">"Vždy zobrazovať panel aplikácií"</string>
     <string name="taskbar_edu_pinning_standalone" msgid="2636919474366410467">"Ak chcete, aby sa panel aplikácií vždy zobrazoval v dolnej časti obrazovky, pridržte rozdeľovač"</string>
-    <string name="taskbar_search_edu_title" msgid="5569194922234364530">"Ak chcete vyhľadávať, čo je na obrazovke, pridržte akčný kláves."</string>
+    <string name="taskbar_search_edu_title" msgid="5569194922234364530">"Ak chcete vyhľadávať, čo je na obrazovke, pridržte akčný kláves"</string>
     <string name="taskbar_edu_search_disclosure" msgid="8734536088447779686">"Táto služba používa na účely vyhľadávania vybranú časť obrazovky. Uplatňujú sa <xliff:g id="BEGIN_PRIVACY_LINK">&lt;a href="%1$s"&gt;</xliff:g>pravidlá ochrany súkromia<xliff:g id="END_PRIVACY_LINK">&lt;/a&gt;</xliff:g> a <xliff:g id="BEGIN_TOS_LINK">&lt;a href="%2$s"&gt;</xliff:g>zmluvné podmienky<xliff:g id="END_TOS_LINK">&lt;/a&gt;</xliff:g> spoločnosti Google."</string>
     <string name="taskbar_edu_close" msgid="887022990168191073">"Zavrieť"</string>
     <string name="taskbar_edu_done" msgid="6880178093977704569">"Hotovo"</string>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index 2a1eea6..cec75d9 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -95,7 +95,7 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Systemnavigeringsinställningar"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Dela"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Skärmbild"</string>
-    <string name="action_split" msgid="2098009717623550676">"Delat"</string>
+    <string name="action_split" msgid="2098009717623550676">"Delad skärm"</string>
     <string name="action_save_app_pair" msgid="5974823919237645229">"Spara app-par"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"Tryck på en annan app för att använda delad skärm"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Välj en annan app för att använda delad skärm"</string>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 1086f9f..727f3e7 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -95,7 +95,7 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"系统导航设置"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"分享"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"屏幕截图"</string>
-    <string name="action_split" msgid="2098009717623550676">"拆分"</string>
+    <string name="action_split" msgid="2098009717623550676">"分屏"</string>
     <string name="action_save_app_pair" msgid="5974823919237645229">"保存应用组合"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"点按另一个应用即可使用分屏"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"另外选择一个应用才可使用分屏模式"</string>
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index b3502db..fd12210 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -17,8 +17,7 @@
     <string name="overscroll_plugin_factory_class" translatable="false" />
     <string name="task_overlay_factory_class" translatable="false"/>
 
-    <!-- Activities which block home gesture -->
-    <string-array name="gesture_blocking_activities" translatable="false">
+    <string-array name="back_gesture_blocking_activities" translatable="false">
         <item>com.android.launcher3/com.android.quickstep.interaction.GestureSandboxActivity</item>
     </string-array>
 
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index c5f25ad..2021a0b 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -177,6 +177,8 @@
     <dimen name="gesture_tutorial_menu_done_button_top_spacing">0dp</dimen>
     <dimen name="gesture_tutorial_menu_back_shape_bottom_margin">0dp</dimen>
     <dimen name="gesture_tutorial_menu_done_button_spacing">72dp</dimen>
+    <dimen name="gesture_tutorial_done_button_bottom_margin">40dp</dimen>
+    <dimen name="gesture_tutorial_done_button_end_margin">24dp</dimen>
 
     <!-- Gesture Tutorial mock conversations -->
     <dimen name="gesture_tutorial_message_icon_size">44dp</dimen>
@@ -456,8 +458,10 @@
 
     <!-- Bubble bar drop target -->
     <dimen name="bubblebar_drop_target_corner_radius">36dp</dimen>
-    <dimen name="bubble_expanded_view_drop_target_corner_radius">16dp</dimen>
-    <dimen name="bubble_expanded_view_drop_target_width">412dp</dimen>
+    <dimen name="bubble_expanded_view_drop_target_default_width">412dp</dimen>
+    <dimen name="bubble_expanded_view_drop_target_default_height">600dp</dimen>
+    <dimen name="bubble_expanded_view_drop_target_corner_radius">28dp</dimen>
+    <dimen name="bubble_expanded_view_drop_target_padding">24dp</dimen>
     <dimen name="bubble_expanded_view_drop_target_margin">16dp</dimen>
 
     <!-- Launcher splash screen -->
@@ -480,11 +484,4 @@
     <dimen name="keyboard_quick_switch_no_recent_items_icon_size">24dp</dimen>
     <dimen name="keyboard_quick_switch_no_recent_items_icon_margin">8dp</dimen>
 
-    <!-- Desktop mode -->
-    <dimen name="desktop_mode_floating_app_select_height">56dp</dimen>
-    <dimen name="desktop_mode_floating_app_select_elevation">4dp</dimen>
-    <dimen name="desktop_mode_floating_app_select_margin">16dp</dimen>
-    <dimen name="desktop_mode_floating_app_select_text_size">14sp</dimen>
-    <dimen name="desktop_mode_floating_app_select_text_margin">8dp</dimen>
-
 </resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 278c66a..cf987c3 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -332,10 +332,4 @@
 
     <!-- Accessibility label for quick switch tiles showing split tasks [CHAR LIMIT=NONE] -->
     <string name="quick_switch_split_task"><xliff:g id="app_name_1" example="Chrome">%1$s</xliff:g> and <xliff:g id="app_name_2" example="Gmail">%2$s</xliff:g></string>
-
-    <!-- ******* Desktop ******* -->
-    <!-- Text shown in popup to choose a desktop app. [CHAR LIMIT=60] -->
-    <string name="desktop_select_app_toast">Adding app to Desktop</string>
-    <!-- Text shown on a button that closes the popup for choosing a desktop app. [CHAR_LIMIT=40] -->
-    <string name="desktop_button_close_app_toast">Cancel</string>
 </resources>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 0697f47..0c499b8 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -58,6 +58,7 @@
 import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
 import static com.android.launcher3.testing.shared.TestProtocol.WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE;
 import static com.android.launcher3.util.DisplayController.isTransientTaskbar;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.ORDERED_BG_EXECUTOR;
 import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
 import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
@@ -374,7 +375,7 @@
      */
     protected boolean isLaunchingFromRecents(@NonNull View v,
             @Nullable RemoteAnimationTarget[] targets) {
-        return mLauncher.getStateManager().getState().overviewUi
+        return mLauncher.getStateManager().getState().isRecentsViewVisible
                 && findTaskViewToLaunch(mLauncher.getOverviewPanel(), v, targets) != null;
     }
 
@@ -1881,7 +1882,7 @@
 
             return new ContainerAnimationRunner(
                     new ActivityTransitionAnimator.AnimationDelegate(
-                            controller, callback, listener));
+                            MAIN_EXECUTOR, controller, callback, listener));
         }
 
         /**
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index 70e01f5..a16031d 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -286,7 +286,7 @@
     }
 
     public void dump(String prefix, PrintWriter writer) {
-        writer.println(prefix + this.getClass().getSimpleName());
+        writer.println(prefix + "PredictionRowView");
         writer.println(prefix + "\tmPredictionsEnabled: " + mPredictionsEnabled);
         writer.println(prefix + "\tmPredictionUiUpdatePaused: " + mPredictionUiUpdatePaused);
         writer.println(prefix + "\tmNumPredictedAppsPerRow: " + mNumPredictedAppsPerRow);
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 1c5a75d..de974ec 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -29,6 +29,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.content.ComponentName;
+import android.util.Log;
 import android.view.HapticFeedbackConstants;
 import android.view.View;
 import android.view.ViewGroup;
@@ -80,6 +81,7 @@
         SystemShortcut.Factory<QuickstepLauncher>, DeviceProfile.OnDeviceProfileChangeListener,
         DragSource, ViewGroup.OnHierarchyChangeListener {
 
+    private static final String TAG = "HotseatPredictionController";
     private static final int FLAG_UPDATE_PAUSED = 1 << 0;
     private static final int FLAG_DRAG_IN_PROGRESS = 1 << 1;
     private static final int FLAG_FILL_IN_PROGRESS = 1 << 2;
@@ -292,6 +294,16 @@
     }
 
     /**
+     * Ensures that if the flag FLAG_UPDATE_PAUSED is active we set it to false.
+     */
+    public void verifyUIUpdateNotPaused() {
+        if ((mPauseFlags & FLAG_UPDATE_PAUSED) != 0) {
+            setPauseUIUpdate(false);
+            Log.e(TAG, "FLAG_UPDATE_PAUSED should not be set to true (see b/339700174)");
+        }
+    }
+
+    /**
      * Sets or updates the predicted items
      */
     public void setPredictedItems(FixedContainerItems items) {
@@ -521,7 +533,7 @@
     }
 
     public void dump(String prefix, PrintWriter writer) {
-        writer.println(prefix + this.getClass().getSimpleName());
+        writer.println(prefix + "HotseatPredictionController");
         writer.println(prefix + "\tFlags: " + getStateString(mPauseFlags));
         writer.println(prefix + "\tmHotSeatItemsCount: " + mHotSeatItemsCount);
         writer.println(prefix + "\tmPredictedItems: " + mPredictedItems);
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index 65a49bd..8b5ed7c 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -73,6 +73,7 @@
 import com.android.launcher3.util.ApiWrapper;
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PersistedItemArray;
 import com.android.quickstep.logging.SettingsChangeLogger;
 import com.android.quickstep.logging.StatsLogCompatManager;
@@ -150,7 +151,8 @@
         // TODO: Implement caching and preloading
 
         WorkspaceItemFactory factory =
-                new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, numColumns, state.containerId);
+                new WorkspaceItemFactory(mApp, ums, mPmHelper, pinnedShortcuts, numColumns,
+                        state.containerId);
         FixedContainerItems fci = new FixedContainerItems(state.containerId,
                 state.storage.read(mApp.getContext(), factory, ums.allUsers::get));
         if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
@@ -530,6 +532,7 @@
 
         private final LauncherAppState mAppState;
         private final UserManagerState mUMS;
+        private final PackageManagerHelper mPmHelper;
         private final Map<ShortcutKey, ShortcutInfo> mPinnedShortcuts;
         private final int mMaxCount;
         private final int mContainer;
@@ -537,9 +540,11 @@
         private int mReadCount = 0;
 
         protected WorkspaceItemFactory(LauncherAppState appState, UserManagerState ums,
-                Map<ShortcutKey, ShortcutInfo> pinnedShortcuts, int maxCount, int container) {
+                PackageManagerHelper pmHelper, Map<ShortcutKey, ShortcutInfo> pinnedShortcuts,
+                int maxCount, int container) {
             mAppState = appState;
             mUMS = ums;
+            mPmHelper = pmHelper;
             mPinnedShortcuts = pinnedShortcuts;
             mMaxCount = maxCount;
             mContainer = container;
@@ -563,6 +568,7 @@
                             lai,
                             UserCache.INSTANCE.get(mAppState.getContext()).getUserInfo(user),
                             ApiWrapper.INSTANCE.get(mAppState.getContext()),
+                            mPmHelper,
                             mUMS.isUserQuiet(user));
                     info.container = mContainer;
                     mAppState.getIconCache().getTitleAndIcon(info, lai, false);
diff --git a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
index 184ea71..fe9ade9 100644
--- a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
+++ b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
@@ -25,7 +25,7 @@
 /** {@link SystemShortcut.Factory} implementation to create workspace split shortcuts */
 public interface QuickstepSystemShortcut {
 
-    String TAG = QuickstepSystemShortcut.class.getSimpleName();
+    String TAG = "QuickstepSystemShortcut";
 
     static SystemShortcut.Factory<QuickstepLauncher> getSplitSelectShortcutByPosition(
             SplitPositionOption position) {
diff --git a/quickstep/src/com/android/launcher3/splitscreen/SplitShortcut.kt b/quickstep/src/com/android/launcher3/splitscreen/SplitShortcut.kt
index 2b6f77f..c94edce 100644
--- a/quickstep/src/com/android/launcher3/splitscreen/SplitShortcut.kt
+++ b/quickstep/src/com/android/launcher3/splitscreen/SplitShortcut.kt
@@ -45,7 +45,7 @@
 ) : SystemShortcut<T>(iconResId, labelResId, target, itemInfo, originalView) where
 T : Context?,
 T : ActivityContext? {
-    private val TAG = SystemShortcut::class.java.simpleName
+    private val TAG = "SplitShortcut"
 
     // Initiate splitscreen from the Home screen or Home All Apps
     protected val splitSelectSource: SplitSelectSource?
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index 882682d..747612d 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -182,7 +182,7 @@
     }
 
     public void dump(String prefix, PrintWriter writer) {
-        writer.println(prefix + this.getClass().getSimpleName());
+        writer.println(prefix + "DepthController");
         writer.println(prefix + "\tmMaxBlurRadius=" + mMaxBlurRadius);
         writer.println(prefix + "\tmCrossWindowBlursEnabled=" + mCrossWindowBlursEnabled);
         writer.println(prefix + "\tmSurface=" + mSurface);
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index 9eabb55..62cc0bb 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -35,7 +35,6 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.views.DesktopAppSelectView;
 import com.android.wm.shell.desktopmode.IDesktopTaskListener;
 
 import java.util.HashSet;
@@ -49,8 +48,6 @@
 
     private static final String TAG = "DesktopVisController";
     private static final boolean DEBUG = false;
-    private static final boolean IS_STASHING_ENABLED = SystemProperties.getBoolean(
-            "persist.wm.debug.desktop_stashing", false);
     private final Launcher mLauncher;
     private final Set<DesktopVisibilityListener> mDesktopVisibilityListeners = new HashSet<>();
 
@@ -61,7 +58,6 @@
 
     @Nullable
     private IDesktopTaskListener mDesktopTaskListener;
-    private DesktopAppSelectView mSelectAppToast;
 
     public DesktopVisibilityController(Launcher launcher) {
         mLauncher = launcher;
@@ -86,21 +82,7 @@
 
             @Override
             public void onStashedChanged(int displayId, boolean stashed) {
-                if (!IS_STASHING_ENABLED) {
-                    return;
-                }
-                MAIN_EXECUTOR.execute(() -> {
-                    if (displayId == mLauncher.getDisplayId()) {
-                        if (DEBUG) {
-                            Log.d(TAG, "desktop stashed changed value=" + stashed);
-                        }
-                        if (stashed) {
-                            showSelectAppToast();
-                        } else {
-                            hideSelectAppToast();
-                        }
-                    }
-                });
+              Log.w(TAG, "IDesktopTaskListener: onStashedChanged is deprecated");
             }
         };
         SystemUiProxy.INSTANCE.get(mLauncher).setDesktopTaskListener(mDesktopTaskListener);
@@ -111,6 +93,7 @@
      */
     public void unregisterSystemUiListener() {
         SystemUiProxy.INSTANCE.get(mLauncher).setDesktopTaskListener(null);
+        mDesktopTaskListener = null;
     }
 
     /**
@@ -190,7 +173,7 @@
         }
         setBackgroundStateEnabled(state == BACKGROUND_APP);
         // Desktop visibility tracks overview and background state separately
-        setOverviewStateEnabled(state != BACKGROUND_APP && state.overviewUi);
+        setOverviewStateEnabled(state != BACKGROUND_APP && state.isRecentsViewVisible);
     }
 
     private void setOverviewStateEnabled(boolean overviewStateEnabled) {
@@ -303,15 +286,6 @@
     }
 
     /**
-     * Handle launcher moving to home due to home gesture or home button press.
-     */
-    public void onHomeActionTriggered() {
-        if (IS_STASHING_ENABLED && areDesktopTasksVisible()) {
-            SystemUiProxy.INSTANCE.get(mLauncher).stashDesktopApps(mLauncher.getDisplayId());
-        }
-    }
-
-    /**
      * TODO: b/333533253 - Remove after flag rollout
      */
     private void setLauncherViewsVisibility(int visibility) {
@@ -373,30 +347,6 @@
         }
     }
 
-    private void showSelectAppToast() {
-        if (mSelectAppToast != null) {
-            return;
-        }
-        if (DEBUG) {
-            Log.d(TAG, "show toast to select desktop apps");
-        }
-        Runnable onCloseCallback = () -> {
-            SystemUiProxy.INSTANCE.get(mLauncher).hideStashedDesktopApps(mLauncher.getDisplayId());
-        };
-        mSelectAppToast = DesktopAppSelectView.show(mLauncher, onCloseCallback);
-    }
-
-    private void hideSelectAppToast() {
-        if (mSelectAppToast == null) {
-            return;
-        }
-        if (DEBUG) {
-            Log.d(TAG, "hide toast to select desktop apps");
-        }
-        mSelectAppToast.hide();
-        mSelectAppToast = null;
-    }
-
     /** A listener for when the user enters/exits Desktop Mode. */
     public interface DesktopVisibilityListener {
         /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
index c0ecc61..06d9ee6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
@@ -133,4 +133,9 @@
     protected TISBindHelper getTISBindHelper() {
         return mRecentsActivity.getTISBindHelper();
     }
+
+    @Override
+    protected String getTaskbarUIControllerName() {
+        return "FallbackTaskbarUIController";
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
index 48fc7d1..39b4f77 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
@@ -186,7 +186,7 @@
             @NonNull ImageView thumbnailView,
             @ColorInt int backgroundColor,
             @Nullable ThumbnailData thumbnailData) {
-        Bitmap bm = thumbnailData == null ? null : thumbnailData.thumbnail;
+        Bitmap bm = thumbnailData == null ? null : thumbnailData.getThumbnail();
 
         if (thumbnailView.getVisibility() != VISIBLE) {
             thumbnailView.setVisibility(VISIBLE);
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 2ce6a41..779009a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -51,6 +51,7 @@
 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 java.io.PrintWriter;
 import java.util.Arrays;
@@ -379,7 +380,7 @@
     }
 
     @Override
-    public void updateStateForSysuiFlags(int sysuiFlags) {
+    public void updateStateForSysuiFlags(@SystemUiStateFlags long sysuiFlags) {
         mTaskbarLauncherStateController.updateStateForSysuiFlags(sysuiFlags);
     }
 
@@ -446,4 +447,9 @@
 
         mTaskbarLauncherStateController.dumpLogs(prefix + "\t", pw);
     }
+
+    @Override
+    protected String getTaskbarUIControllerName() {
+        return "LauncherTaskbarUIController";
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index abb763a..81581b8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -45,6 +45,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
 
 import android.animation.ArgbEvaluator;
@@ -105,6 +106,7 @@
 import com.android.systemui.shared.rotation.RotationButton;
 import com.android.systemui.shared.rotation.RotationButtonController;
 import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -133,6 +135,7 @@
     private static final int FLAG_VOICE_INTERACTION_WINDOW_SHOWING = 1 << 12;
     private static final int FLAG_SMALL_SCREEN = 1 << 13;
     private static final int FLAG_SLIDE_IN_VIEW_VISIBLE = 1 << 14;
+    private static final int FLAG_KEYBOARD_SHORTCUT_HELPER_SHOWING = 1 << 15;
 
     /**
      * Flags where a UI could be over Taskbar surfaces, so the color override should be disabled.
@@ -197,7 +200,8 @@
     private TaskbarControllers mControllers;
     private boolean mIsImeRenderingNavButtons;
     private ImageView mA11yButton;
-    private int mSysuiStateFlags;
+    @SystemUiStateFlags
+    private long mSysuiStateFlags;
     private ImageView mBackButton;
     private ImageView mHomeButton;
     private MultiValueAlpha mBackButtonAlpha;
@@ -284,8 +288,9 @@
         // - Notification shade is expanded
         // - IME is showing (add separate translation for IME)
         // - VoiceInteractionWindow (assistant) is showing
+        // - Keyboard shortcuts helper is showing
         int flagsToRemoveTranslation = FLAG_NOTIFICATION_SHADE_EXPANDED | FLAG_IME_VISIBLE
-                | FLAG_VOICE_INTERACTION_WINDOW_SHOWING;
+                | FLAG_VOICE_INTERACTION_WINDOW_SHOWING | FLAG_KEYBOARD_SHORTCUT_HELPER_SHOWING;
         mPropertyHolders.add(new StatePropertyHolder(mNavButtonInAppDisplayProgressForSysui,
                 flags -> (flags & flagsToRemoveTranslation) != 0, AnimatedFloat.VALUE,
                 1, 0));
@@ -442,7 +447,7 @@
                 navButtonController.onButtonLongClick(BUTTON_SPACE, view));
     }
 
-    private void parseSystemUiFlags(int sysUiStateFlags) {
+    private void parseSystemUiFlags(@SystemUiStateFlags long sysUiStateFlags) {
         mSysuiStateFlags = sysUiStateFlags;
         boolean isImeVisible = (sysUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0;
         boolean isImeSwitcherShowing = (sysUiStateFlags & SYSUI_STATE_IME_SWITCHER_SHOWING) != 0;
@@ -450,12 +455,14 @@
         boolean isHomeDisabled = (sysUiStateFlags & SYSUI_STATE_HOME_DISABLED) != 0;
         boolean isRecentsDisabled = (sysUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0;
         boolean isBackDisabled = (sysUiStateFlags & SYSUI_STATE_BACK_DISABLED) != 0;
-        int shadeExpandedFlags = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
+        long shadeExpandedFlags = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
                 | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
         boolean isNotificationShadeExpanded = (sysUiStateFlags & shadeExpandedFlags) != 0;
         boolean isScreenPinningActive = (sysUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0;
         boolean isVoiceInteractionWindowShowing =
                 (sysUiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0;
+        boolean isKeyboardShortcutHelperShowing =
+                (sysUiStateFlags & SYSUI_STATE_SHORTCUT_HELPER_SHOWING) != 0;
 
         // TODO(b/202218289) we're getting IME as not visible on lockscreen from system
         updateStateForFlag(FLAG_IME_VISIBLE, isImeVisible);
@@ -467,6 +474,7 @@
         updateStateForFlag(FLAG_NOTIFICATION_SHADE_EXPANDED, isNotificationShadeExpanded);
         updateStateForFlag(FLAG_SCREEN_PINNING_ACTIVE, isScreenPinningActive);
         updateStateForFlag(FLAG_VOICE_INTERACTION_WINDOW_SHOWING, isVoiceInteractionWindowShowing);
+        updateStateForFlag(FLAG_KEYBOARD_SHORTCUT_HELPER_SHOWING, isKeyboardShortcutHelperShowing);
 
         if (mA11yButton != null) {
             // Only used in 3 button
@@ -477,7 +485,8 @@
         }
     }
 
-    public void updateStateForSysuiFlags(int systemUiStateFlags, boolean skipAnim) {
+    public void updateStateForSysuiFlags(@SystemUiStateFlags long systemUiStateFlags,
+            boolean skipAnim) {
         if (systemUiStateFlags == mSysuiStateFlags) {
             return;
         }
@@ -1053,6 +1062,8 @@
         appendFlag(str, flags, FLAG_SCREEN_PINNING_ACTIVE, "FLAG_SCREEN_PINNING_ACTIVE");
         appendFlag(str, flags, FLAG_VOICE_INTERACTION_WINDOW_SHOWING,
                 "FLAG_VOICE_INTERACTION_WINDOW_SHOWING");
+        appendFlag(str, flags, FLAG_KEYBOARD_SHORTCUT_HELPER_SHOWING,
+                "FLAG_KEYBOARD_SHORTCUT_HELPER_SHOWING");
         return str.toString();
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index 36e054a..8d4c34d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -42,6 +42,7 @@
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.quickstep.NavHandle;
 import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
 
 import java.io.PrintWriter;
 
@@ -303,7 +304,7 @@
                 homeDisabled ? 0 : 1);
     }
 
-    public void updateStateForSysuiFlags(int systemUiStateFlags) {
+    public void updateStateForSysuiFlags(@SystemUiStateFlags long systemUiStateFlags) {
         mTaskbarHidden = (systemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) != 0;
         updateRegionSamplingWindowVisibility();
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index b24be54..1571ac0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -37,6 +37,7 @@
 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING;
 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN;
 import static com.android.launcher3.testing.shared.ResourceUtils.getBoolByName;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
@@ -137,6 +138,7 @@
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.rotation.RotationButtonController;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
 import com.android.systemui.unfold.updates.RotationChangeProvider;
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
 
@@ -260,9 +262,9 @@
                     new BubbleDragController(this),
                     new BubbleDismissController(this, mDragLayer),
                     new BubbleBarPinController(this, mDragLayer,
-                            () -> getDeviceProfile().getDisplayInfo().currentSize),
+                            () -> DisplayController.INSTANCE.get(this).getInfo().currentSize),
                     new BubblePinController(this, mDragLayer,
-                            () -> getDeviceProfile().getDisplayInfo().currentSize)
+                            () -> DisplayController.INSTANCE.get(this).getInfo().currentSize)
             ));
         }
 
@@ -288,7 +290,7 @@
                 new TaskbarUnfoldAnimationController(this, unfoldTransitionProgressProvider,
                         mWindowManager,
                         new RotationChangeProvider(c.getSystemService(DisplayManager.class), this,
-                                getMainThreadHandler())),
+                                UI_HELPER_EXECUTOR.getHandler(), getMainThreadHandler())),
                 new TaskbarKeyguardController(this),
                 new StashedHandleViewController(this, stashedHandleView),
                 new TaskbarStashController(this),
@@ -825,7 +827,8 @@
         return mIsDestroyed;
     }
 
-    public void updateSysuiStateFlags(int systemUiStateFlags, boolean fromInit) {
+    public void updateSysuiStateFlags(@SystemUiStateFlags long systemUiStateFlags,
+            boolean fromInit) {
         mControllers.navbarButtonsViewController.updateStateForSysuiFlags(systemUiStateFlags,
                 fromInit);
         boolean isShadeVisible = (systemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE) != 0;
@@ -965,7 +968,9 @@
                 mWindowLayoutParams.paramsForRotation[rot].height = size;
             }
         }
-        mControllers.taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged();
+        mControllers.runAfterInit(
+                mControllers.taskbarInsetsController
+                        ::onTaskbarOrBubblebarWindowHeightOrInsetsChanged);
         notifyUpdateLayoutParams();
     }
 
@@ -1451,7 +1456,7 @@
         });
     }
 
-    protected boolean isUserSetupComplete() {
+    public boolean isUserSetupComplete() {
         return mIsUserSetupComplete;
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 189b687..4f5922c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -74,12 +74,14 @@
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.views.BubbleTextHolder;
+import com.android.quickstep.LauncherActivityInterface;
 import com.android.quickstep.util.LogUtils;
 import com.android.quickstep.util.MultiValueUpdateListener;
 import com.android.systemui.shared.recents.model.Task;
@@ -163,11 +165,6 @@
             return false;
         }
         TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onTaskbarItemLongClick");
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.TWO_TASKBAR_LONG_CLICKS,
-                    "TaskbarDragController.startDragOnLongClick",
-                    new Throwable());
-        }
         BubbleTextView btv = (BubbleTextView) view;
         mActivity.onDragStart();
         btv.post(() -> {
@@ -340,8 +337,13 @@
     @Override
     protected void callOnDragStart() {
         super.callOnDragStart();
+        // TODO(297921594) clean it up when taskbar to desktop drag is implemented.
+        DesktopVisibilityController desktopController =
+                LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
+
         // Pre-drag has ended, start the global system drag.
-        if (mDisallowGlobalDrag) {
+        if (mDisallowGlobalDrag || (desktopController != null
+                && desktopController.areDesktopTasksVisible())) {
             AbstractFloatingView.closeAllOpenViewsExcept(mActivity, TYPE_TASKBAR_ALL_APPS);
             return;
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index 7f201b4..84874a9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -37,7 +37,7 @@
 import androidx.core.graphics.Insets;
 import androidx.core.view.WindowInsetsCompat;
 
-import com.android.app.viewcapture.SettingsAwareViewCapture;
+import com.android.app.viewcapture.ViewCaptureFactory;
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.TestProtocol;
@@ -150,7 +150,7 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         getViewTreeObserver().addOnComputeInternalInsetsListener(mTaskbarInsetsComputer);
-        mViewCaptureCloseable = SettingsAwareViewCapture.getInstance(getContext())
+        mViewCaptureCloseable = ViewCaptureFactory.getInstance(getContext())
                 .startCapture(getRootView(), ".Taskbar");
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
index 333c07b..6ac862e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
@@ -34,6 +34,7 @@
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.util.MultiPropertyFactory;
 import com.android.launcher3.util.TouchController;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
 
 /**
  * Controller for taskbar when force visible in immersive mode is set.
@@ -83,7 +84,7 @@
     }
 
     /** Update values tracked via sysui flags. */
-    public void updateSysuiFlags(int sysuiFlags) {
+    public void updateSysuiFlags(@SystemUiStateFlags long sysuiFlags) {
         mIsImmersiveMode = (sysuiFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) == 0;
         if (mContext.isNavBarForceVisible()) {
             if (mIsImmersiveMode) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
index 03d08eb..eac4eaa 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
@@ -17,6 +17,7 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
 
 import java.io.PrintWriter;
 
@@ -25,7 +26,7 @@
  */
 public class TaskbarKeyguardController implements TaskbarControllers.LoggableTaskbarController {
 
-    private static final int KEYGUARD_SYSUI_FLAGS = SYSUI_STATE_BOUNCER_SHOWING
+    private static final long KEYGUARD_SYSUI_FLAGS = SYSUI_STATE_BOUNCER_SHOWING
             | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING | SYSUI_STATE_DEVICE_DOZING
             | SYSUI_STATE_OVERVIEW_DISABLED | SYSUI_STATE_HOME_DISABLED
             | SYSUI_STATE_BACK_DISABLED | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
@@ -33,13 +34,13 @@
 
     // If any of these SysUi flags (via QuickstepContract) is set, the device to be considered
     // locked.
-    public static final int MASK_ANY_SYSUI_LOCKED = SYSUI_STATE_BOUNCER_SHOWING
+    public static final long MASK_ANY_SYSUI_LOCKED = SYSUI_STATE_BOUNCER_SHOWING
             | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
             | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
             | SYSUI_STATE_DEVICE_DREAMING;
 
     private final TaskbarActivityContext mContext;
-    private int mKeyguardSysuiFlags;
+    private long mKeyguardSysuiFlags;
     private boolean mBouncerShowing;
     private NavbarButtonsViewController mNavbarButtonsViewController;
     private final KeyguardManager mKeyguardManager;
@@ -53,8 +54,8 @@
         mNavbarButtonsViewController = navbarButtonUIController;
     }
 
-    public void updateStateForSysuiFlags(int systemUiStateFlags) {
-        int interestingKeyguardFlags = systemUiStateFlags & KEYGUARD_SYSUI_FLAGS;
+    public void updateStateForSysuiFlags(@SystemUiStateFlags long systemUiStateFlags) {
+        long interestingKeyguardFlags = systemUiStateFlags & KEYGUARD_SYSUI_FLAGS;
         if (interestingKeyguardFlags == mKeyguardSysuiFlags) {
             // No change in keyguard relevant flags
             return;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index ee21eac..ead1a8a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -54,6 +54,7 @@
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.animation.ViewRootSync;
 import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
 
 import java.io.PrintWriter;
 import java.util.HashMap;
@@ -65,7 +66,7 @@
  */
 public class TaskbarLauncherStateController {
 
-    private static final String TAG = TaskbarLauncherStateController.class.getSimpleName();
+    private static final String TAG = "TaskbarLauncherStateController";
     private static final boolean DEBUG = false;
 
     /** Launcher activity is visible and focused. */
@@ -226,7 +227,7 @@
 
     /** Initializes the controller instance, and applies the initial state immediately. */
     public void init(TaskbarControllers controllers, QuickstepLauncher launcher,
-            int sysuiStateFlags) {
+            @SystemUiStateFlags long sysuiStateFlags) {
         mCanSyncViews = false;
 
         mControllers = controllers;
@@ -325,11 +326,12 @@
     }
 
     /** SysUI flags updated, see QuickStepContract.SYSUI_STATE_* values. */
-    public void updateStateForSysuiFlags(int systemUiStateFlags) {
+    public void updateStateForSysuiFlags(@SystemUiStateFlags long systemUiStateFlags) {
         updateStateForSysuiFlags(systemUiStateFlags, /* applyState */ true);
     }
 
-    private void updateStateForSysuiFlags(int systemUiStateFlags, boolean applyState) {
+    private void updateStateForSysuiFlags(@SystemUiStateFlags long systemUiStateFlags,
+            boolean applyState) {
         final boolean prevIsAwake = hasAnyFlag(FLAG_AWAKE);
         final boolean currIsAwake = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_AWAKE);
 
@@ -389,11 +391,11 @@
         }
     }
 
-    private boolean hasAnyFlag(int flagMask) {
+    private boolean hasAnyFlag(long flagMask) {
         return hasAnyFlag(mState, flagMask);
     }
 
-    private boolean hasAnyFlag(int flags, int flagMask) {
+    private boolean hasAnyFlag(long flags, long flagMask) {
         return (flags & flagMask) != 0;
     }
 
@@ -667,7 +669,7 @@
     }
 
     boolean isInOverviewUi() {
-        return mLauncherState.overviewUi;
+        return mLauncherState.isRecentsViewVisible;
     }
 
     private void playStateTransitionAnim(AnimatorSet animatorSet, long duration,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 42e6edb..e8dc177 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -73,6 +73,7 @@
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.util.AssistUtils;
 import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
 
@@ -491,7 +492,7 @@
         }
     }
 
-    public void onSystemUiFlagsChanged(int systemUiStateFlags) {
+    public void onSystemUiFlagsChanged(@SystemUiStateFlags long systemUiStateFlags) {
         if (DEBUG) {
             Log.d(TAG, "SysUI flags changed: " + formatFlagChange(systemUiStateFlags,
                     mSharedState.sysuiStateFlags, QuickStepContract::getSystemUiStateString));
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index ade4649..d26a36d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -44,13 +44,12 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.statehandlers.DesktopVisibilityController;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.quickstep.LauncherActivityInterface;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskUtils;
 import com.android.quickstep.util.AssistUtils;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -66,7 +65,7 @@
     /** Allow some time in between the long press for back and recents. */
     static final int SCREEN_PIN_LONG_PRESS_THRESHOLD = 200;
     static final int SCREEN_PIN_LONG_PRESS_RESET = SCREEN_PIN_LONG_PRESS_THRESHOLD + 100;
-    private static final String TAG = TaskbarNavButtonController.class.getSimpleName();
+    private static final String TAG = "TaskbarNavButtonController";
 
     private long mLastScreenPinLongPress;
     private boolean mScreenPinned;
@@ -258,7 +257,7 @@
         mLastScreenPinLongPress = 0;
     }
 
-    public void updateSysuiFlags(int sysuiFlags) {
+    public void updateSysuiFlags(@SystemUiStateFlags long sysuiFlags) {
         mScreenPinned = (sysuiFlags & SYSUI_STATE_SCREEN_PINNING) != 0;
     }
 
@@ -284,13 +283,6 @@
 
     private void navigateHome() {
         TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
-
-        DesktopVisibilityController desktopVisibilityController =
-                LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
-        if (desktopVisibilityController != null) {
-            desktopVisibilityController.onHomeActionTriggered();
-        }
-
         mCallbacks.onNavigateHome();
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
index 712374d..92d9b23 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -30,6 +30,7 @@
 import com.android.launcher3.anim.AnimatedFloat;
 import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.SystemUiProxy;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
 
 import java.io.PrintWriter;
 
@@ -45,7 +46,8 @@
     private final TaskbarActivityContext mActivity;
     private final TaskbarScrimView mScrimView;
     private boolean mTaskbarVisible;
-    private int mSysUiStateFlags;
+    @SystemUiStateFlags
+    private long mSysUiStateFlags;
 
     // Alpha property for the scrim.
     private final AnimatedFloat mScrimAlpha = new AnimatedFloat(this::updateScrimAlpha);
@@ -82,7 +84,7 @@
     /**
      * Updates the scrim state based on the flags.
      */
-    public void updateStateForSysuiFlags(int stateFlags, boolean skipAnim) {
+    public void updateStateForSysuiFlags(@SystemUiStateFlags long stateFlags, boolean skipAnim) {
         if (isBubbleBarEnabled() && DisplayController.isTransientTaskbar(mActivity)) {
             // These scrims aren't used if bubble bar & transient taskbar are active.
             return;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
index e2c71bf..edaeb63 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
@@ -29,6 +29,8 @@
 import android.os.IBinder;
 import android.view.InsetsFrameProvider;
 
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
+
 /**
  * State shared across different taskbar instance
  */
@@ -39,7 +41,8 @@
     private static int INDEX_RIGHT = 1;
 
     // TaskbarManager#onSystemUiFlagsChanged
-    public int sysuiStateFlags;
+    @SystemUiStateFlags
+    public long sysuiStateFlags;
 
     // TaskbarManager#disableNavBarElements()
     public int disableNavBarDisplayId;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 4da7762..74d2d60 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -71,14 +71,14 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.StringJoiner;
-import java.util.function.IntPredicate;
+import java.util.function.LongPredicate;
 
 /**
  * Coordinates between controllers such as TaskbarViewController and StashedHandleViewController to
  * create a cohesive animation between stashed/unstashed states.
  */
 public class TaskbarStashController implements TaskbarControllers.LoggableTaskbarController {
-    private static final String TAG = TaskbarStashController.class.getSimpleName();
+    private static final String TAG = "TaskbarStashController";
     private static final boolean DEBUG = false;
 
     public static final int FLAG_IN_APP = 1 << 0;
@@ -229,7 +229,7 @@
 
     /** Whether we are currently visually stashed (might change based on launcher state). */
     private boolean mIsStashed = false;
-    private int mState;
+    private long mState;
 
     private @Nullable AnimatorSet mAnimator;
     private boolean mIsSystemGestureInProgress;
@@ -240,7 +240,7 @@
     private boolean mEnableBlockingTimeoutDuringTests = false;
 
     // Evaluate whether the handle should be stashed
-    private final IntPredicate mIsStashedPredicate = flags -> {
+    private final LongPredicate mIsStashedPredicate = flags -> {
         boolean inApp = hasAnyFlag(flags, FLAGS_IN_APP);
         boolean stashedInApp = hasAnyFlag(flags, FLAGS_STASHED_IN_APP);
         boolean stashedLauncherState = hasAnyFlag(flags, FLAG_IN_STASHED_LAUNCHER_STATE);
@@ -388,11 +388,11 @@
         return (hasAnyFlag(FLAG_IN_STASHED_LAUNCHER_STATE) && supportsVisualStashing());
     }
 
-    private boolean hasAnyFlag(int flagMask) {
+    private boolean hasAnyFlag(long flagMask) {
         return hasAnyFlag(mState, flagMask);
     }
 
-    private boolean hasAnyFlag(int flags, int flagMask) {
+    private boolean hasAnyFlag(long flags, long flagMask) {
         return (flags & flagMask) != 0;
     }
 
@@ -424,14 +424,15 @@
      * @see android.view.WindowInsets.Type#systemBars()
      */
     public int getContentHeightToReportToApps() {
-        if ((mActivity.isPhoneMode() && !mActivity.isThreeButtonNav())
-                || DisplayController.isTransientTaskbar(mActivity)) {
+        if (mActivity.isUserSetupComplete() && (mActivity.isPhoneGestureNavMode()
+                || DisplayController.isTransientTaskbar(mActivity))) {
             return getStashedHeight();
         }
 
         if (supportsVisualStashing() && hasAnyFlag(FLAGS_REPORT_STASHED_INSETS_TO_APP)) {
             DeviceProfile dp = mActivity.getDeviceProfile();
-            if (hasAnyFlag(FLAG_STASHED_IN_APP_SETUP) && dp.isTaskbarPresent) {
+            if (hasAnyFlag(FLAG_STASHED_IN_APP_SETUP) && (dp.isTaskbarPresent
+                    || mActivity.isPhoneGestureNavMode())) {
                 // We always show the back button in SUW but in portrait the SUW layout may not
                 // be wide enough to support overlapping the nav bar with its content.
                 // We're sending different res values in portrait vs landscape
@@ -929,7 +930,7 @@
     }
 
     /** Called when some system ui state has changed. (See SYSUI_STATE_... in QuickstepContract) */
-    public void updateStateForSysuiFlags(int systemUiStateFlags, boolean skipAnim) {
+    public void updateStateForSysuiFlags(long systemUiStateFlags, boolean skipAnim) {
         long animDuration = TASKBAR_STASH_DURATION;
         long startDelay = 0;
 
@@ -1005,8 +1006,8 @@
      *                unstashed.
      * @return Whether the flag state changed.
      */
-    public boolean updateStateForFlag(int flag, boolean enabled) {
-        int oldState = mState;
+    public boolean updateStateForFlag(long flag, boolean enabled) {
+        long oldState = mState;
         if (enabled) {
             mState |= flag;
         } else {
@@ -1020,7 +1021,7 @@
      *
      * @param changedFlags The flags that have changed.
      */
-    private void onStateChangeApplied(int changedFlags) {
+    private void onStateChangeApplied(long changedFlags) {
         if (hasAnyFlag(changedFlags, FLAGS_STASHED_IN_APP)) {
             mControllers.uiController.onStashedInAppChanged();
         }
@@ -1151,7 +1152,7 @@
         pw.println(prefix + "\tmIsImeSwitcherShowing=" + mIsImeSwitcherShowing);
     }
 
-    private static String getStateString(int flags) {
+    private static String getStateString(long flags) {
         StringJoiner sj = new StringJoiner("|");
         appendFlag(sj, flags, FLAGS_IN_APP, "FLAG_IN_APP");
         appendFlag(sj, flags, FLAG_STASHED_IN_APP_SYSUI, "FLAG_STASHED_IN_APP_SYSUI");
@@ -1168,15 +1169,15 @@
     }
 
     private class StatePropertyHolder {
-        private final IntPredicate mStashCondition;
+        private final LongPredicate mStashCondition;
 
         private boolean mIsStashed;
         private @StashAnimation int mLastStartedTransitionType = TRANSITION_DEFAULT;
-        private int mPrevFlags;
+        private long mPrevFlags;
 
         private long mLastUnlockTransitionTimeout = 0;
 
-        StatePropertyHolder(IntPredicate stashCondition) {
+        StatePropertyHolder(LongPredicate stashCondition) {
             mStashCondition = stashCondition;
         }
 
@@ -1189,7 +1190,7 @@
          * @return mAnimator if mIsStashed changed, or {@code null} otherwise.
          */
         @Nullable
-        public Animator createSetStateAnimator(int flags, long duration) {
+        public Animator createSetStateAnimator(long flags, long duration) {
             boolean isStashed = mStashCondition.test(flags);
 
             if (DEBUG) {
@@ -1201,7 +1202,7 @@
                         + ", mIsStashed: " + mIsStashed);
             }
 
-            int changedFlags = mPrevFlags ^ flags;
+            long changedFlags = mPrevFlags ^ flags;
             if (mPrevFlags != flags) {
                 onStateChangeApplied(changedFlags);
                 mPrevFlags = flags;
@@ -1248,7 +1249,7 @@
         }
 
         /** Calculates the tag for CUJ_TASKBAR_EXPAND and CUJ_TASKBAR_COLLAPSE jank traces. */
-        private String computeTaskbarJankMonitorTag(int changedFlags) {
+        private String computeTaskbarJankMonitorTag(long changedFlags) {
             if (hasAnyFlag(changedFlags, FLAG_IN_APP)) {
                 // moving in or out of the app
                 if (hasAnyFlag(FLAG_IN_APP)) {
@@ -1268,7 +1269,7 @@
             return "";
         }
 
-        private @StashAnimation int computeTransitionType(int changedFlags) {
+        private @StashAnimation int computeTransitionType(long changedFlags) {
 
             boolean hotseatHiddenDuringAppLaunch =
                     !mControllers.uiController.isHotseatIconOnTopWhenAligned()
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 2e78489..2b68b52 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -44,8 +44,9 @@
 import com.android.quickstep.util.TISBindHelper;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
-import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
+import com.android.quickstep.views.TaskView.TaskContainer;
 import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
 
 import java.io.PrintWriter;
 import java.util.Collections;
@@ -55,7 +56,6 @@
  * Base class for providing different taskbar UI
  */
 public class TaskbarUIController {
-
     public static final TaskbarUIController DEFAULT = new TaskbarUIController();
 
     // Initialized in init.
@@ -91,6 +91,10 @@
      */
     protected void onIconLayoutBoundsChanged() { }
 
+    protected String getTaskbarUIControllerName() {
+        return "TaskbarUIController";
+    }
+
     /** Called when an icon is launched. */
     @CallSuper
     public void onTaskbarIconLaunched(ItemInfo item) {
@@ -137,7 +141,7 @@
     /**
      * SysUI flags updated, see QuickStepContract.SYSUI_STATE_* values.
      */
-    public void updateStateForSysuiFlags(int sysuiFlags) {
+    public void updateStateForSysuiFlags(@SystemUiStateFlags long sysuiFlags) {
     }
 
     /**
@@ -207,7 +211,7 @@
         pw.println(String.format(
                 "%sTaskbarUIController: using an instance of %s",
                 prefix,
-                getClass().getSimpleName()));
+                getTaskbarUIControllerName()));
     }
 
     /**
@@ -259,14 +263,14 @@
                         if (foundTaskView != null) {
                             // There is already a running app of this type, use that as second app.
                             // Get index of task (0 or 1), in case it's a GroupedTaskView
-                            TaskIdAttributeContainer taskAttributes =
-                                    foundTaskView.getTaskAttributesById(foundTask.key.id);
+                            TaskContainer taskContainer =
+                                    foundTaskView.getTaskContainerById(foundTask.key.id);
                             recents.confirmSplitSelect(
                                     foundTaskView,
                                     foundTask,
-                                    taskAttributes.getIconView().getDrawable(),
-                                    taskAttributes.getThumbnailView(),
-                                    taskAttributes.getThumbnailView().getThumbnail(),
+                                    taskContainer.getIconView().getDrawable(),
+                                    taskContainer.getThumbnailViewDeprecated(),
+                                    taskContainer.getThumbnailViewDeprecated().getThumbnail(),
                                     null /* intent */,
                                     null /* user */,
                                     info);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 77f8a8a..e4ccc58 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -24,6 +24,8 @@
 import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR;
 import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning;
 import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.quickstep.RecentsAnimationDeviceState.QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -31,11 +33,13 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.DisplayCutout;
 import android.view.InputDevice;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
 
@@ -63,6 +67,9 @@
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.IconButtonView;
+import com.android.quickstep.DeviceConfigWrapper;
+import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.util.AssistStateManager;
 
 import java.util.function.Predicate;
 
@@ -71,7 +78,7 @@
  */
 public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconParent, Insettable,
         DeviceProfile.OnDeviceProfileChangeListener {
-    private static final String TAG = TaskbarView.class.getSimpleName();
+    private static final String TAG = "TaskbarView";
 
     private static final Rect sTmpRect = new Rect();
 
@@ -82,8 +89,10 @@
     private final int mItemPadding;
     private final int mFolderLeaveBehindColor;
     private final boolean mIsRtl;
+    private final boolean mIsTransientTaskbar;
 
     private final TaskbarActivityContext mActivityContext;
+    private final RecentsAnimationDeviceState mDeviceState;
 
     // Initialized in init.
     private TaskbarViewCallbacks mControllerCallbacks;
@@ -95,6 +104,11 @@
 
     // Only non-null when device supports having an All Apps button.
     private @Nullable IconButtonView mAllAppsButton;
+    private Runnable mAllAppsTouchRunnable;
+    private long mAllAppsButtonTouchDelayMs;
+    private boolean mAllAppsTouchTriggered;
+    private MotionEvent mCurrentDownEvent;
+    private float mTouchSlopSquared;
 
     // Only non-null when device supports having an All Apps button.
     private @Nullable IconButtonView mTaskbarDivider;
@@ -124,12 +138,11 @@
         mActivityContext = ActivityContext.lookupContext(context);
         mIconLayoutBounds = mActivityContext.getTransientTaskbarBounds();
         Resources resources = getResources();
-        boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivityContext)
+        mIsTransientTaskbar = DisplayController.isTransientTaskbar(mActivityContext)
                 && !mActivityContext.isPhoneMode();
         mIsRtl = Utilities.isRtl(resources);
         mTransientTaskbarMinWidth = resources.getDimension(R.dimen.transient_taskbar_min_width);
 
-
         onDeviceProfileChanged(mActivityContext.getDeviceProfile());
 
         int actualMargin = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
@@ -159,7 +172,7 @@
         mAllAppsButton = (IconButtonView) LayoutInflater.from(context)
                 .inflate(R.layout.taskbar_all_apps_button, this, false);
         mAllAppsButton.setIconDrawable(resources.getDrawable(
-                getAllAppsButton(isTransientTaskbar)));
+                getAllAppsButton(mIsTransientTaskbar)));
         mAllAppsButton.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
         mAllAppsButton.setForegroundTint(
                 mActivityContext.getColor(R.color.all_apps_button_color));
@@ -175,6 +188,13 @@
 
         // TODO: Disable touch events on QSB otherwise it can crash.
         mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
+
+        // Default long press (touch) delay = 400ms
+        mAllAppsButtonTouchDelayMs = ViewConfiguration.getLongPressTimeout();
+        // Default touch slop
+        mDeviceState = new RecentsAnimationDeviceState(mContext);
+        mTouchSlopSquared = mDeviceState.getSquaredTouchSlop(
+                QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON, 1);
     }
 
     @DrawableRes
@@ -265,11 +285,26 @@
         mIconLongClickListener = mControllerCallbacks.getIconOnLongClickListener();
 
         if (mAllAppsButton != null) {
-            mAllAppsButton.setOnClickListener(mControllerCallbacks.getAllAppsButtonClickListener());
-            mAllAppsButton.setOnLongClickListener(
-                    mControllerCallbacks.getAllAppsButtonLongClickListener());
+            mAllAppsButton.setOnClickListener(this::onAllAppsButtonClick);
+            mAllAppsButton.setOnLongClickListener(this::onAllAppsButtonLongClick);
+            mAllAppsButton.setOnTouchListener(this::onAllAppsButtonTouch);
             mAllAppsButton.setHapticFeedbackEnabled(
                     mControllerCallbacks.isAllAppsButtonHapticFeedbackEnabled());
+            mAllAppsTouchRunnable = () -> {
+                mControllerCallbacks.triggerAllAppsButtonLongClick();
+                mAllAppsTouchTriggered = true;
+            };
+            AssistStateManager assistStateManager = AssistStateManager.INSTANCE.get(mContext);
+            if (DeviceConfigWrapper.get().getCustomLpaaThresholds()
+                    && assistStateManager.getLPNHDurationMillis().isPresent()) {
+                mAllAppsButtonTouchDelayMs = assistStateManager.getLPNHDurationMillis().get();
+            }
+            if (DeviceConfigWrapper.get().getCustomLpaaThresholds()
+                    && assistStateManager.getLPNHCustomSlopMultiplier().isPresent()) {
+                mTouchSlopSquared = mDeviceState.getSquaredTouchSlop(
+                        QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON,
+                        assistStateManager.getLPNHCustomSlopMultiplier().get());
+            }
         }
         if (mTaskbarDivider != null && !mActivityContext.isThreeButtonNav()) {
             mTaskbarDivider.setOnLongClickListener(
@@ -305,7 +340,6 @@
         }
         removeView(mQsb);
 
-
         for (int i = 0; i < hotseatItemInfos.length; i++) {
             ItemInfo hotseatItemInfo = hotseatItemInfos[i];
             if (hotseatItemInfo == null) {
@@ -689,4 +723,63 @@
         }
         return mAllAppsButton;
     }
+
+    private boolean onAllAppsButtonTouch(View view, MotionEvent ev) {
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                if (mCurrentDownEvent != null) {
+                    mCurrentDownEvent.recycle();
+                }
+                mCurrentDownEvent = MotionEvent.obtain(ev);
+                mAllAppsTouchTriggered = false;
+                MAIN_EXECUTOR.getHandler().postDelayed(
+                        mAllAppsTouchRunnable, mAllAppsButtonTouchDelayMs);
+                break;
+            case MotionEvent.ACTION_MOVE:
+                if (!MAIN_EXECUTOR.getHandler().hasCallbacks(mAllAppsTouchRunnable)
+                        || mIsTransientTaskbar) {
+                    break;
+                }
+                float dx = ev.getX() - mCurrentDownEvent.getX();
+                float dy = ev.getY() - mCurrentDownEvent.getY();
+                double distanceSquared = (dx * dx) + (dy * dy);
+                if (distanceSquared > mTouchSlopSquared) {
+                    Log.d(TAG, "Touch slop out");
+                    cancelAllAppsButtonTouch();
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                cancelAllAppsButtonTouch();
+        }
+        return false;
+    }
+
+    private void cancelAllAppsButtonTouch() {
+        MAIN_EXECUTOR.getHandler().removeCallbacks(mAllAppsTouchRunnable);
+        // ACTION_UP is first triggered, then click listener / long-click listener is triggered on
+        // the next frame, so we need to post twice and delay the reset.
+        if (mAllAppsButton != null) {
+            mAllAppsButton.post(() -> {
+                mAllAppsButton.post(() -> {
+                    mAllAppsTouchTriggered = false;
+                });
+            });
+        }
+    }
+
+    private void onAllAppsButtonClick(View view) {
+        if (!mAllAppsTouchTriggered) {
+            mControllerCallbacks.triggerAllAppsButtonClick(view);
+        }
+    }
+
+    // Handle long click from Switch Access and Voice Access
+    private boolean onAllAppsButtonLongClick(View view) {
+        if (!MAIN_EXECUTOR.getHandler().hasCallbacks(mAllAppsTouchRunnable)
+                && !mAllAppsTouchTriggered) {
+            mControllerCallbacks.triggerAllAppsButtonLongClick();
+        }
+        return true;
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
index c841cac..3c646cb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
@@ -46,20 +46,17 @@
         return mActivity.getItemOnClickListener();
     }
 
-    public View.OnClickListener getAllAppsButtonClickListener() {
-        return v -> {
-            InteractionJankMonitorWrapper.begin(v, Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS,
-                    /* tag= */ "TASKBAR_BUTTON");
-            mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP);
-            mControllers.taskbarAllAppsController.toggle();
-        };
+    /** Trigger All Apps button click action. */
+    protected void triggerAllAppsButtonClick(View v) {
+        InteractionJankMonitorWrapper.begin(v, Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS,
+                /* tag= */ "TASKBAR_BUTTON");
+        mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP);
+        mControllers.taskbarAllAppsController.toggle();
     }
 
-    public View.OnLongClickListener getAllAppsButtonLongClickListener() {
-        return v -> {
-            mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_ALLAPPS_BUTTON_LONG_PRESS);
-            return true;
-        };
+    /** Trigger All Apps button long click action. */
+    protected void triggerAllAppsButtonLongClick() {
+        mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_ALLAPPS_BUTTON_LONG_PRESS);
     }
 
     public boolean isAllAppsButtonHapticFeedbackEnabled() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index e0b446e..93814b7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -17,6 +17,7 @@
 
 import static com.android.app.animation.Interpolators.FINAL_FRAME;
 import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
@@ -79,7 +80,7 @@
  */
 public class TaskbarViewController implements TaskbarControllers.LoggableTaskbarController {
 
-    private static final String TAG = TaskbarViewController.class.getSimpleName();
+    private static final String TAG = "TaskbarViewController";
 
     private static final Runnable NO_OP = () -> { };
 
@@ -689,6 +690,12 @@
                         && mIsStashed) {
                     // Prevent All Apps icon from appearing when going from hotseat to nav handle.
                     setter.setViewAlpha(child, 0, Interpolators.clampToProgress(LINEAR, 0f, 0f));
+                } else if (enableScalingRevealHomeAnimation()) {
+                    // Tighten clamp so that these icons do not linger as the spring settles.
+                    setter.setViewAlpha(child, 0,
+                            isToHome
+                                    ? Interpolators.clampToProgress(LINEAR, 0f, 0.07f)
+                                    : Interpolators.clampToProgress(LINEAR, 0.93f, 1f));
                 } else {
                     setter.setViewAlpha(child, 0,
                             isToHome
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
index 90c3ea7..a59e81b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
@@ -158,6 +158,7 @@
     override fun setAlpha(alpha: Int) {
         paint.alpha = alpha
         arrowDrawable.alpha = alpha
+        invalidateSelf()
     }
 
     override fun getAlpha(): Int {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 66e5302..046f5b6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -31,8 +31,6 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
 
-import static java.lang.Math.abs;
-
 import android.annotation.BinderThread;
 import android.annotation.Nullable;
 import android.app.Notification;
@@ -47,7 +45,6 @@
 import android.graphics.Matrix;
 import android.graphics.Path;
 import android.graphics.Point;
-import android.graphics.Rect;
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
@@ -67,10 +64,9 @@
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.BubbleIconFactory;
 import com.android.launcher3.shortcuts.ShortcutRequest;
-import com.android.launcher3.taskbar.TaskbarControllers;
-import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.Executors.SimpleThreadFactory;
 import com.android.quickstep.SystemUiProxy;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
 import com.android.wm.shell.Flags;
 import com.android.wm.shell.bubbles.IBubblesListener;
 import com.android.wm.shell.common.bubbles.BubbleBarLocation;
@@ -94,7 +90,7 @@
  */
 public class BubbleBarController extends IBubblesListener.Stub {
 
-    private static final String TAG = BubbleBarController.class.getSimpleName();
+    private static final String TAG = "BubbleBarController";
     private static final boolean DEBUG = false;
 
     /**
@@ -117,7 +113,7 @@
                 || SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false);
     }
 
-    private static final int MASK_HIDE_BUBBLE_BAR = SYSUI_STATE_BOUNCER_SHOWING
+    private static final long MASK_HIDE_BUBBLE_BAR = SYSUI_STATE_BOUNCER_SHOWING
             | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
             | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
             | SYSUI_STATE_IME_SHOWING
@@ -125,11 +121,11 @@
             | SYSUI_STATE_QUICK_SETTINGS_EXPANDED
             | SYSUI_STATE_IME_SWITCHER_SHOWING;
 
-    private static final int MASK_HIDE_HANDLE_VIEW = SYSUI_STATE_BOUNCER_SHOWING
+    private static final long MASK_HIDE_HANDLE_VIEW = SYSUI_STATE_BOUNCER_SHOWING
             | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
             | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
 
-    private static final int MASK_SYSUI_LOCKED = SYSUI_STATE_BOUNCER_SHOWING
+    private static final long MASK_SYSUI_LOCKED = SYSUI_STATE_BOUNCER_SHOWING
             | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
             | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
 
@@ -147,12 +143,14 @@
     private BubbleBarItem mSelectedBubble;
     private BubbleBarOverflow mOverflowBubble;
 
+    private ImeVisibilityChecker mImeVisibilityChecker;
     private BubbleBarViewController mBubbleBarViewController;
     private BubbleStashController mBubbleStashController;
     private BubbleStashedHandleViewController mBubbleStashedHandleViewController;
+    private BubblePinController mBubblePinController;
 
-    // Keep track of bubble bar bounds sent to shell to avoid sending duplicate updates
-    private final Rect mLastSentBubbleBarBounds = new Rect();
+    // Cache last sent top coordinate to avoid sending duplicate updates to shell
+    private int mLastSentBubbleBarTop;
 
     /**
      * Similar to {@link BubbleBarUpdate} but rather than {@link BubbleInfo}s it uses
@@ -169,6 +167,7 @@
         BubbleBarLocation bubbleBarLocation;
         List<RemovedBubble> removedBubbles;
         List<String> bubbleKeysInOrder;
+        Point expandedViewDropTargetSize;
 
         // These need to be loaded in the background
         BubbleBarBubble addedBubble;
@@ -186,6 +185,7 @@
             bubbleBarLocation = update.bubbleBarLocation;
             removedBubbles = update.removedBubbles;
             bubbleKeysInOrder = update.bubbleKeysInOrder;
+            expandedViewDropTargetSize = update.expandedViewDropTargetSize;
         }
     }
 
@@ -212,10 +212,14 @@
         mSystemUiProxy.setBubblesListener(null);
     }
 
-    public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) {
+    /** Initializes controllers. */
+    public void init(BubbleControllers bubbleControllers,
+            ImeVisibilityChecker imeVisibilityChecker) {
+        mImeVisibilityChecker = imeVisibilityChecker;
         mBubbleBarViewController = bubbleControllers.bubbleBarViewController;
         mBubbleStashController = bubbleControllers.bubbleStashController;
         mBubbleStashedHandleViewController = bubbleControllers.bubbleStashedHandleViewController;
+        mBubblePinController = bubbleControllers.bubblePinController;
 
         bubbleControllers.runAfterInit(() -> {
             mBubbleBarViewController.setHiddenForBubbles(
@@ -252,7 +256,7 @@
     /**
      * Updates the bubble bar, handle bar, and stash controllers based on sysui state flags.
      */
-    public void updateStateForSysuiFlags(int flags) {
+    public void updateStateForSysuiFlags(@SystemUiStateFlags long flags) {
         boolean hideBubbleBar = (flags & MASK_HIDE_BUBBLE_BAR) != 0;
         mBubbleBarViewController.setHiddenForSysui(hideBubbleBar);
 
@@ -316,9 +320,9 @@
         // enabling gesture nav. also suppress animation if the bubble bar is hidden for sysui e.g.
         // the shade is open, or we're locked.
         final boolean suppressAnimation =
-                update.initialState || mBubbleBarViewController.isHiddenForSysui();
+                update.initialState || mBubbleBarViewController.isHiddenForSysui()
+                        || mImeVisibilityChecker.isImeVisible();
 
-        BubbleBarItem previouslySelectedBubble = mSelectedBubble;
         BubbleBarBubble bubbleToSelect = null;
         if (!update.removedBubbles.isEmpty()) {
             for (int i = 0; i < update.removedBubbles.size(); i++) {
@@ -374,6 +378,7 @@
             BubbleBarBubble bb = mBubbles.get(update.updatedBubble.getKey());
             // If we're not stashed, we're visible so animate
             bb.getView().updateDotVisibility(!mBubbleStashController.isStashed() /* animate */);
+            mBubbleBarViewController.animateBubbleNotification(bb, /* isExpanding= */ false);
         }
         if (update.bubbleKeysInOrder != null && !update.bubbleKeysInOrder.isEmpty()) {
             // Create the new list
@@ -403,9 +408,6 @@
         }
         if (bubbleToSelect != null) {
             setSelectedBubbleInternal(bubbleToSelect);
-            if (previouslySelectedBubble == null) {
-                mBubbleStashController.animateToInitialState(update.expanded);
-            }
         }
         if (update.shouldShowEducation) {
             mBubbleBarViewController.prepareToShowEducation();
@@ -422,6 +424,9 @@
                 updateBubbleBarLocationInternal(update.bubbleBarLocation);
             }
         }
+        if (update.expandedViewDropTargetSize != null) {
+            mBubblePinController.setDropTargetSize(update.expandedViewDropTargetSize);
+        }
     }
 
     /** Tells WMShell to show the currently selected bubble. */
@@ -436,9 +441,8 @@
                         info.getFlags() | Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
                 mSelectedBubble.getView().updateDotVisibility(true /* animate */);
             }
-            Rect bounds = getExpandedBubbleBarDisplayBounds();
-            mLastSentBubbleBarBounds.set(bounds);
-            mSystemUiProxy.showBubble(getSelectedBubbleKey(), bounds);
+            mLastSentBubbleBarTop = mBarView.getRestingTopPositionOnScreen();
+            mSystemUiProxy.showBubble(getSelectedBubbleKey(), mLastSentBubbleBarTop);
         } else {
             Log.w(TAG, "Trying to show the selected bubble but it's null");
         }
@@ -627,39 +631,17 @@
         return mIconFactory.createBadgedIconBitmap(drawable).icon;
     }
 
-    private void onBubbleBarBoundsChanged(Rect newBounds) {
-        Rect displayBounds = convertToDisplayBounds(newBounds);
-        // Only send bounds over if they changed
-        if (!displayBounds.equals(mLastSentBubbleBarBounds)) {
-            mLastSentBubbleBarBounds.set(displayBounds);
-            mSystemUiProxy.setBubbleBarBounds(displayBounds);
+    private void onBubbleBarBoundsChanged() {
+        int newTop = mBarView.getRestingTopPositionOnScreen();
+        if (newTop != mLastSentBubbleBarTop) {
+            mLastSentBubbleBarTop = newTop;
+            mSystemUiProxy.updateBubbleBarTopOnScreen(newTop);
         }
     }
 
-    /**
-     * Get bounds of the bubble bar as if it would be expanded.
-     * Calculates the bounds instead of retrieving current view location as the view may be
-     * animating.
-     */
-    private Rect getExpandedBubbleBarDisplayBounds() {
-        return convertToDisplayBounds(mBarView.getBubbleBarBounds());
-    }
-
-    private Rect convertToDisplayBounds(Rect currentBarBounds) {
-        Point displaySize = DisplayController.INSTANCE.get(mContext).getInfo().currentSize;
-        Rect displayBounds = new Rect();
-        // currentBarBounds is only useful for distance from left or right edge.
-        // It contains the current bounds, calculate the expanded bounds.
-        if (mBarView.getBubbleBarLocation().isOnLeft(mBarView.isLayoutRtl())) {
-            displayBounds.left = currentBarBounds.left;
-            displayBounds.right = (int) (currentBarBounds.left + mBarView.expandedWidth());
-        } else {
-            displayBounds.left = (int) (currentBarBounds.right - mBarView.expandedWidth());
-            displayBounds.right = currentBarBounds.right;
-        }
-        final int translation = (int) abs(mBubbleStashController.getBubbleBarTranslationY());
-        displayBounds.top = displaySize.y - currentBarBounds.height() - translation;
-        displayBounds.bottom = displaySize.y - translation;
-        return displayBounds;
+    /** Interface for checking whether the IME is visible. */
+    public interface ImeVisibilityChecker {
+        /** Whether the IME is visible. */
+        boolean isImeVisible();
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index de93ba5..23e52e6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -43,6 +43,7 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.anim.SpringAnimationBuilder;
+import com.android.launcher3.util.DisplayController;
 import com.android.wm.shell.common.bubbles.BubbleBarLocation;
 
 import java.util.List;
@@ -76,7 +77,7 @@
  */
 public class BubbleBarView extends FrameLayout {
 
-    private static final String TAG = BubbleBarView.class.getSimpleName();
+    private static final String TAG = "BubbleBarView";
 
     // TODO: (b/273594744) calculate the amount of space we have and base the max on that
     //  if it's smaller than 5.
@@ -98,23 +99,6 @@
     private static final float FADE_IN_ANIM_POSITION_SHIFT = 1 / 60f;
 
     /**
-     * Custom property to set translationX value for the bar view while a bubble is being dragged.
-     * Skips applying translation to the dragged bubble.
-     */
-    private static final FloatProperty<BubbleBarView> BUBBLE_DRAG_TRANSLATION_X =
-            new FloatProperty<>("bubbleDragTranslationX") {
-                @Override
-                public void setValue(BubbleBarView bubbleBarView, float translationX) {
-                    bubbleBarView.setTranslationXDuringBubbleDrag(translationX);
-                }
-
-                @Override
-                public Float get(BubbleBarView bubbleBarView) {
-                    return bubbleBarView.mTranslationXDuringDrag;
-                }
-            };
-
-    /**
      * Custom property to set alpha value for the bar view while a bubble is being dragged.
      * Skips applying alpha to the dragged bubble.
      */
@@ -189,8 +173,9 @@
 
     @Nullable
     private BubbleView mDraggedBubbleView;
-    private float mTranslationXDuringDrag = 0f;
-    private float mAlphaDuringDrag = 0f;
+    private float mAlphaDuringDrag = 1f;
+
+    private Controller mController;
 
     private int mPreviousLayoutDirection = LayoutDirection.UNDEFINED;
 
@@ -264,6 +249,17 @@
         });
     }
 
+    @Override
+    public void setTranslationX(float translationX) {
+        super.setTranslationX(translationX);
+        if (mDraggedBubbleView != null) {
+            // Apply reverse of the translation as an offset to the dragged view. This ensures
+            // that the dragged bubble stays at the current location on the screen and its
+            // position is not affected by the parent translation.
+            mDraggedBubbleView.setOffsetX(-translationX);
+        }
+    }
+
     /**
      * Sets new icon size and spacing between icons and bubble bar borders.
      *
@@ -322,11 +318,9 @@
         final boolean onLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl());
         mBubbleBarBackground.setAnchorLeft(onLeft);
         mRelativePivotX = onLeft ? 0f : 1f;
-        if (getLayoutParams() instanceof LayoutParams lp) {
-            lp.gravity = Gravity.BOTTOM | (onLeft ? Gravity.LEFT : Gravity.RIGHT);
-            setLayoutParams(lp);
-        }
-        invalidate();
+        LayoutParams lp = (LayoutParams) getLayoutParams();
+        lp.gravity = Gravity.BOTTOM | (onLeft ? Gravity.LEFT : Gravity.RIGHT);
+        setLayoutParams(lp); // triggers a relayout
     }
 
     /**
@@ -340,11 +334,6 @@
      * Update {@link BubbleBarLocation}
      */
     public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
-        if (mBubbleBarLocationAnimator != null) {
-            mBubbleBarLocationAnimator.removeAllListeners();
-            mBubbleBarLocationAnimator.cancel();
-            mBubbleBarLocationAnimator = null;
-        }
         resetDragAnimation();
         if (bubbleBarLocation != mBubbleBarLocation) {
             mBubbleBarLocation = bubbleBarLocation;
@@ -361,6 +350,10 @@
         }
         mDragging = dragging;
         setElevation(dragging ? mDragElevation : mBubbleElevation);
+        if (!mDragging) {
+            // Relayout after dragging to ensure that the dragged bubble is positioned correctly
+            requestLayout();
+        }
     }
 
     /**
@@ -376,22 +369,23 @@
      */
     public PointF getBubbleBarDragReleaseTranslation(PointF initialTranslation,
             BubbleBarLocation location) {
-        // Start with the initial translation. Value on y-axis can be reused.
-        final PointF dragEndTranslation = new PointF(initialTranslation);
-        // Bubble bar is laid out on left or right side of the screen. And the desired new
-        // location is on the other side. Calculate x translation value required to shift
-        // bubble bar from one side to the other.
-        final float shift = getDistanceFromOtherSide();
-        if (location.isOnLeft(isLayoutRtl())) {
-            // New location is on the left, shift left
-            // before -> |......ooo.| after -> |.ooo......|
-            dragEndTranslation.x = -shift;
-        } else {
-            // New location is on the right, shift right
-            // before -> |.ooo......| after -> |......ooo.|
-            dragEndTranslation.x = shift;
+        float dragEndTranslationX = initialTranslation.x;
+        if (getBubbleBarLocation().isOnLeft(isLayoutRtl()) != location.isOnLeft(isLayoutRtl())) {
+            // Bubble bar is laid out on left or right side of the screen. And the desired new
+            // location is on the other side. Calculate x translation value required to shift
+            // bubble bar from one side to the other.
+            final float shift = getDistanceFromOtherSide();
+            if (location.isOnLeft(isLayoutRtl())) {
+                // New location is on the left, shift left
+                // before -> |......ooo.| after -> |.ooo......|
+                dragEndTranslationX = -shift;
+            } else {
+                // New location is on the right, shift right
+                // before -> |.ooo......| after -> |......ooo.|
+                dragEndTranslationX = shift;
+            }
         }
-        return dragEndTranslation;
+        return new PointF(dragEndTranslationX, mController.getBubbleBarTranslationY());
     }
 
     /**
@@ -401,21 +395,24 @@
      * from the internal location that was used during bubble bar layout, translation values are
      * calculated to position the bar at the desired location.
      *
-     * @param initialTranslation initial bubble bar translation at the start of drag
+     * @param initialTranslation initial bubble translation inside the bar at the start of drag
      * @param location           desired location of the bubble bar when drag is released
      * @return point with x and y values representing translation on x and y-axis
      */
     public PointF getDraggedBubbleReleaseTranslation(PointF initialTranslation,
             BubbleBarLocation location) {
-        // Start with bubble bar translation
-        final PointF dragEndTranslation = new PointF(
-                getBubbleBarDragReleaseTranslation(initialTranslation, location));
-        // Apply individual bubble translation, as the order may have changed
-        int viewIndex = indexOfChild(mDraggedBubbleView);
-        dragEndTranslation.x += getExpandedBubbleTranslationX(viewIndex,
-                getChildCount(),
-                location.isOnLeft(isLayoutRtl()));
-        return dragEndTranslation;
+        float dragEndTranslationX = initialTranslation.x;
+        boolean newLocationOnLeft = location.isOnLeft(isLayoutRtl());
+        if (getBubbleBarLocation().isOnLeft(isLayoutRtl()) != newLocationOnLeft) {
+            // Calculate translationX based on bar and bubble translations
+            float bubbleBarTx = getBubbleBarDragReleaseTranslation(initialTranslation, location).x;
+            float bubbleTx =
+                    getExpandedBubbleTranslationX(
+                            indexOfChild(mDraggedBubbleView), getChildCount(), newLocationOnLeft);
+            dragEndTranslationX = bubbleBarTx + bubbleTx;
+        }
+        // translationY does not change during drag and can be reused
+        return new PointF(dragEndTranslationX, initialTranslation.y);
     }
 
     private float getDistanceFromOtherSide() {
@@ -459,14 +456,13 @@
     }
 
     private Animator getLocationUpdateFadeOutAnimator(BubbleBarLocation newLocation) {
-        final FloatProperty<? super BubbleBarView> txProp = getLocationAnimTranslationXProperty();
         final float shift =
                 getResources().getDisplayMetrics().widthPixels * FADE_OUT_ANIM_POSITION_SHIFT;
         final boolean onLeft = newLocation.isOnLeft(isLayoutRtl());
-        final float tx = txProp.get(this) + (onLeft ? -shift : shift);
+        final float tx = getTranslationX() + (onLeft ? -shift : shift);
 
-        ObjectAnimator positionAnim = ObjectAnimator.ofFloat(this, txProp, tx).setDuration(
-                FADE_OUT_ANIM_POSITION_DURATION_MS);
+        ObjectAnimator positionAnim = ObjectAnimator.ofFloat(this, VIEW_TRANSLATE_X, tx)
+                .setDuration(FADE_OUT_ANIM_POSITION_DURATION_MS);
         positionAnim.setInterpolator(EMPHASIZED_ACCELERATE);
 
         ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(this, getLocationAnimAlphaProperty(), 0f)
@@ -505,7 +501,7 @@
                 .setEndValue(finalTx)
                 .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
                 .setStiffness(FADE_IN_ANIM_POSITION_SPRING_STIFFNESS)
-                .build(this, getLocationAnimTranslationXProperty());
+                .build(this, VIEW_TRANSLATE_X);
 
         ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(this, getLocationAnimAlphaProperty(), 1f)
                 .setDuration(FADE_IN_ANIM_ALPHA_DURATION_MS);
@@ -516,15 +512,6 @@
     }
 
     /**
-     * Get property that can be used to animate the translation-x value for the bar.
-     * When a bubble is being dragged, uses {@link #BUBBLE_DRAG_TRANSLATION_X}.
-     * Falls back to {@link com.android.launcher3.LauncherAnimUtils#VIEW_TRANSLATE_X} otherwise.
-     */
-    private FloatProperty<? super BubbleBarView> getLocationAnimTranslationXProperty() {
-        return mDraggedBubbleView == null ? VIEW_TRANSLATE_X : BUBBLE_DRAG_TRANSLATION_X;
-    }
-
-    /**
      * Get property that can be used to animate the alpha value for the bar.
      * When a bubble is being dragged, uses {@link #BUBBLE_DRAG_ALPHA}.
      * Falls back to {@link com.android.launcher3.LauncherAnimUtils#VIEW_ALPHA} otherwise.
@@ -534,31 +521,6 @@
     }
 
     /**
-     * Set translation-x value for the bar while a bubble is being dragged.
-     * We can not update translation on the bar directly because the dragged bubble would be
-     * affected as well. As it is a child view.
-     * Instead, while a bubble is being dragged, set translation on each child view, that is not the
-     * dragged view. And set a translation on the background.
-     * This allows for the dragged bubble view to remain in position while the bar moves during
-     * animation.
-     */
-    private void setTranslationXDuringBubbleDrag(float translationX) {
-        mTranslationXDuringDrag = translationX;
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            BubbleView view = (BubbleView) getChildAt(i);
-            if (view != mDraggedBubbleView) {
-                view.setBubbleBarTranslationX(translationX);
-            }
-        }
-        if (mBubbleBarBackground != null) {
-            mTempBackgroundBounds.set(mBubbleBarBackground.getBounds());
-            mTempBackgroundBounds.offsetTo((int) translationX, 0);
-            mBubbleBarBackground.setBounds(mTempBackgroundBounds);
-        }
-    }
-
-    /**
      * Set alpha value for the bar while a bubble is being dragged.
      * We can not update the alpha on the bar directly because the dragged bubble would be affected
      * as well. As it is a child view.
@@ -587,13 +549,21 @@
             mBubbleBarLocationAnimator.cancel();
             mBubbleBarLocationAnimator = null;
         }
-        setTranslationXDuringBubbleDrag(0f);
         setAlphaDuringBubbleDrag(1f);
         setTranslationX(0f);
         setAlpha(1f);
     }
 
     /**
+     * Get bubble bar top coordinate on screen when bar is resting
+     */
+    public int getRestingTopPositionOnScreen() {
+        int displayHeight = DisplayController.INSTANCE.get(getContext()).getInfo().currentSize.y;
+        int bubbleBarHeight = getBubbleBarBounds().height();
+        return displayHeight - bubbleBarHeight + (int) mController.getBubbleBarTranslationY();
+    }
+
+    /**
      * Updates the bounds with translation that may have been applied and returns the result.
      */
     public Rect getBubbleBarBounds() {
@@ -706,6 +676,10 @@
                 // Skip the dragged bubble. Its translation is managed by the drag controller.
                 continue;
             }
+            // Clear out drag translation and offset
+            bv.setDragTranslationX(0f);
+            bv.setOffsetX(0f);
+
             bv.setTranslationY(ty);
 
             // the position of the bubble when the bar is fully expanded
@@ -833,6 +807,10 @@
         mUpdateSelectedBubbleAfterCollapse = updateSelectedBubbleAfterCollapse;
     }
 
+    void setController(Controller controller) {
+        mController = controller;
+    }
+
     /**
      * Sets which bubble view should be shown as selected.
      */
@@ -851,10 +829,6 @@
     public void setDraggedBubble(@Nullable BubbleView view) {
         if (mDraggedBubbleView != null) {
             mDraggedBubbleView.setZ(0);
-            if (view == null) {
-                // We are clearing the dragged bubble, reset drag
-                resetDragAnimation();
-            }
         }
         mDraggedBubbleView = view;
         if (view != null) {
@@ -1009,7 +983,10 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (!mIsBarExpanded && !mIsAnimatingNewBubble) {
+        if (mIsAnimatingNewBubble) {
+            mController.onBubbleBarTouchedWhileAnimating();
+        }
+        if (!mIsBarExpanded) {
             // When the bar is collapsed, all taps on it should expand it.
             return true;
         }
@@ -1020,4 +997,14 @@
     public boolean isAnimatingNewBubble() {
         return mIsAnimatingNewBubble;
     }
+
+    /** Interface for BubbleBarView to communicate with its controller. */
+    interface Controller {
+
+        /** Returns the translation Y that the bubble bar should have. */
+        float getBubbleBarTranslationY();
+
+        /** Notifies the controller that the bubble bar was touched while it was animating. */
+        void onBubbleBarTouchedWhileAnimating();
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 0b74e15..f614dc6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -55,7 +55,7 @@
  */
 public class BubbleBarViewController {
 
-    private static final String TAG = BubbleBarViewController.class.getSimpleName();
+    private static final String TAG = "BubbleBarViewController";
     private static final float APP_ICON_SMALL_DP = 44f;
     private static final float APP_ICON_MEDIUM_DP = 48f;
     private static final float APP_ICON_LARGE_DP = 52f;
@@ -93,8 +93,6 @@
     @Nullable
     private BubbleBarBoundsChangeListener mBoundsChangeListener;
 
-    private final Rect mPreviousBubbleBarBounds = new Rect();
-
     public BubbleBarViewController(TaskbarActivityContext activity, BubbleBarView barView) {
         mActivity = activity;
         mBarView = barView;
@@ -122,16 +120,23 @@
         mBarView.addOnLayoutChangeListener(
                 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
                     mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged();
-                    Rect bubbleBarBounds = mBarView.getBubbleBarBounds();
-                    if (!bubbleBarBounds.equals(mPreviousBubbleBarBounds)) {
-                        mPreviousBubbleBarBounds.set(bubbleBarBounds);
-                        if (mBoundsChangeListener != null) {
-                            mBoundsChangeListener.onBoundsChanged(bubbleBarBounds);
-                        }
+                    if (mBoundsChangeListener != null) {
+                        mBoundsChangeListener.onBoundsChanged();
                     }
                 });
 
         mBubbleBarViewAnimator = new BubbleBarViewAnimator(mBarView, mBubbleStashController);
+        mBarView.setController(new BubbleBarView.Controller() {
+            @Override
+            public float getBubbleBarTranslationY() {
+                return mBubbleStashController.getBubbleBarTranslationY();
+            }
+
+            @Override
+            public void onBubbleBarTouchedWhileAnimating() {
+                BubbleBarViewController.this.onBubbleBarTouchedWhileAnimating();
+            }
+        });
     }
 
     private void onBubbleClicked(View v) {
@@ -140,14 +145,6 @@
             Log.e(TAG, "bubble click listener, bubble was null");
         }
 
-        if (mBarView.isAnimatingNewBubble()) {
-            mBubbleBarViewAnimator.onBubbleClickedWhileAnimating();
-            mBubbleStashController.showBubbleBarImmediate();
-            setExpanded(true);
-            mBubbleBarController.showAndSelectBubble(bubble);
-            return;
-        }
-
         final String currentlySelected = mBubbleBarController.getSelectedBubbleKey();
         if (mBarView.isExpanded() && Objects.equals(bubble.getKey(), currentlySelected)) {
             // Tapping the currently selected bubble while expanded collapses the view.
@@ -158,6 +155,11 @@
         }
     }
 
+    private void onBubbleBarTouchedWhileAnimating() {
+        mBubbleBarViewAnimator.onBubbleBarTouchedWhileAnimating();
+        mBubbleStashController.onNewBubbleAnimationInterrupted(false, mBarView.getTranslationY());
+    }
+
     private void onBubbleBarClicked() {
         if (mShouldShowEducation) {
             mShouldShowEducation = false;
@@ -169,6 +171,10 @@
             // Show user education relative to the reference point
             mSystemUiProxy.showUserEducation(position);
         } else {
+            // ensure that the bubble bar has the correct translation. we may have just interrupted
+            // the animation by touching the bubble bar.
+            mBubbleBarTranslationY.animateToValue(mBubbleStashController.getBubbleBarTranslationY())
+                    .start();
             setExpanded(true);
         }
     }
@@ -402,20 +408,31 @@
             b.getView().setOnClickListener(mBubbleClickListener);
             mBubbleDragController.setupBubbleView(b.getView());
 
-            if (suppressAnimation) {
+            if (suppressAnimation || !(b instanceof BubbleBarBubble bubble)) {
                 return;
             }
-
-            boolean isInApp = mTaskbarStashController.isInApp();
-            // only animate the new bubble if we're in an app and not auto expanding
-            if (b instanceof BubbleBarBubble && isInApp && !isExpanding && !isExpanded()) {
-                mBubbleBarViewAnimator.animateBubbleInForStashed((BubbleBarBubble) b);
-            }
+            animateBubbleNotification(bubble, isExpanding);
         } else {
             Log.w(TAG, "addBubble, bubble was null!");
         }
     }
 
+    /** Animates the bubble bar to notify the user about a bubble change. */
+    public void animateBubbleNotification(BubbleBarBubble bubble, boolean isExpanding) {
+        boolean isInApp = mTaskbarStashController.isInApp();
+        // if this is the first bubble, animate to the initial state. one bubble is the overflow
+        // so check for at most 2 children.
+        if (mBarView.getChildCount() <= 2) {
+            mBubbleBarViewAnimator.animateToInitialState(bubble, isInApp, isExpanding);
+            return;
+        }
+
+        // only animate the new bubble if we're in an app and not auto expanding
+        if (isInApp && !isExpanding && !isExpanded()) {
+            mBubbleBarViewAnimator.animateBubbleInForStashed(bubble);
+        }
+    }
+
     /**
      * Reorders the bubbles based on the provided list.
      */
@@ -474,7 +491,7 @@
      * that a bubble is being dragged to dismiss.
      * @param bubbleView dragged bubble view
      */
-    public void onDragStart(@NonNull BubbleView bubbleView) {
+    public void onBubbleDragStart(@NonNull BubbleView bubbleView) {
         if (bubbleView.getBubble() == null) return;
 
         mSystemUiProxy.startBubbleDrag(bubbleView.getBubble().getKey());
@@ -483,21 +500,25 @@
 
     /**
      * Notifies SystemUI to expand the selected bubble when the bubble is released.
-     * @param bubbleView dragged bubble view
      */
-    public void onDragRelease(@NonNull BubbleView bubbleView, BubbleBarLocation location) {
-        if (bubbleView.getBubble() == null) return;
-        // TODO(b/330585402): send new bubble bar bounds to shell for the animation
-        mSystemUiProxy.stopBubbleDrag(bubbleView.getBubble().getKey(), location);
+    public void onBubbleDragRelease(BubbleBarLocation location) {
+        mSystemUiProxy.stopBubbleDrag(location, mBarView.getRestingTopPositionOnScreen());
     }
 
     /**
      * Notifies {@link BubbleBarView} that drag and all animations are finished.
      */
-    public void onDragEnd() {
+    public void onBubbleDragEnd() {
         mBarView.setDraggedBubble(null);
     }
 
+    /** Notifies that dragging the bubble bar ended. */
+    public void onBubbleBarDragEnd() {
+        // we may have changed the bubble bar translation Y value from the value it had at the
+        // beginning of the drag, so update the translation Y animator state
+        mBubbleBarTranslationY.updateValue(mBarView.getTranslationY());
+    }
+
     /**
      * Get translation for bubble bar when drag is released.
      *
@@ -505,9 +526,6 @@
      */
     public PointF getBubbleBarDragReleaseTranslation(PointF initialTranslation,
             BubbleBarLocation location) {
-        if (location == mBarView.getBubbleBarLocation()) {
-            return initialTranslation;
-        }
         return mBarView.getBubbleBarDragReleaseTranslation(initialTranslation, location);
     }
 
@@ -529,7 +547,7 @@
      * @param bubble dismissed bubble item
      */
     public void onDismissBubbleWhileDragging(@NonNull BubbleBarItem bubble) {
-        mSystemUiProxy.removeBubble(bubble.getKey());
+        mSystemUiProxy.dragBubbleToDismiss(bubble.getKey());
     }
 
     /**
@@ -551,6 +569,6 @@
      */
     public interface BubbleBarBoundsChangeListener {
         /** Called when bounds have changed */
-        void onBoundsChanged(Rect newBounds);
+        void onBoundsChanged();
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
index 295477c..32d6375 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
@@ -64,7 +64,8 @@
      * in constructors for now, as some controllers may still be waiting for init().
      */
     public void init(TaskbarControllers taskbarControllers) {
-        bubbleBarController.init(taskbarControllers, this);
+        bubbleBarController.init(this,
+                taskbarControllers.navbarButtonsViewController::isImeVisible);
         bubbleBarViewController.init(taskbarControllers, this);
         bubbleStashedHandleViewController.init(taskbarControllers, this);
         bubbleStashController.init(taskbarControllers, this);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
index a40f33c..0e6fa3c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
@@ -40,7 +40,7 @@
  * @see BubbleDragController
  */
 public class BubbleDismissController {
-    private static final String TAG = BubbleDismissController.class.getSimpleName();
+    private static final String TAG = "BubbleDismissController";
     private static final float FLING_TO_DISMISS_MIN_VELOCITY = 6000f;
     private final TaskbarActivityContext mActivity;
     private final TaskbarDragLayer mDragLayer;
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
index 49f114a..287e906 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
@@ -60,6 +60,7 @@
     private final float mBubbleFocusedScale;
     private final float mBubbleCapturedScale;
     private final float mDismissCapturedScale;
+    private final FloatPropertyCompat<View> mTranslationXProperty;
 
     /**
      * Should be initialised for each dragged view
@@ -81,9 +82,28 @@
         if (view instanceof BubbleBarView) {
             mBubbleFocusedScale = SCALE_BUBBLE_BAR_FOCUSED;
             mBubbleCapturedScale = mDismissCapturedScale;
+            mTranslationXProperty = DynamicAnimation.TRANSLATION_X;
         } else {
             mBubbleFocusedScale = SCALE_BUBBLE_FOCUSED;
             mBubbleCapturedScale = SCALE_BUBBLE_CAPTURED;
+            // Wrap BubbleView.DRAG_TRANSLATION_X as it can't be cast to FloatPropertyCompat<View>
+            mTranslationXProperty = new FloatPropertyCompat<>(
+                    BubbleView.DRAG_TRANSLATION_X.getName()) {
+                @Override
+                public float getValue(View object) {
+                    if (object instanceof BubbleView bubbleView) {
+                        return BubbleView.DRAG_TRANSLATION_X.get(bubbleView);
+                    }
+                    return 0;
+                }
+
+                @Override
+                public void setValue(View object, float value) {
+                    if (object instanceof BubbleView bubbleView) {
+                        BubbleView.DRAG_TRANSLATION_X.setValue(bubbleView, value);
+                    }
+                }
+            };
         }
     }
 
@@ -120,7 +140,7 @@
         mBubbleAnimator
                 .spring(DynamicAnimation.SCALE_X, 1f)
                 .spring(DynamicAnimation.SCALE_Y, 1f)
-                .spring(DynamicAnimation.TRANSLATION_X, restingPosition.x, velocity.x,
+                .spring(mTranslationXProperty, restingPosition.x, velocity.x,
                         mTranslationConfig)
                 .spring(DynamicAnimation.TRANSLATION_Y, restingPosition.y, velocity.y,
                         mTranslationConfig)
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
index 1764f75..2ebc3e8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
@@ -97,21 +97,23 @@
             @Override
             void onDragStart() {
                 mBubblePinController.setListener(mLocationChangeListener);
-                mBubbleBarViewController.onDragStart(bubbleView);
+                mBubbleBarViewController.onBubbleDragStart(bubbleView);
                 mBubblePinController.onDragStart(
                         mBubbleBarViewController.getBubbleBarLocation().isOnLeft(
                                 bubbleView.isLayoutRtl()));
             }
 
             @Override
-            protected void onDragUpdate(float x, float y) {
+            protected void onDragUpdate(float x, float y, float newTx, float newTy) {
+                bubbleView.setDragTranslationX(newTx);
+                bubbleView.setTranslationY(newTy);
                 mBubblePinController.onDragUpdate(x, y);
             }
 
             @Override
             protected void onDragRelease() {
                 mBubblePinController.onDragEnd();
-                mBubbleBarViewController.onDragRelease(bubbleView, mReleasedLocation);
+                mBubbleBarViewController.onBubbleDragRelease(mReleasedLocation);
             }
 
             @Override
@@ -122,7 +124,7 @@
             @Override
             void onDragEnd() {
                 mBubbleBarController.updateBubbleBarLocation(mReleasedLocation);
-                mBubbleBarViewController.onDragEnd();
+                mBubbleBarViewController.onBubbleDragEnd();
                 mBubblePinController.setListener(null);
             }
 
@@ -145,12 +147,7 @@
             private BubbleBarLocation mReleasedLocation = BubbleBarLocation.DEFAULT;
 
             private final LocationChangeListener mLocationChangeListener =
-                    new LocationChangeListener() {
-                        @Override
-                        public void onRelease(@NonNull BubbleBarLocation location) {
-                            mReleasedLocation = location;
-                        }
-                    };
+                    location -> mReleasedLocation = location;
 
             @Override
             protected boolean onTouchDown(@NonNull View view, @NonNull MotionEvent event) {
@@ -172,7 +169,9 @@
             }
 
             @Override
-            protected void onDragUpdate(float x, float y) {
+            protected void onDragUpdate(float x, float y, float newTx, float newTy) {
+                bubbleBarView.setTranslationX(newTx);
+                bubbleBarView.setTranslationY(newTy);
                 mBubbleBarPinController.onDragUpdate(x, y);
             }
 
@@ -193,6 +192,7 @@
                 bubbleBarView.setIsDragging(false);
                 // Restoring the initial pivot for the bubble bar view
                 bubbleBarView.setRelativePivot(initialRelativePivot.x, initialRelativePivot.y);
+                mBubbleBarViewController.onBubbleBarDragEnd();
                 mBubbleBarPinController.setListener(null);
             }
 
@@ -265,8 +265,7 @@
          * Called when bubble is dragged to new coordinates.
          * Not called while bubble is stuck to the dismiss target.
          */
-        protected void onDragUpdate(float x, float y) {
-        }
+        protected abstract void onDragUpdate(float x, float y, float newTx, float newTy);
 
         /**
          * Called when the dragging interaction has ended and all the animations have completed
@@ -411,9 +410,9 @@
         private void drag(@NonNull View view, @NonNull MotionEvent event, float dx, float dy,
                 float x, float y) {
             if (mBubbleDismissController.handleTouchEvent(event)) return;
-            view.setTranslationX(mViewInitialPosition.x + dx);
-            view.setTranslationY(mViewInitialPosition.y + dy);
-            onDragUpdate(x, y);
+            final float newTx = mViewInitialPosition.x + dx;
+            final float newTy = mViewInitialPosition.y + dy;
+            onDragUpdate(x, y, newTx, newTy);
         }
 
         private void stopDragging(@NonNull View view, @NonNull MotionEvent event) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubblePinController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubblePinController.kt
index fef7fa1..a77e685 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubblePinController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubblePinController.kt
@@ -37,12 +37,17 @@
     screenSizeProvider: () -> Point
 ) : BaseBubblePinController(screenSizeProvider) {
 
+    var dropTargetSize: Point? = null
+
     private lateinit var bubbleBarViewController: BubbleBarViewController
     private lateinit var bubbleStashController: BubbleStashController
     private var exclRectWidth: Float = 0f
     private var exclRectHeight: Float = 0f
 
     private var dropTargetView: View? = null
+    // Fallback width and height in case shell has not sent the size over
+    private var dropTargetDefaultWidth: Int = 0
+    private var dropTargetDefaultHeight: Int = 0
     private var dropTargetMargin: Int = 0
 
     fun init(bubbleControllers: BubbleControllers) {
@@ -50,6 +55,14 @@
         bubbleStashController = bubbleControllers.bubbleStashController
         exclRectWidth = context.resources.getDimension(R.dimen.bubblebar_dismiss_zone_width)
         exclRectHeight = context.resources.getDimension(R.dimen.bubblebar_dismiss_zone_height)
+        dropTargetDefaultWidth =
+            context.resources.getDimensionPixelSize(
+                R.dimen.bubble_expanded_view_drop_target_default_width
+            )
+        dropTargetDefaultHeight =
+            context.resources.getDimensionPixelSize(
+                R.dimen.bubble_expanded_view_drop_target_default_height
+            )
         dropTargetMargin =
             context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_drop_target_margin)
     }
@@ -75,7 +88,6 @@
         return LayoutInflater.from(context)
             .inflate(R.layout.bubble_expanded_view_drop_target, container, false)
             .also { view ->
-                // TODO(b/330585402): dynamic height for the drop target based on actual height
                 dropTargetView = view
                 container.addView(view)
             }
@@ -88,6 +100,8 @@
         val bubbleBarBounds = bubbleBarViewController.bubbleBarBounds
         dropTargetView?.updateLayoutParams<FrameLayout.LayoutParams> {
             gravity = BOTTOM or (if (onLeft) LEFT else RIGHT)
+            width = dropTargetSize?.x ?: dropTargetDefaultWidth
+            height = dropTargetSize?.y ?: dropTargetDefaultHeight
             bottomMargin =
                 -bubbleStashController.bubbleBarTranslationY.toInt() +
                     bubbleBarBounds.height() +
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
index 4b3416c..5d01b9b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
@@ -42,7 +42,7 @@
  */
 public class BubbleStashController {
 
-    private static final String TAG = BubbleStashController.class.getSimpleName();
+    private static final String TAG = "BubbleStashController";
 
     /**
      * How long to stash/unstash.
@@ -123,21 +123,17 @@
     }
 
     /**
-     * Animates the bubble bar and handle to their initial state, transitioning from the state where
-     * both views are invisible. Called when the first bubble is added or when the device is
+     * Animates the handle (or bubble bar depending on state) to be visible after the device is
      * unlocked.
      *
      * <p>Normally either the bubble bar or the handle is visible,
      * and {@link #showBubbleBar(boolean)} and {@link #stashBubbleBar()} are used to transition
      * between these two states. But the transition from the state where both the bar and handle
      * are invisible is slightly different.
-     *
-     * <p>The initial state will depend on the current state of the device, i.e. overview, home etc
-     * and whether bubbles are requested to be expanded.
      */
-    public void animateToInitialState(boolean expanding) {
+    private void animateAfterUnlock() {
         AnimatorSet animatorSet = new AnimatorSet();
-        if (expanding || mBubblesShowingOnHome || mBubblesShowingOnOverview) {
+        if (mBubblesShowingOnHome || mBubblesShowingOnOverview) {
             mIsStashed = false;
             animatorSet.playTogether(mIconScaleForStash.animateToValue(1),
                     mIconTranslationYForStash.animateToValue(getBubbleBarTranslationY()),
@@ -217,7 +213,7 @@
         if (isSysuiLocked != mIsSysuiLocked) {
             mIsSysuiLocked = isSysuiLocked;
             if (!mIsSysuiLocked && mBarViewController.hasBubbles()) {
-                animateToInitialState(false /* expanding */);
+                animateAfterUnlock();
             }
         }
     }
@@ -453,4 +449,9 @@
         mIsStashed = isStashed;
         onIsStashedChanged();
     }
+
+    /** Set the translation Y for the stashed handle. */
+    public void setHandleTranslationY(float ty) {
+        mHandleViewController.setTranslationYForSwipe(ty);
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
index 3dc4ebc..61a6bce 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
@@ -22,11 +22,13 @@
 import android.graphics.Outline;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.FloatProperty;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewOutlineProvider;
 import android.widget.ImageView;
 
+import androidx.annotation.NonNull;
 import androidx.constraintlayout.widget.ConstraintLayout;
 
 import com.android.launcher3.R;
@@ -47,6 +49,25 @@
     public static final int DEFAULT_PATH_SIZE = 100;
 
     /**
+     * Property to update drag translation value.
+     *
+     * @see BubbleView#getDragTranslationX()
+     * @see BubbleView#setDragTranslationX(float)
+     */
+    public static final FloatProperty<BubbleView> DRAG_TRANSLATION_X = new FloatProperty<>(
+            "dragTranslationX") {
+        @Override
+        public void setValue(@NonNull BubbleView bubbleView, float value) {
+            bubbleView.setDragTranslationX(value);
+        }
+
+        @Override
+        public Float get(BubbleView bubbleView) {
+            return bubbleView.getDragTranslationX();
+        }
+    };
+
+    /**
      * Flags that suppress the visibility of the 'new' dot or the app badge, for one reason or
      * another. If any of these flags are set, the dot will not be shown.
      * If {@link SuppressionFlag#BEHIND_STACK} then the app badge will not be shown.
@@ -66,8 +87,8 @@
     private final ImageView mAppIcon;
     private final int mBubbleSize;
 
-    private float mBubbleBarTranslationX = 0f;
-    private float mTranslationX = 0f;
+    private float mDragTranslationX;
+    private float mOffsetX;
 
     private DotRenderer mDotRenderer;
     private DotRenderer.DrawParams mDrawParams;
@@ -129,33 +150,37 @@
         outline.setOval(inset, inset, inset + normalizedSize, inset + normalizedSize);
     }
 
-    @Override
-    public void setTranslationX(float translationX) {
-        // Overriding setting translationX as it can be a combination of the parent translation
-        // and current view translation.
-        // When a BubbleView is being dragged to pin the bubble bar to other side, we animate the
-        // bar to the new location during the drag.
-        // One part of the animation is updating the translation of the bubble bar. But doing
-        // that also updates the translation for the child views, like the dragged bubble.
-        // To get around that, we instead apply translation on each child view of bubble bar. It
-        // is applied as bubble bar translation. This results in BubbleView's translation being a
-        // sum of the translation it has and the parent bubble bar translation.
-        mTranslationX = translationX;
-        applyTranslation();
+    /**
+     * Set translation-x while this bubble is being dragged.
+     * Translation applied to the view is a sum of {@code translationX} and offset defined by
+     * {@link #setOffsetX(float)}.
+     */
+    public void setDragTranslationX(float translationX) {
+        mDragTranslationX = translationX;
+        applyDragTranslation();
     }
 
     /**
-     * Translation of the bubble bar that hosts this bubble.
-     * Is applied together with translation applied on the view through
-     * {@link #setTranslationX(float)}.
+     * Get translation value applied via {@link #setDragTranslationX(float)}.
      */
-    void setBubbleBarTranslationX(float translationX) {
-        mBubbleBarTranslationX = translationX;
-        applyTranslation();
+    public float getDragTranslationX() {
+        return mDragTranslationX;
     }
 
-    private void applyTranslation() {
-        super.setTranslationX(mTranslationX + mBubbleBarTranslationX);
+    /**
+     * Set offset on x-axis while dragging.
+     * Used to counter parent translation in order to keep the dragged view at the current position
+     * on screen.
+     * Translation applied to the view is a sum of {@code offsetX} and translation defined by
+     * {@link #setDragTranslationX(float)}
+     */
+    public void setOffsetX(float offsetX) {
+        mOffsetX = offsetX;
+        applyDragTranslation();
+    }
+
+    private void applyDragTranslation() {
+        setTranslationX(mDragTranslationX + mOffsetX);
     }
 
     @Override
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 66521c1..d88e272 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
@@ -42,6 +42,8 @@
         const val FLYOUT_DELAY_MS: Long = 2500
         /** The initial scale Y value that the new bubble is set to before the animation starts. */
         const val BUBBLE_ANIMATION_INITIAL_SCALE_Y = 0.3f
+        /** The minimum alpha value to make the bubble bar touchable. */
+        const val MIN_ALPHA_FOR_TOUCHABLE = 0.5f
     }
 
     /** Wrapper around the animating bubble with its show and hide animations. */
@@ -93,15 +95,15 @@
         if (animator.isRunning()) animator.cancel()
         // the animation of a new bubble is divided into 2 parts. The first part shows the bubble
         // and the second part hides it after a delay.
-        val showAnimation = buildShowAnimation()
-        val hideAnimation = buildHideAnimation()
+        val showAnimation = buildHandleToBubbleBarAnimation()
+        val hideAnimation = buildBubbleBarToHandleAnimation()
         animatingBubble = AnimatingBubble(bubbleView, showAnimation, hideAnimation)
         scheduler.post(showAnimation)
         scheduler.postDelayed(FLYOUT_DELAY_MS, hideAnimation)
     }
 
     /**
-     * Returns a [Runnable] that starts the animation that shows the new or updated bubble.
+     * Returns a [Runnable] that starts the animation that morphs the handle to the bubble bar.
      *
      * Visually, the animation is divided into 2 parts. The stash handle starts animating up and
      * fading out and then the bubble bar starts animating up and fading in.
@@ -114,7 +116,7 @@
      * 3. The third part is the overshoot of the spring animation, where we make the bubble fully
      *    visible which helps avoiding further updates when we re-enter the second part.
      */
-    private fun buildShowAnimation() = Runnable {
+    private fun buildHandleToBubbleBarAnimation() = Runnable {
         // prepare the bubble bar for the animation
         bubbleBarView.onAnimatingBubbleStarted()
         bubbleBarView.visibility = VISIBLE
@@ -167,6 +169,9 @@
                         bubbleBarView.scaleY =
                             BUBBLE_ANIMATION_INITIAL_SCALE_Y +
                                 (1 - BUBBLE_ANIMATION_INITIAL_SCALE_Y) * fraction
+                        if (bubbleBarView.alpha > MIN_ALPHA_FOR_TOUCHABLE) {
+                            bubbleStashController.updateTaskbarTouchRegion()
+                        }
                     }
                 }
                 else -> {
@@ -176,6 +181,7 @@
                     bubbleBarView.alpha = 1f
                     bubbleBarView.scaleY = 1f
                     bubbleBarView.translationY = ty - offset
+                    bubbleStashController.updateTaskbarTouchRegion()
                 }
             }
         }
@@ -197,7 +203,8 @@
     }
 
     /**
-     * Returns a [Runnable] that starts the animation that hides the bubble bar.
+     * Returns a [Runnable] that starts the animation that hides the bubble bar and morphs it into
+     * the stashed handle.
      *
      * Similarly to the show animation, this is visually divided into 2 parts. We first animate the
      * bubble bar out, and then animate the stash handle in. At the end of the animation we reset
@@ -209,13 +216,14 @@
      * 2. In the second part the bubble bar is fully hidden and the handle animates in.
      * 3. The third part is the overshoot. The handle is made fully visible.
      */
-    private fun buildHideAnimation() = Runnable {
+    private fun buildBubbleBarToHandleAnimation() = Runnable {
         if (animatingBubble == null) return@Runnable
         val offset = bubbleStashController.diffBetweenHandleAndBarCenters
         val stashedHandleTranslationY =
             bubbleStashController.stashedHandleTranslationForNewBubbleAnimation
         // this is the total distance that both the stashed handle and the bar will be traveling
         val totalTranslationY = bubbleStashController.bubbleBarTranslationYForTaskbar + offset
+        bubbleStashController.setHandleTranslationY(totalTranslationY)
         val animator = bubbleStashController.stashedHandlePhysicsAnimator
         animator.setDefaultSpringConfig(springConfig)
         animator.spring(DynamicAnimation.TRANSLATION_Y, 0f)
@@ -231,6 +239,9 @@
                         (totalTranslationY - ty) / (totalTranslationY - stashedHandleTranslationY)
                     bubbleBarView.alpha = 1 - fraction
                     bubbleBarView.scaleY = 1 - (1 - BUBBLE_ANIMATION_INITIAL_SCALE_Y) * fraction
+                    if (bubbleBarView.alpha > MIN_ALPHA_FOR_TOUCHABLE) {
+                        bubbleStashController.updateTaskbarTouchRegion()
+                    }
                 }
                 ty <= 0 -> {
                     // this is the second part of the animation. make the bubble bar invisible and
@@ -259,8 +270,56 @@
         animator.start()
     }
 
-    /** Handles clicking on the animating bubble while the animation is still playing. */
-    fun onBubbleClickedWhileAnimating() {
+    /** Animates to the initial state of the bubble bar, when there are no previous bubbles. */
+    fun animateToInitialState(b: BubbleBarBubble, isInApp: Boolean, isExpanding: Boolean) {
+        val bubbleView = b.view
+        val animator = PhysicsAnimator.getInstance(bubbleView)
+        if (animator.isRunning()) animator.cancel()
+        // the animation of a new bubble is divided into 2 parts. The first part shows the bubble
+        // and the second part hides it after a delay if we are in an app.
+        val showAnimation = buildBubbleBarBounceAnimation()
+        val hideAnimation =
+            if (isInApp && !isExpanding) {
+                buildBubbleBarToHandleAnimation()
+            } else {
+                // in this case the bubble bar remains visible so not much to do. once we implement
+                // the flyout we'll update this runnable to hide it.
+                Runnable {
+                    animatingBubble = null
+                    bubbleStashController.showBubbleBarImmediate()
+                    bubbleBarView.onAnimatingBubbleCompleted()
+                    bubbleStashController.updateTaskbarTouchRegion()
+                }
+            }
+        animatingBubble = AnimatingBubble(bubbleView, showAnimation, hideAnimation)
+        scheduler.post(showAnimation)
+        scheduler.postDelayed(FLYOUT_DELAY_MS, hideAnimation)
+    }
+
+    private fun buildBubbleBarBounceAnimation() = Runnable {
+        // prepare the bubble bar for the animation
+        bubbleBarView.onAnimatingBubbleStarted()
+        bubbleBarView.translationY = bubbleBarView.height.toFloat()
+        bubbleBarView.visibility = VISIBLE
+        bubbleBarView.alpha = 1f
+        bubbleBarView.scaleX = 1f
+        bubbleBarView.scaleY = 1f
+
+        val animator = PhysicsAnimator.getInstance(bubbleBarView)
+        animator.setDefaultSpringConfig(springConfig)
+        animator.spring(DynamicAnimation.TRANSLATION_Y, bubbleStashController.bubbleBarTranslationY)
+        animator.addUpdateListener { _, _ -> bubbleStashController.updateTaskbarTouchRegion() }
+        animator.addEndListener { _, _, _, _, _, _, _ ->
+            // the bubble bar is now fully settled in. update taskbar touch region so it's touchable
+            bubbleStashController.updateTaskbarTouchRegion()
+        }
+        animator.start()
+    }
+
+    /** Handles touching the animating bubble bar. */
+    fun onBubbleBarTouchedWhileAnimating() {
+        PhysicsAnimator.getInstance(bubbleBarView).cancelIfRunning()
+        bubbleStashController.stashedHandlePhysicsAnimator.cancelIfRunning()
         val hideAnimation = animatingBubble?.hideAnimation ?: return
         scheduler.cancel(hideAnimation)
         bubbleBarView.onAnimatingBubbleCompleted()
@@ -281,4 +340,8 @@
             bubbleBarView.translationY
         )
     }
+
+    private fun <T> PhysicsAnimator<T>.cancelIfRunning() {
+        if (isRunning()) cancel()
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
index 8ad2493..e487f9f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
@@ -24,8 +24,10 @@
 import android.widget.ImageView
 import android.widget.LinearLayout
 import android.widget.Space
+import com.android.launcher3.DeviceProfile
 import com.android.launcher3.R
 import com.android.launcher3.Utilities
+import com.android.launcher3.taskbar.TaskbarActivityContext
 import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter
 
 /**
@@ -73,6 +75,23 @@
         return params
     }
 
+    fun adjustForSetupInPhoneMode(
+        navButtonsLayoutParams: FrameLayout.LayoutParams,
+        navButtonsViewLayoutParams: FrameLayout.LayoutParams,
+        deviceProfile: DeviceProfile
+    ) {
+        val phoneOrPortraitSetupMargin =
+            resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_suw_margin)
+        navButtonsLayoutParams.marginStart = phoneOrPortraitSetupMargin
+        navButtonsLayoutParams.bottomMargin =
+            if (!deviceProfile.isLandscape) 0
+            else
+                phoneOrPortraitSetupMargin -
+                        resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) / 2
+        navButtonsViewLayoutParams.height =
+            resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_suw_height)
+    }
+
     open fun repositionContextualContainer(
         contextualContainer: ViewGroup,
         buttonSize: Int,
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
index 1e9f09b..2497fbb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
@@ -116,6 +116,7 @@
                 isPhoneGestureMode -> {
                     PhoneGestureLayoutter(
                         resources,
+                        navButtonsView,
                         navButtonContainer,
                         endContextualContainer,
                         startContextualContainer,
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
index 8d91f2c..390ec34 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
@@ -17,15 +17,19 @@
 package com.android.launcher3.taskbar.navbutton
 
 import android.content.res.Resources
+import android.view.Gravity
 import android.view.ViewGroup
+import android.widget.FrameLayout
 import android.widget.ImageView
 import android.widget.LinearLayout
 import android.widget.Space
+import com.android.launcher3.DeviceProfile
 import com.android.launcher3.taskbar.TaskbarActivityContext
 
 /** Layoutter for showing gesture navigation on phone screen. No buttons here, no-op container */
 class PhoneGestureLayoutter(
     resources: Resources,
+    navButtonsView: NearestTouchFrame,
     navBarContainer: LinearLayout,
     endContextualContainer: ViewGroup,
     startContextualContainer: ViewGroup,
@@ -42,8 +46,31 @@
         a11yButton,
         space
     ) {
+    private val mNavButtonsView = navButtonsView
 
     override fun layoutButtons(context: TaskbarActivityContext, isA11yButtonPersistent: Boolean) {
+        // TODO: look into if we should use SetupNavLayoutter instead.
+        if (!context.isUserSetupComplete) {
+            // Since setup wizard only has back button enabled, it looks strange to be
+            // end-aligned, so start-align instead.
+            val navButtonsLayoutParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+            val navButtonsViewLayoutParams =
+                mNavButtonsView.layoutParams as FrameLayout.LayoutParams
+            val deviceProfile: DeviceProfile = context.deviceProfile
+
+            navButtonsLayoutParams.marginEnd = 0
+            navButtonsLayoutParams.gravity = Gravity.START
+            context.setTaskbarWindowSize(context.setupWindowSize)
+
+            adjustForSetupInPhoneMode(
+                navButtonsLayoutParams,
+                navButtonsViewLayoutParams,
+                deviceProfile
+            )
+            mNavButtonsView.layoutParams = navButtonsViewLayoutParams
+            navButtonContainer.layoutParams = navButtonsLayoutParams
+        }
+
         endContextualContainer.removeAllViews()
         startContextualContainer.removeAllViews()
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
index 91042c3..22a3630 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
@@ -77,16 +77,11 @@
             navButtonsLayoutParams.height =
                 resources.getDimensionPixelSize(R.dimen.taskbar_back_button_suw_height)
         } else {
-            val phoneOrPortraitSetupMargin =
-                resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_suw_margin)
-            navButtonsLayoutParams.marginStart = phoneOrPortraitSetupMargin
-            navButtonsLayoutParams.bottomMargin =
-                if (!deviceProfile.isLandscape) 0
-                else
-                    phoneOrPortraitSetupMargin -
-                        resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) / 2
-            navButtonsViewLayoutParams.height =
-                resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_suw_height)
+            adjustForSetupInPhoneMode(
+                navButtonsLayoutParams,
+                navButtonsViewLayoutParams,
+                deviceProfile
+            )
         }
         mNavButtonsView.layoutParams = navButtonsViewLayoutParams
         navButtonContainer.layoutParams = navButtonsLayoutParams
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
index 9c3e8af..773b0b9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
@@ -30,7 +30,7 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.app.viewcapture.SettingsAwareViewCapture;
+import com.android.app.viewcapture.ViewCaptureFactory;
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.TestProtocol;
@@ -59,7 +59,7 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         getViewTreeObserver().addOnComputeInternalInsetsListener(this);
-        mViewCaptureCloseable = SettingsAwareViewCapture.getInstance(getContext())
+        mViewCaptureCloseable = ViewCaptureFactory.getInstance(getContext())
                 .startCapture(getRootView(), ".TaskbarOverlay");
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 2eced74..14d391b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -71,7 +71,7 @@
         ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mRecentsView, scaleAndOffset[1]);
         TASK_SECONDARY_TRANSLATION.set(mRecentsView, 0f);
 
-        getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
+        getContentAlphaProperty().set(mRecentsView, state.isRecentsViewVisible ? 1f : 0);
         getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness());
         RECENTS_GRID_PROGRESS.set(mRecentsView,
                 state.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f);
@@ -109,7 +109,8 @@
         setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f,
                 config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
 
-        boolean exitingOverview = !FeatureFlags.enableSplitContextually() && !toState.overviewUi;
+        boolean exitingOverview =
+                !FeatureFlags.enableSplitContextually() && !toState.isRecentsViewVisible;
         if (mRecentsView.isSplitSelectionActive() && exitingOverview) {
             setter.add(mRecentsView.getSplitSelectController().getSplitAnimationController()
                     .createPlaceholderDismissAnim(mLauncher, LAUNCHER_SPLIT_SELECTION_EXIT_HOME,
@@ -124,7 +125,8 @@
             );
         }
 
-        setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
+        setter.setFloat(mRecentsView, getContentAlphaProperty(),
+                toState.isRecentsViewVisible ? 1 : 0,
                 config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
 
         setter.setFloat(
@@ -145,7 +147,7 @@
     private Interpolator getOverviewInterpolator(LauncherState fromState, LauncherState toState) {
         return fromState == QUICK_SWITCH_FROM_HOME
                 ? ACCELERATE_DECELERATE
-                : toState.overviewUi ? INSTANT : FINAL_FRAME;
+                : toState.isRecentsViewVisible ? INSTANT : FINAL_FRAME;
     }
 
     abstract FloatProperty getTaskModalnessProperty();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 5ffa4b5..c33e4cc 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -103,7 +103,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 
-import com.android.app.viewcapture.SettingsAwareViewCapture;
+import com.android.app.viewcapture.ViewCaptureFactory;
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Flags;
@@ -194,9 +194,12 @@
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
 import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig;
 import com.android.systemui.unfold.config.UnfoldTransitionConfig;
+import com.android.systemui.unfold.dagger.UnfoldMain;
 import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver;
 import com.android.systemui.unfold.updates.RotationChangeProvider;
 
+import kotlin.Unit;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -398,6 +401,12 @@
     }
 
     @Override
+    public void startBinding() {
+        super.startBinding();
+        mHotseatPredictionController.verifyUIUpdateNotPaused();
+    }
+
+    @Override
     protected void onActivityFlagsChanged(int changeBits) {
         if ((changeBits & ACTIVITY_STATE_STARTED) != 0) {
             mDepthController.setActivityStarted(isStarted());
@@ -582,6 +591,7 @@
                         } else {
                             getStateManager().moveToRestState();
                         }
+                        return Unit.INSTANCE;
                     });
                 } else {
                     getStateManager().goToState(NORMAL);
@@ -664,7 +674,7 @@
         addMultiWindowModeChangedListener(mDepthController);
         initUnfoldTransitionProgressProvider();
         if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
-            mViewCapture = SettingsAwareViewCapture.getInstance(this).startCapture(getWindow());
+            mViewCapture = ViewCaptureFactory.getInstance(this).startCapture(getWindow());
         }
         getWindow().addPrivateFlags(PRIVATE_FLAG_OPTIMIZE_MEASURE);
         QuickstepOnboardingPrefs.setup(this);
@@ -1040,6 +1050,7 @@
                         getMainExecutor(),
                         getMainThreadHandler(),
                         /* backgroundExecutor= */ UI_HELPER_EXECUTOR,
+                        /* bgHandler= */ UI_HELPER_EXECUTOR.getHandler(),
                         /* tracingTagPrefix= */ "launcher",
                         getSystemService(DisplayManager.class)
                 );
@@ -1059,7 +1070,7 @@
     }
 
     private void initUnfoldAnimationController(UnfoldTransitionProgressProvider progressProvider,
-            RotationChangeProvider rotationChangeProvider) {
+            @UnfoldMain RotationChangeProvider rotationChangeProvider) {
         mLauncherUnfoldAnimationController = new LauncherUnfoldAnimationController(
                 /* launcher= */ this,
                 getWindowManager(),
@@ -1473,4 +1484,9 @@
         }
         return super.onCreateView(parent, name, context, attrs);
     }
+
+    @Override
+    public boolean isRecentsViewVisible() {
+        return getStateManager().getState().isRecentsViewVisible;
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 6c1d4b1..235ec7b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -21,7 +21,6 @@
 import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS;
 import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
-import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
 import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
 import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
 import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
@@ -41,6 +40,7 @@
 
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatedFloat;
 import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.anim.PropertySetter;
@@ -67,7 +67,7 @@
     @Override
     public void setState(@NonNull LauncherState state) {
         super.setState(state);
-        if (state.overviewUi) {
+        if (state.isRecentsViewVisible) {
             mRecentsView.updateEmptyMessage();
         } else {
             mRecentsView.resetTaskVisuals();
@@ -76,7 +76,7 @@
         mRecentsView.setFullscreenProgress(state.getOverviewFullscreenProgress());
         // In Overview, we may be layering app surfaces behind Launcher, so we need to notify
         // DepthController to prevent optimizations which might occlude the layers behind
-        mLauncher.getDepthController().setHasContentBehindLauncher(state.overviewUi);
+        mLauncher.getDepthController().setHasContentBehindLauncher(state.isRecentsViewVisible);
 
         PendingAnimation builder =
                 new PendingAnimation(state.getTransitionDuration(mLauncher, true));
@@ -89,7 +89,7 @@
             @NonNull StateAnimationConfig config, @NonNull PendingAnimation builder) {
         super.setStateWithAnimationInternal(toState, config, builder);
 
-        if (toState.overviewUi) {
+        if (toState.isRecentsViewVisible) {
             // While animating into recents, update the visible task data as needed
             builder.addOnFrameCallback(() -> mRecentsView.loadVisibleTaskData(FLAG_UPDATE_ALL));
             mRecentsView.updateEmptyMessage();
@@ -107,7 +107,8 @@
         // In Overview, we may be layering app surfaces behind Launcher, so we need to notify
         // DepthController to prevent optimizations which might occlude the layers behind
         builder.addListener(AnimatorListeners.forSuccessCallback(() ->
-                mLauncher.getDepthController().setHasContentBehindLauncher(toState.overviewUi)));
+                mLauncher.getDepthController().setHasContentBehindLauncher(
+                        toState.isRecentsViewVisible)));
 
         handleSplitSelectionState(toState, builder, /* animate */true);
 
@@ -168,7 +169,7 @@
                 clearAllButtonAlpha, LINEAR);
         float overviewButtonAlpha = state.areElementsVisible(mLauncher, OVERVIEW_ACTIONS) ? 1 : 0;
         propertySetter.setFloat(mLauncher.getActionsView().getVisibilityAlpha(),
-                MULTI_PROPERTY_VALUE, overviewButtonAlpha, config.getInterpolator(
+                AnimatedFloat.VALUE, overviewButtonAlpha, config.getInterpolator(
                         ANIM_OVERVIEW_ACTIONS_FADE, LINEAR));
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt b/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
index 535b4c2..146ff3d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
@@ -40,7 +40,7 @@
 import com.android.quickstep.util.FadeOutRemoteTransition
 
 /** A wrapper for the hidden API calls */
-class SystemApiWrapper(context: Context?) : ApiWrapper(context) {
+open class SystemApiWrapper(context: Context?) : ApiWrapper(context) {
 
     override fun getPersons(si: ShortcutInfo) = si.persons ?: Utilities.EMPTY_PERSON_ARRAY
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt b/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt
index 3881e9a..dc6365b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt
@@ -16,14 +16,28 @@
 
 package com.android.launcher3.uioverrides.flags
 
+import android.app.PendingIntent
+import android.app.blob.BlobHandle.createWithSha256
+import android.app.blob.BlobStoreManager
 import android.content.Context
+import android.content.IIntentReceiver
+import android.content.IIntentSender.Stub
 import android.content.Intent
+import android.content.Intent.ACTION_CREATE_DOCUMENT
+import android.content.Intent.ACTION_OPEN_DOCUMENT
 import android.content.pm.PackageManager
 import android.net.Uri
+import android.os.Bundle
+import android.os.IBinder
+import android.os.ParcelFileDescriptor.AutoCloseOutputStream
 import android.provider.DeviceConfig
 import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
+import android.provider.Settings.Secure
 import android.text.Html
 import android.util.AttributeSet
+import android.util.Base64
+import android.util.Base64.NO_PADDING
+import android.util.Base64.NO_WRAP
 import android.view.inputmethod.EditorInfo
 import android.widget.TextView
 import android.widget.Toast
@@ -33,11 +47,32 @@
 import androidx.preference.PreferenceGroup
 import androidx.preference.PreferenceViewHolder
 import androidx.preference.SwitchPreference
+import com.android.launcher3.AutoInstallsLayout
 import com.android.launcher3.ExtendedEditText
+import com.android.launcher3.LauncherAppState
 import com.android.launcher3.LauncherPrefs
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER
+import com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_KEY
+import com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_LABEL
+import com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_TAG
 import com.android.launcher3.R
+import com.android.launcher3.model.data.FolderInfo
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.model.data.LauncherAppWidgetInfo
+import com.android.launcher3.pm.UserCache
+import com.android.launcher3.proxy.ProxyActivityStarter
 import com.android.launcher3.secondarydisplay.SecondaryDisplayLauncher
+import com.android.launcher3.shortcuts.ShortcutKey
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapperImpl
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import com.android.launcher3.util.Executors.MODEL_EXECUTOR
+import com.android.launcher3.util.Executors.ORDERED_BG_EXECUTOR
+import com.android.launcher3.util.LauncherLayoutBuilder
 import com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT
 import com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_COUNT
 import com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_SEEN
@@ -45,12 +80,17 @@
 import com.android.launcher3.util.OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN
 import com.android.launcher3.util.OnboardingPrefs.TASKBAR_EDU_TOOLTIP_STEP
 import com.android.launcher3.util.PluginManagerWrapper
+import com.android.launcher3.util.StartActivityParams
+import com.android.launcher3.util.UserIconInfo
 import com.android.quickstep.util.DeviceConfigHelper
 import com.android.quickstep.util.DeviceConfigHelper.Companion.NAMESPACE_LAUNCHER
 import com.android.quickstep.util.DeviceConfigHelper.DebugInfo
 import com.android.systemui.shared.plugins.PluginEnabler
 import com.android.systemui.shared.plugins.PluginPrefs
+import java.io.OutputStreamWriter
+import java.security.MessageDigest
 import java.util.Locale
+import java.util.concurrent.Executor
 
 /** Helper class to generate UI for Device Config */
 class DevOptionsUiHelper(c: Context, attr: AttributeSet?) : PreferenceGroup(c, attr) {
@@ -67,6 +107,9 @@
         (holder.findViewById(R.id.filter_box) as TextView?)?.doAfterTextChanged {
             val query: String = it.toString().lowercase(Locale.getDefault()).replace("_", " ")
             filterPreferences(query, this)
+
+            // Always keep myself visible
+            this@DevOptionsUiHelper.isVisible = true
         }
     }
 
@@ -97,6 +140,7 @@
         }
         addIntentTargets()
         addOnboardingPrefsCategory()
+        addLayoutSharePref()
     }
 
     private fun newCategory(titleText: String, subTitleText: String? = null) =
@@ -359,7 +403,7 @@
             Preference(context).also {
                 it.title = title
                 it.summary = "Tap to reset"
-                setOnPreferenceClickListener { _ ->
+                it.setOnPreferenceClickListener { _ ->
                     LauncherPrefs.getPrefs(context)
                         .edit()
                         .apply { keys.forEach { key -> remove(key) } }
@@ -370,6 +414,137 @@
             }
         )
 
+    private fun addLayoutSharePref() {
+        val model = LauncherAppState.getInstance(context).model
+        val category = newCategory("Workspace grid layout")
+        Preference(context).apply {
+            title = "Export"
+            intent =
+                createUriPickerIntent(ACTION_CREATE_DOCUMENT, MAIN_EXECUTOR) { uri ->
+                    model.enqueueModelUpdateTask { _, dataModel, _ ->
+                        val builder = LauncherLayoutBuilder()
+                        dataModel.workspaceItems.forEach { info ->
+                            val loc =
+                                when (info.container) {
+                                    CONTAINER_DESKTOP ->
+                                        builder.atWorkspace(info.cellX, info.cellY, info.screenId)
+                                    CONTAINER_HOTSEAT -> builder.atHotseat(info.screenId)
+                                    else -> return@forEach
+                                }
+                            loc.addItem(info)
+                        }
+                        dataModel.appWidgets.forEach { info ->
+                            builder.atWorkspace(info.cellX, info.cellY, info.screenId).addItem(info)
+                        }
+
+                        context.contentResolver.openOutputStream(uri).use { os ->
+                            builder.build(OutputStreamWriter(os))
+                        }
+
+                        MAIN_EXECUTOR.execute {
+                            Toast.makeText(context, "File saved", Toast.LENGTH_LONG).show()
+                        }
+                    }
+                }
+            category.addPreference(this)
+        }
+
+        Preference(context).apply {
+            title = "Import"
+            intent =
+                createUriPickerIntent(ACTION_OPEN_DOCUMENT, ORDERED_BG_EXECUTOR) { uri ->
+                    val resolver = context.contentResolver
+                    val data =
+                        resolver.openInputStream(uri).use { stream ->
+                            stream?.readAllBytes() ?: return@createUriPickerIntent
+                        }
+
+                    val digest = MessageDigest.getInstance("SHA-256").digest(data)
+                    val handle = createWithSha256(digest, LAYOUT_DIGEST_LABEL, 0, LAYOUT_DIGEST_TAG)
+                    val blobManager = context.getSystemService(BlobStoreManager::class.java)!!
+
+                    blobManager.openSession(blobManager.createSession(handle)).use { session ->
+                        AutoCloseOutputStream(session.openWrite(0, -1)).use { it.write(data) }
+                        session.allowPublicAccess()
+
+                        session.commit(ORDERED_BG_EXECUTOR) {
+                            val key = Base64.encodeToString(digest, NO_WRAP or NO_PADDING)
+                            Secure.putString(resolver, LAYOUT_DIGEST_KEY, key)
+
+                            MODEL_EXECUTOR.submit { model.modelDbController.createEmptyDB() }.get()
+                            MAIN_EXECUTOR.submit { model.forceReload() }.get()
+                            MODEL_EXECUTOR.submit {}.get()
+                            Secure.putString(resolver, LAYOUT_DIGEST_KEY, null)
+                        }
+                    }
+                }
+            category.addPreference(this)
+        }
+    }
+
+    private fun LauncherLayoutBuilder.ItemTarget.addItem(info: ItemInfo) {
+        val userType: String? =
+            when (UserCache.INSTANCE.get(context).getUserInfo(info.user).type) {
+                UserIconInfo.TYPE_WORK -> AutoInstallsLayout.USER_TYPE_WORK
+                UserIconInfo.TYPE_CLONED -> AutoInstallsLayout.USER_TYPE_CLONED
+                else -> null
+            }
+        when (info.itemType) {
+            ITEM_TYPE_APPLICATION ->
+                info.targetComponent?.let { c -> putApp(c.packageName, c.className, userType) }
+            ITEM_TYPE_DEEP_SHORTCUT ->
+                ShortcutKey.fromItemInfo(info).let { key ->
+                    putShortcut(key.packageName, key.id, userType)
+                }
+            ITEM_TYPE_FOLDER ->
+                (info as FolderInfo).let { folderInfo ->
+                    putFolder(folderInfo.title?.toString() ?: "").also { folderBuilder ->
+                        folderInfo.getContents().forEach { folderContent ->
+                            folderBuilder.addItem(folderContent)
+                        }
+                    }
+                }
+            ITEM_TYPE_APPWIDGET ->
+                putWidget(
+                    (info as LauncherAppWidgetInfo).providerName.packageName,
+                    info.providerName.className,
+                    info.spanX,
+                    info.spanY,
+                    userType
+                )
+        }
+    }
+
+    private fun createUriPickerIntent(
+        action: String,
+        executor: Executor,
+        callback: (uri: Uri) -> Unit
+    ): Intent {
+        val pendingIntent =
+            PendingIntent(
+                object : Stub() {
+                    override fun send(
+                        code: Int,
+                        intent: Intent,
+                        resolvedType: String?,
+                        allowlistToken: IBinder?,
+                        finishedReceiver: IIntentReceiver?,
+                        requiredPermission: String?,
+                        options: Bundle?
+                    ) {
+                        intent.data?.let { uri -> executor.execute { callback(uri) } }
+                    }
+                }
+            )
+        val params = StartActivityParams(pendingIntent, 0)
+        params.intent =
+            Intent(action)
+                .addCategory(Intent.CATEGORY_OPENABLE)
+                .setType("text/xml")
+                .putExtra(Intent.EXTRA_TITLE, "launcher_grid.xml")
+        return ProxyActivityStarter.getLaunchIntent(context, params)
+    }
+
     private inner class CustomSwitchPref(
         private val bindCallback: (holder: PreferenceViewHolder, pref: SwitchPreference) -> Unit
     ) : SwitchPreference(context) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 7fa121d..2625646 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -34,7 +34,7 @@
  */
 public class BackgroundAppState extends OverviewState {
 
-    private static final int STATE_FLAGS = FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI
+    private static final int STATE_FLAGS = FLAG_DISABLE_RESTORE | FLAG_RECENTS_VIEW_VISIBLE
             | FLAG_WORKSPACE_INACCESSIBLE | FLAG_NON_INTERACTIVE | FLAG_CLOSE_POPUPS;
 
     public BackgroundAppState(int id) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index 3c291e6..932d241 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -32,7 +32,7 @@
 public class OverviewModalTaskState extends OverviewState {
 
     private static final int STATE_FLAGS =
-            FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_WORKSPACE_INACCESSIBLE;
+            FLAG_DISABLE_RESTORE | FLAG_RECENTS_VIEW_VISIBLE | FLAG_WORKSPACE_INACCESSIBLE;
 
     public OverviewModalTaskState(int id) {
         super(id, LAUNCHER_STATE_OVERVIEW, STATE_FLAGS);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index d0eef8e..7173298 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -47,7 +47,7 @@
     protected static final Rect sTempRect = new Rect();
 
     private static final int STATE_FLAGS = FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED
-            | FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_WORKSPACE_INACCESSIBLE
+            | FLAG_DISABLE_RESTORE | FLAG_RECENTS_VIEW_VISIBLE | FLAG_WORKSPACE_INACCESSIBLE
             | FLAG_CLOSE_POPUPS;
 
     public OverviewState(int id) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index 3ed2d0b..11e0ed5 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -118,7 +118,7 @@
         if (!cameFromNavBar) {
             return false;
         }
-        if (mStartState.overviewUi || mStartState == ALL_APPS) {
+        if (mStartState.isRecentsViewVisible || mStartState == ALL_APPS) {
             return true;
         }
         int typeToClose = TYPE_ALL & ~TYPE_ALL_APPS_EDU;
@@ -145,7 +145,7 @@
     private void initCurrentAnimation() {
         long accuracy = (long) (getShiftRange() * 2);
         final PendingAnimation builder = new PendingAnimation(accuracy);
-        if (mStartState.overviewUi) {
+        if (mStartState.isRecentsViewVisible) {
             RecentsView recentsView = mLauncher.getOverviewPanel();
             AnimatorControllerWithResistance.createRecentsResistanceFromOverviewAnim(mLauncher,
                     builder);
@@ -194,7 +194,7 @@
             RecentsView recentsView = mLauncher.getOverviewPanel();
             recentsView.switchToScreenshot(null,
                     () -> recentsView.finishRecentsAnimation(true /* toRecents */, null));
-            if (mStartState.overviewUi) {
+            if (mStartState.isRecentsViewVisible) {
                 Runnable onReachedHome = () -> {
                     StateManager.StateListener<LauncherState> listener =
                             new StateManager.StateListener<>() {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 42be52f..3325009 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -249,7 +249,7 @@
     }
 
     private boolean handlingOverviewAnim() {
-        int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
+        long stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
         return mDidTouchStartInNavBar && mStartState == NORMAL
                 && (stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0;
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 527a776..ab277b6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -172,7 +172,7 @@
         if ((ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) == 0) {
             return false;
         }
-        int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
+        long stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
         if ((stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0) {
             return false;
         }
@@ -248,7 +248,7 @@
         TASK_THUMBNAIL_SPLASH_ALPHA.set(mRecentsView, fromState.showTaskThumbnailSplash() ? 1f : 0);
         mRecentsView.setContentAlpha(1);
         mRecentsView.setFullscreenProgress(fromState.getOverviewFullscreenProgress());
-        mLauncher.getActionsView().getVisibilityAlpha().setValue(
+        mLauncher.getActionsView().getVisibilityAlpha().updateValue(
                 (fromState.getVisibleElements(mLauncher) & OVERVIEW_ACTIONS) != 0 ? 1f : 0f);
         mRecentsView.setTaskIconScaledDown(true);
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index 05a55d0..93e4fbd 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -84,7 +84,7 @@
 
     @Override
     protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
-        int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
+        long stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
         if ((stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0) {
             return NORMAL;
         }
@@ -151,7 +151,7 @@
             int sysuiFlags = 0;
             TaskView tv = mOverviewPanel.getTaskViewAt(0);
             if (tv != null) {
-                sysuiFlags = tv.getThumbnail().getSysUiStatusNavFlags();
+                sysuiFlags = tv.getFirstThumbnailViewDeprecated().getSysUiStatusNavFlags();
             }
             mLauncher.getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, sysuiFlags);
         } else {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 300d697..4bc3c16 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -252,7 +252,7 @@
                     mTaskBeingDragged, maxDuration, currentInterpolator);
 
             // Since the thumbnail is what is filling the screen, based the end displacement on it.
-            View thumbnailView = mTaskBeingDragged.getThumbnail();
+            View thumbnailView = mTaskBeingDragged.getFirstThumbnailViewDeprecated();
             mTempCords[1] = orientationHandler.getSecondaryDimension(thumbnailView);
             dl.getDescendantCoordRelativeToSelf(thumbnailView, mTempCords);
             mEndDisplacement = secondaryLayerDimension - mTempCords[1];
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index d6e4dbd..463222d 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -107,7 +107,6 @@
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.logging.StatsLogManager.StatsLogger;
-import com.android.launcher3.statehandlers.DesktopVisibilityController;
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.taskbar.TaskbarThresholdUtils;
 import com.android.launcher3.taskbar.TaskbarUIController;
@@ -139,7 +138,7 @@
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.RecentsViewContainer;
 import com.android.quickstep.views.TaskView;
-import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
+import com.android.quickstep.views.TaskView.TaskContainer;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -148,9 +147,12 @@
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
+import com.android.window.flags.Flags;
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.startingsurface.SplashScreenExitAnimationUtils;
 
+import kotlin.Unit;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -923,7 +925,7 @@
             TaskView runningTask = mRecentsView.getRunningTaskView();
             TaskView centermostTask = mRecentsView.getTaskViewNearestToCenterOfScreen();
             int centermostTaskFlags = centermostTask == null ? 0
-                    : centermostTask.getThumbnail().getSysUiStatusNavFlags();
+                    : centermostTask.getFirstThumbnailViewDeprecated().getSysUiStatusNavFlags();
             boolean swipeUpThresholdPassed = windowProgress > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD;
             boolean quickswitchThresholdPassed = centermostTask != runningTask;
 
@@ -1172,12 +1174,6 @@
                 mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
                 // Notify the SysUI to use fade-in animation when entering PiP
                 SystemUiProxy.INSTANCE.get(mContext).setPipAnimationTypeToAlpha();
-                DesktopVisibilityController desktopVisibilityController =
-                        mContainerInterface.getDesktopVisibilityController();
-                if (desktopVisibilityController != null) {
-                    // Notify the SysUI to stash desktop apps if they are visible
-                    desktopVisibilityController.onHomeActionTriggered();
-                }
                 break;
             case RECENTS:
                 mStateCallback.setState(STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
@@ -1259,13 +1255,16 @@
                 ? mRecentsView.getNextPageTaskView() : null;
         TaskView currentPageTaskView = mRecentsView != null
                 ? mRecentsView.getCurrentPageTaskView() : null;
-        if ((nextPageTaskView instanceof DesktopTaskView
-                || currentPageTaskView instanceof DesktopTaskView)
-                && endTarget == NEW_TASK) {
-            // TODO(b/268075592): add support for quickswitch to/from desktop
-            return LAST_TASK;
-        }
 
+        if (Flags.enableDesktopWindowingMode()
+                && !(Flags.enableDesktopWindowingWallpaperActivity()
+                && Flags.enableDesktopWindowingQuickSwitch())) {
+            if ((nextPageTaskView instanceof DesktopTaskView
+                    || currentPageTaskView instanceof DesktopTaskView)
+                    && endTarget == NEW_TASK) {
+                return LAST_TASK;
+            }
+        }
         return endTarget;
     }
 
@@ -1422,14 +1421,27 @@
             mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED);
             setClampScrollOffset(false);
         };
-        if (mRecentsView != null && (mRecentsView.getCurrentPageTaskView() != null
-                && !(mRecentsView.getCurrentPageTaskView() instanceof DesktopTaskView))) {
-            ActiveGestureLog.INSTANCE.trackEvent(ActiveGestureErrorDetector.GestureEvent
-                    .SET_ON_PAGE_TRANSITION_END_CALLBACK);
-            // TODO(b/268075592): add support for quickswitch to/from desktop
-            mRecentsView.setOnPageTransitionEndCallback(onPageTransitionEnd);
+
+        if (Flags.enableDesktopWindowingMode()
+                && !(Flags.enableDesktopWindowingWallpaperActivity()
+                && Flags.enableDesktopWindowingQuickSwitch())) {
+            if (mRecentsView != null && (mRecentsView.getCurrentPageTaskView() != null
+                    && !(mRecentsView.getCurrentPageTaskView() instanceof DesktopTaskView))) {
+                ActiveGestureLog.INSTANCE.trackEvent(ActiveGestureErrorDetector.GestureEvent
+                        .SET_ON_PAGE_TRANSITION_END_CALLBACK);
+                mRecentsView.setOnPageTransitionEndCallback(onPageTransitionEnd);
+            } else {
+                onPageTransitionEnd.run();
+            }
         } else {
-            onPageTransitionEnd.run();
+            if (mRecentsView != null) {
+                ActiveGestureLog.INSTANCE.trackEvent(
+                        ActiveGestureErrorDetector
+                                .GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK);
+                mRecentsView.setOnPageTransitionEndCallback(onPageTransitionEnd);
+            } else {
+                onPageTransitionEnd.run();
+            }
         }
 
         animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs);
@@ -1457,14 +1469,15 @@
             default:
                 event = IGNORE;
         }
-        StatsLogger logger = StatsLogManager.newInstance(mContext).logger()
+        StatsLogger logger = StatsLogManager.newInstance(
+                        mContainer != null ? mContainer.asContext() : mContext).logger()
                 .withSrcState(LAUNCHER_STATE_BACKGROUND)
                 .withDstState(endTarget.containerType)
                 .withInputType(mGestureState.isTrackpadGesture()
                         ? SysUiStatsLog.LAUNCHER_UICHANGED__INPUT_TYPE__TRACKPAD
                         : SysUiStatsLog.LAUNCHER_UICHANGED__INPUT_TYPE__TOUCH);
         if (targetTask != null) {
-            logger.withItemInfo(targetTask.getItemInfo());
+            logger.withItemInfo(targetTask.getFirstItemInfo());
         }
 
         int pageIndex = endTarget == LAST_TASK || mRecentsView == null
@@ -2251,11 +2264,14 @@
                     mRecentsAnimationController, mRecentsAnimationTargets);
         });
 
-        if (mRecentsView.getNextPageTaskView() instanceof DesktopTaskView
-                || mRecentsView.getCurrentPageTaskView() instanceof DesktopTaskView) {
-            // TODO(b/268075592): add support for quickswitch to/from desktop
-            mRecentsViewScrollLinked = false;
-            return;
+        if (Flags.enableDesktopWindowingMode()
+                && !(Flags.enableDesktopWindowingWallpaperActivity()
+                        && Flags.enableDesktopWindowingQuickSwitch())) {
+            if (mRecentsView.getNextPageTaskView() instanceof DesktopTaskView
+                    || mRecentsView.getCurrentPageTaskView() instanceof DesktopTaskView) {
+                mRecentsViewScrollLinked = false;
+                return;
+            }
         }
 
         // Disable scrolling in RecentsView for trackpad 3-finger swipe up gesture.
@@ -2286,15 +2302,15 @@
                 int[] taskIds = nextTask.getTaskIds();
                 ActiveGestureLog.CompoundString nextTaskLog = new ActiveGestureLog.CompoundString(
                         "Launching task: ");
-                for (TaskIdAttributeContainer c : nextTask.getTaskIdAttributeContainers()) {
-                    if (c == null) {
+                for (TaskContainer container : nextTask.getTaskContainers()) {
+                    if (container == null) {
                         continue;
                     }
                     nextTaskLog
                             .append("[id: ")
-                            .append(c.getTask().key.id)
+                            .append(container.getTask().key.id)
                             .append(", pkg: ")
-                            .append(c.getTask().key.getPackageName())
+                            .append(container.getTask().key.getPackageName())
                             .append("] | ");
                 }
                 mGestureState.updateLastStartedTaskIds(taskIds);
@@ -2317,6 +2333,7 @@
                             mRecentsAnimationController.finish(true /* toRecents */, null);
                         }
                     }
+                    return Unit.INSTANCE;
                 }, true /* freezeTaskList */);
             } else {
                 mContainerInterface.onLaunchTaskFailed();
@@ -2406,7 +2423,8 @@
         RemoteAnimationTarget taskTarget = taskTargetOptional.get();
         TaskView taskView = mRecentsView == null
                 ? null : mRecentsView.getTaskViewByTaskId(taskTarget.taskId);
-        if (taskView == null || !taskView.getThumbnail().shouldShowSplashView()) {
+        if (taskView == null
+                || !taskView.getFirstThumbnailViewDeprecated().shouldShowSplashView()) {
             ActiveGestureLog.INSTANCE.addLog("Invalid task view splash state");
             finishRecentsAnimationOnTasksAppeared(null /* onFinishComplete */);
             return;
diff --git a/quickstep/src/com/android/quickstep/DesktopModeStatus.java b/quickstep/src/com/android/quickstep/DesktopModeStatus.java
deleted file mode 100644
index b1aae16..0000000
--- a/quickstep/src/com/android/quickstep/DesktopModeStatus.java
+++ /dev/null
@@ -1,58 +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;
-
-import android.content.Context;
-import android.os.SystemProperties;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.Flags;
-
-// TODO(b/335401172): Explore unifying logic across core and shell
-public class DesktopModeStatus {
-
-    /**
-     * Flag to indicate whether to restrict desktop mode to supported devices.
-     */
-    private static final boolean ENFORCE_DEVICE_RESTRICTIONS = SystemProperties.getBoolean(
-            "persist.wm.debug.desktop_mode_enforce_device_restrictions", true);
-
-    /**
-     * Return {@code true} if desktop mode should be restricted to supported devices.
-     */
-    @VisibleForTesting
-    public static boolean enforceDeviceRestrictions() {
-        return ENFORCE_DEVICE_RESTRICTIONS;
-    }
-
-    /**
-     * Return {@code true} if the current device supports desktop mode.
-     */
-    @VisibleForTesting
-    public static boolean isDesktopModeSupported(Context context) {
-        return context.getResources().getBoolean(
-                com.android.internal.R.bool.config_isDesktopModeSupported);
-    }
-
-    /**
-     * Return {@code true} if desktop mode can be entered on the current device.
-     */
-    public static boolean canEnterDesktopMode(Context context) {
-        return Flags.enableDesktopWindowingMode()
-                && (!enforceDeviceRestrictions() || isDesktopModeSupported(context));
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
index aab6aa1..50a06fc 100644
--- a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
+++ b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
@@ -23,29 +23,30 @@
 import com.android.launcher3.popup.SystemShortcut
 import com.android.quickstep.views.RecentsView
 import com.android.quickstep.views.RecentsViewContainer
-import com.android.quickstep.views.TaskView.TaskIdAttributeContainer
+import com.android.quickstep.views.TaskView.TaskContainer
+import com.android.wm.shell.shared.DesktopModeStatus
 
 /** A menu item, "Desktop", that allows the user to bring the current app into Desktop Windowing. */
 class DesktopSystemShortcut(
     container: RecentsViewContainer,
-    private val mTaskContainer: TaskIdAttributeContainer,
+    private val taskContainer: TaskContainer,
     abstractFloatingViewHelper: AbstractFloatingViewHelper
 ) :
     SystemShortcut<RecentsViewContainer>(
         R.drawable.ic_caption_desktop_button_foreground,
         R.string.recent_task_option_desktop,
         container,
-        mTaskContainer.itemInfo,
-        mTaskContainer.taskView,
+        taskContainer.itemInfo,
+        taskContainer.taskView,
         abstractFloatingViewHelper
     ) {
     override fun onClick(view: View) {
         dismissTaskMenuView()
-        val recentsView = mTarget!!.getOverviewPanel<RecentsView<*, *>>()
-        recentsView.moveTaskToDesktop(mTaskContainer) {
+        val recentsView = mTarget.getOverviewPanel<RecentsView<*, *>>()
+        recentsView.moveTaskToDesktop(taskContainer) {
             mTarget.statsLogManager
                 .logger()
-                .withItemInfo(mTaskContainer.itemInfo)
+                .withItemInfo(taskContainer.itemInfo)
                 .log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_DESKTOP_TAP)
         }
     }
@@ -59,7 +60,7 @@
             return object : TaskShortcutFactory {
                 override fun getShortcuts(
                     container: RecentsViewContainer,
-                    taskContainer: TaskIdAttributeContainer
+                    taskContainer: TaskContainer
                 ): List<DesktopSystemShortcut>? {
                     return if (!DesktopModeStatus.canEnterDesktopMode(container.asContext())) null
                     else if (!taskContainer.task.isDockable) null
diff --git a/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt b/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt
index f68f793..3549a12 100644
--- a/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt
+++ b/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt
@@ -37,6 +37,13 @@
             "Server side control to customize LPH timeout and touch slop"
         )
 
+    val customLpaaThresholds =
+        propReader.get(
+            "CUSTOM_LPAA_THRESHOLDS",
+            false,
+            "Server side control to customize LPAA timeout and touch slop"
+        )
+
     val overrideLpnhLphThresholds =
         propReader.get(
             "OVERRIDE_LPNH_LPH_THRESHOLDS",
@@ -154,7 +161,7 @@
     }
 
     companion object {
-        val configHelper by lazy { DeviceConfigHelper(::DeviceConfigWrapper) }
+        @JvmStatic val configHelper by lazy { DeviceConfigHelper(::DeviceConfigWrapper) }
 
         @JvmStatic fun get() = configHelper.config
     }
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 7655c59..811b9fd 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -192,7 +192,7 @@
     public RecentsView getVisibleRecentsView() {
         QuickstepLauncher launcher = getVisibleLauncher();
         RecentsView recentsView =
-                launcher != null && launcher.getStateManager().getState().overviewUi
+                launcher != null && launcher.getStateManager().getState().isRecentsViewVisible
                         ? launcher.getOverviewPanel() : null;
         if (recentsView == null || (!launcher.hasBeenResumed()
                 && recentsView.getRunningTaskViewId() == -1)) {
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 4b5a15d..080e03a 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -304,9 +304,7 @@
             // Disable if swiping to PIP
             return null;
         }
-        if (sourceTaskView == null
-                || sourceTaskView.getTask() == null
-                || sourceTaskView.getTask().key.getComponent() == null) {
+        if (sourceTaskView == null || sourceTaskView.getFirstTask().key.getComponent() == null) {
             // Disable if it's an invalid task
             return null;
         }
@@ -323,8 +321,8 @@
         }
 
         return mContainer.getFirstMatchForAppClose(launchCookieItemId,
-                sourceTaskView.getTask().key.getComponent().getPackageName(),
-                UserHandle.of(sourceTaskView.getTask().key.userId),
+                sourceTaskView.getFirstTask().key.getComponent().getPackageName(),
+                UserHandle.of(sourceTaskView.getFirstTask().key.userId),
                 false /* supportsAllAppsState */);
     }
 
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 68923ee..3091e3d 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -16,6 +16,9 @@
 package com.android.quickstep;
 
 import static com.android.launcher3.PagedView.INVALID_PAGE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_3_BUTTON;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_QUICK_SWITCH;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
 
@@ -34,6 +37,8 @@
 import com.android.internal.jank.Cuj;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.taskbar.TaskbarUIController;
 import com.android.launcher3.util.RunnableList;
@@ -171,7 +176,7 @@
         RunnableList callbackList = null;
         if (taskView != null) {
             mWaitForToggleCommandComplete = true;
-            taskView.setEndQuickswitchCuj(true);
+            taskView.setEndQuickSwitchCuj(true);
             callbackList = taskView.launchTasks();
         }
 
@@ -285,6 +290,7 @@
             public void onAnimationStart(Animator animation) {
                 super.onAnimationStart(animation);
                 updateRecentsViewFocus(cmd);
+                logShowOverviewFrom(cmd.type);
             }
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -319,6 +325,7 @@
             public void onRecentsAnimationStart(RecentsAnimationController controller,
                     RecentsAnimationTargets targets) {
                 updateRecentsViewFocus(cmd);
+                logShowOverviewFrom(cmd.type);
                 activityInterface.runOnInitBackgroundStateUI(() ->
                         interactionHandler.onGestureEnded(0, new PointF()));
                 cmd.removeListener(this);
@@ -420,6 +427,33 @@
         return true;
     }
 
+    private <T extends StatefulActivity<?> & RecentsViewContainer>
+            void logShowOverviewFrom(int cmdType) {
+        BaseActivityInterface<?, T> activityInterface =
+                mOverviewComponentObserver.getActivityInterface();
+        var container = activityInterface.getCreatedContainer();
+        if (container != null) {
+            StatsLogManager.LauncherEvent event;
+            switch (cmdType) {
+                case TYPE_SHOW -> event = LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT;
+                case TYPE_HIDE ->
+                        event = LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_QUICK_SWITCH;
+                case TYPE_TOGGLE -> event = LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_3_BUTTON;
+                default -> {
+                    return;
+                }
+            }
+
+            StatsLogManager.newInstance(container.asContext())
+                    .logger()
+                    .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
+                            .setTaskSwitcherContainer(
+                                    LauncherAtom.TaskSwitcherContainer.getDefaultInstance())
+                            .build())
+                    .log(event);
+        }
+    }
+
     public void dump(PrintWriter pw) {
         pw.println("OverviewCommandHelper:");
         pw.println("  mPendingCommands=" + mPendingCommands.size());
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index f57f4c8..97a0b3f 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -33,7 +33,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.app.ActivityOptions;
-import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.os.Bundle;
@@ -507,4 +506,9 @@
     public TISBindHelper getTISBindHelper() {
         return mTISBindHelper;
     }
+
+    @Override
+    public boolean isRecentsViewVisible() {
+        return getStateManager().getState().isRecentsViewVisible();
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 4b4f914..34b50ca 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -33,6 +33,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DIALOG_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
@@ -90,7 +91,7 @@
     static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
 
     // TODO: Move to quickstep contract
-    private static final float QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON = 3f;
+    public static final float QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON = 3f;
     private static final float QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL = 1.414f;
 
     private final Context mContext;
@@ -107,7 +108,7 @@
 
     private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
 
-    private @SystemUiStateFlags int mSystemUiStateFlags = QuickStepContract.SYSUI_STATE_AWAKE;
+    private @SystemUiStateFlags long mSystemUiStateFlags = QuickStepContract.SYSUI_STATE_AWAKE;
     private NavigationMode mMode = THREE_BUTTONS;
     private NavBarPosition mNavBarPosition;
 
@@ -351,7 +352,7 @@
     /**
      * Updates the system ui state flags from SystemUI.
      */
-    public void setSystemUiFlags(int stateFlags) {
+    public void setSystemUiFlags(@SystemUiStateFlags long stateFlags) {
         mSystemUiStateFlags = stateFlags;
     }
 
@@ -359,7 +360,8 @@
      * @return the system ui state flags.
      */
     // TODO(141886704): See if we can remove this
-    public int getSystemUiStateFlags() {
+    @SystemUiStateFlags
+    public long getSystemUiStateFlags() {
         return mSystemUiStateFlags;
     }
 
@@ -399,7 +401,8 @@
                 && (mSystemUiStateFlags & SYSUI_STATE_MAGNIFICATION_OVERLAP) == 0
                 && ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0
                         || (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0)
-                && (mSystemUiStateFlags & SYSUI_STATE_DEVICE_DREAMING) == 0;
+                && (mSystemUiStateFlags & SYSUI_STATE_DEVICE_DREAMING) == 0
+                && (mSystemUiStateFlags & SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION) == 0;
     }
 
     /**
@@ -607,6 +610,16 @@
         return touchSlop * touchSlop;
     }
 
+    /**
+     * Returns the squared touch slop using the given base slop multiplier {@code slopMultiplier}
+     * and custom slop multiplier {@code customSlopMultiplier}.
+     */
+    public float getSquaredTouchSlop(float slopMultiplier, float customSlopMultiplier) {
+        float systemTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
+        float touchSlop = customSlopMultiplier * slopMultiplier * systemTouchSlop;
+        return touchSlop * touchSlop;
+    }
+
     public String getSystemUiStateString() {
         return  QuickStepContract.getSystemUiStateString(mSystemUiStateFlags);
     }
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 0ad60b7..0ac3ec7 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -72,6 +72,7 @@
 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.SystemUiStateFlags;
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
 import com.android.systemui.shared.system.RecentsAnimationListener;
 import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController;
@@ -111,7 +112,7 @@
  * Holds the reference to SystemUI.
  */
 public class SystemUiProxy implements ISystemUiProxy, NavHandle, SafeCloseable {
-    private static final String TAG = SystemUiProxy.class.getSimpleName();
+    private static final String TAG = "SystemUiProxy";
 
     public static final MainThreadInitializedObject<SystemUiProxy> INSTANCE =
             new MainThreadInitializedObject<>(SystemUiProxy::new);
@@ -172,7 +173,8 @@
     private final Handler mAsyncHandler;
 
     // TODO(141886704): Find a way to remove this
-    private int mLastSystemUiStateFlags;
+    @SystemUiStateFlags
+    private long mLastSystemUiStateFlags;
 
     /**
      * This is a singleton pending intent that is used to start recents via Shell (which is a
@@ -324,12 +326,13 @@
     }
 
     // TODO(141886704): Find a way to remove this
-    public void setLastSystemUiStateFlags(int stateFlags) {
+    public void setLastSystemUiStateFlags(@SystemUiStateFlags long stateFlags) {
         mLastSystemUiStateFlags = stateFlags;
     }
 
     // TODO(141886704): Find a way to remove this
-    public int getLastSystemUiStateFlags() {
+    @SystemUiStateFlags
+    public long getLastSystemUiStateFlags() {
         return mLastSystemUiStateFlags;
     }
 
@@ -575,6 +578,17 @@
         }
     }
 
+    @Override
+    public void toggleQuickSettingsPanel() {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.toggleQuickSettingsPanel();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call toggleQuickSettingsPanel", e);
+            }
+        }
+    }
+
     //
     // Pip
     //
@@ -747,12 +761,12 @@
     /**
      * Tells SysUI to show the bubble with the provided key.
      * @param key the key of the bubble to show.
-     * @param bubbleBarBounds bounds of the bubble bar in display coordinates
+     * @param top top coordinate of bubble bar on screen
      */
-    public void showBubble(String key, Rect bubbleBarBounds) {
+    public void showBubble(String key, int top) {
         if (mBubbles != null) {
             try {
-                mBubbles.showBubble(key, bubbleBarBounds);
+                mBubbles.showBubble(key, top);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call showBubble");
             }
@@ -760,19 +774,6 @@
     }
 
     /**
-     * Tells SysUI to remove the bubble with the provided key.
-     * @param key the key of the bubble to show.
-     */
-    public void removeBubble(String key) {
-        if (mBubbles == null) return;
-        try {
-            mBubbles.removeBubble(key);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed call removeBubble");
-        }
-    }
-
-    /**
      * Tells SysUI to remove all bubbles.
      */
     public void removeAllBubbles() {
@@ -814,19 +815,33 @@
     /**
      * Tells SysUI when the bubble stops being dragged.
      * Should be called only when the bubble bar is expanded.
-     * @param bubbleKey key of the bubble being dragged
+     *
      * @param location location of the bubble bar
+     * @param top      new top coordinate for bubble bar on screen
      */
-    public void stopBubbleDrag(@Nullable String bubbleKey, BubbleBarLocation location) {
+    public void stopBubbleDrag(BubbleBarLocation location, int top) {
         if (mBubbles == null) return;
         try {
-            mBubbles.stopBubbleDrag(bubbleKey, location);
+            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.
+     */
+    public void dragBubbleToDismiss(String key) {
+        if (mBubbles == null) return;
+        try {
+            mBubbles.dragBubbleToDismiss(key);
+        } 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.
      */
@@ -851,16 +866,17 @@
     }
 
     /**
-     * Tells SysUI the bounds for the bubble bar
-     * @param bubbleBarBounds bounds of the bubble bar in display coordinates
+     * Tells SysUI the top coordinate of bubble bar on screen
+     *
+     * @param topOnScreen top coordinate for bubble bar on screen
      */
-    public void setBubbleBarBounds(Rect bubbleBarBounds) {
+    public void updateBubbleBarTopOnScreen(int topOnScreen) {
         try {
             if (mBubbles != null) {
-                mBubbles.setBubbleBarBounds(bubbleBarBounds);
+                mBubbles.updateBubbleBarTopOnScreen(topOnScreen);
             }
         } catch (RemoteException e) {
-            Log.w(TAG, "Failed call setBubbleBarBounds");
+            Log.w(TAG, "Failed call updateBubbleBarTopOnScreen");
         }
     }
 
@@ -1429,28 +1445,6 @@
         }
     }
 
-    /** Call shell to stash desktop apps */
-    public void stashDesktopApps(int displayId) {
-        if (mDesktopMode != null) {
-            try {
-                mDesktopMode.stashDesktopApps(displayId);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call stashDesktopApps", e);
-            }
-        }
-    }
-
-    /** Call shell to hide desktop apps that may be stashed */
-    public void hideStashedDesktopApps(int displayId) {
-        if (mDesktopMode != null) {
-            try {
-                mDesktopMode.hideStashedDesktopApps(displayId);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call hideStashedDesktopApps", e);
-            }
-        }
-    }
-
     /**
      * If task with the given id is on the desktop, bring it to front
      */
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index d32c7a6..b183ae3 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -46,7 +46,7 @@
 import com.android.quickstep.views.RecentsViewContainer;
 import com.android.quickstep.views.TaskThumbnailViewDeprecated;
 import com.android.quickstep.views.TaskView;
-import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
+import com.android.quickstep.views.TaskView.TaskContainer;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
@@ -59,7 +59,7 @@
 public class TaskOverlayFactory implements ResourceBasedOverride {
 
     public static List<SystemShortcut> getEnabledShortcuts(TaskView taskView,
-            TaskIdAttributeContainer taskContainer) {
+            TaskContainer taskContainer) {
         final ArrayList<SystemShortcut> shortcuts = new ArrayList<>();
         final RecentsViewContainer container =
                 RecentsViewContainer.containerFromContext(taskView.getContext());
@@ -80,8 +80,9 @@
         return shortcuts;
     }
 
-    public TaskOverlay createOverlay(TaskThumbnailViewDeprecated thumbnailView) {
-        return new TaskOverlay(thumbnailView);
+    /** Creates a {@link TaskOverlay} associated with the provide {@link TaskContainer}. */
+    public TaskOverlay<?> createOverlay(TaskContainer taskContainer) {
+        return new TaskOverlay<>(taskContainer);
     }
 
     /**
@@ -124,28 +125,29 @@
     public static class TaskOverlay<T extends OverviewActionsView> {
 
         protected final Context mApplicationContext;
-        protected final TaskThumbnailViewDeprecated mThumbnailView;
+        protected final TaskContainer mTaskContainer;
 
         private T mActionsView;
         protected ImageActionsApi mImageApi;
 
-        protected TaskOverlay(TaskThumbnailViewDeprecated taskThumbnailViewDeprecated) {
-            mApplicationContext = taskThumbnailViewDeprecated.getContext().getApplicationContext();
-            mThumbnailView = taskThumbnailViewDeprecated;
+        protected TaskOverlay(TaskContainer taskContainer) {
+            mApplicationContext = taskContainer.getTaskView().getContext().getApplicationContext();
+            mTaskContainer = taskContainer;
             mImageApi = new ImageActionsApi(
-                    mApplicationContext, mThumbnailView::getThumbnail);
+                    mApplicationContext, mTaskContainer.getThumbnailViewDeprecated()::getThumbnail);
         }
 
         protected T getActionsView() {
             if (mActionsView == null) {
-                mActionsView = BaseActivity.fromContext(mThumbnailView.getContext()).findViewById(
+                mActionsView = BaseActivity.fromContext(
+                        mTaskContainer.getThumbnailViewDeprecated().getContext()).findViewById(
                         R.id.overview_actions_view);
             }
             return mActionsView;
         }
 
         public TaskThumbnailViewDeprecated getThumbnailView() {
-            return mThumbnailView;
+            return mTaskContainer.getThumbnailViewDeprecated();
         }
 
         /**
@@ -157,7 +159,8 @@
 
             if (thumbnail != null) {
                 getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated);
-                boolean isAllowedByPolicy = mThumbnailView.isRealSnapshot();
+                boolean isAllowedByPolicy =
+                        mTaskContainer.getThumbnailViewDeprecated().isRealSnapshot();
                 getActionsView().setCallbacks(new OverlayUICallbacksImpl(isAllowedByPolicy, task));
             }
         }
@@ -168,7 +171,8 @@
          * @param callback callback to run, after switching to screenshot
          */
         public void endLiveTileMode(@NonNull Runnable callback) {
-            RecentsView recentsView = mThumbnailView.getTaskView().getRecentsView();
+            RecentsView recentsView =
+                    mTaskContainer.getThumbnailViewDeprecated().getTaskView().getRecentsView();
             // Task has already been dismissed
             if (recentsView == null) return;
             recentsView.switchToScreenshot(
@@ -181,8 +185,8 @@
          */
         @SuppressLint("NewApi")
         protected void saveScreenshot(Task task) {
-            if (mThumbnailView.isRealSnapshot()) {
-                mImageApi.saveScreenshot(mThumbnailView.getThumbnail(),
+            if (mTaskContainer.getThumbnailViewDeprecated().isRealSnapshot()) {
+                mImageApi.saveScreenshot(mTaskContainer.getThumbnailViewDeprecated().getThumbnail(),
                         getTaskSnapshotBounds(), getTaskSnapshotInsets(), task.key);
             } else {
                 showBlockedByPolicyMessage();
@@ -190,14 +194,17 @@
         }
 
         protected void enterSplitSelect() {
-            RecentsView overviewPanel = mThumbnailView.getTaskView().getRecentsView();
+            RecentsView overviewPanel =
+                    mTaskContainer.getThumbnailViewDeprecated().getTaskView().getRecentsView();
             // Task has already been dismissed
             if (overviewPanel == null) return;
-            overviewPanel.initiateSplitSelect(mThumbnailView.getTaskView());
+            overviewPanel.initiateSplitSelect(
+                    mTaskContainer.getThumbnailViewDeprecated().getTaskView());
         }
 
         protected void saveAppPair() {
-            GroupedTaskView taskView = (GroupedTaskView) mThumbnailView.getTaskView();
+            GroupedTaskView taskView =
+                    (GroupedTaskView) mTaskContainer.getThumbnailViewDeprecated().getTaskView();
             taskView.getRecentsView().getSplitSelectController().getAppPairsController()
                     .saveAppPair(taskView);
         }
@@ -243,10 +250,11 @@
          */
         public Rect getTaskSnapshotBounds() {
             int[] location = new int[2];
-            mThumbnailView.getLocationOnScreen(location);
+            mTaskContainer.getThumbnailViewDeprecated().getLocationOnScreen(location);
 
-            return new Rect(location[0], location[1], mThumbnailView.getWidth() + location[0],
-                    mThumbnailView.getHeight() + location[1]);
+            return new Rect(location[0], location[1],
+                    mTaskContainer.getThumbnailViewDeprecated().getWidth() + location[0],
+                    mTaskContainer.getThumbnailViewDeprecated().getHeight() + location[1]);
         }
 
         /**
@@ -256,7 +264,7 @@
          */
         @RequiresApi(api = Build.VERSION_CODES.Q)
         public Insets getTaskSnapshotInsets() {
-            return mThumbnailView.getScaledInsets();
+            return mTaskContainer.getThumbnailViewDeprecated().getScaledInsets();
         }
 
         /**
@@ -267,17 +275,21 @@
 
         protected void showBlockedByPolicyMessage() {
             ActivityContext activityContext = ActivityContext.lookupContext(
-                    mThumbnailView.getContext());
+                    mTaskContainer.getThumbnailViewDeprecated().getContext());
             String message = activityContext.getStringCache() != null
                     ? activityContext.getStringCache().disabledByAdminMessage
-                    : mThumbnailView.getContext().getString(R.string.blocked_by_policy);
+                    : mTaskContainer.getThumbnailViewDeprecated().getContext().getString(
+                            R.string.blocked_by_policy);
 
-            Snackbar.show(BaseActivity.fromContext(mThumbnailView.getContext()), message, null);
+            Snackbar.show(BaseActivity.fromContext(
+                    mTaskContainer.getThumbnailViewDeprecated().getContext()), message, null);
         }
 
         /** Called when the snapshot has updated its full screen drawing parameters. */
-        public void setFullscreenParams(TaskView.FullscreenDrawParams fullscreenParams) {
-        }
+        public void setFullscreenParams(TaskView.FullscreenDrawParams fullscreenParams) {}
+
+        /** Sets visibility for the overlay associated elements. */
+        public void setVisibility(int visibility) {}
 
         private class ScreenshotSystemShortcut extends SystemShortcut {
 
@@ -292,7 +304,8 @@
 
             @Override
             public void onClick(View view) {
-                saveScreenshot(mThumbnailView.getTaskView().getTask());
+                saveScreenshot(
+                        mTaskContainer.getThumbnailViewDeprecated().getTaskView().getFirstTask());
                 dismissTaskMenuView();
             }
         }
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index 8df4bdd..4b5c826 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -42,10 +42,8 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Flags;
 import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
 import com.android.launcher3.model.WellbeingModel;
-import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.popup.SystemShortcut.AppInfo;
 import com.android.launcher3.util.InstantAppResolver;
@@ -58,14 +56,13 @@
 import com.android.quickstep.views.RecentsViewContainer;
 import com.android.quickstep.views.TaskThumbnailViewDeprecated;
 import com.android.quickstep.views.TaskView;
-import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
+import com.android.quickstep.views.TaskView.TaskContainer;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
 import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
 import com.android.systemui.shared.recents.view.RecentsTransition;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.function.Function;
@@ -78,7 +75,7 @@
 public interface TaskShortcutFactory {
     @Nullable
     default List<SystemShortcut> getShortcuts(RecentsViewContainer container,
-            TaskIdAttributeContainer taskContainer) {
+            TaskContainer taskContainer) {
         return null;
     }
 
@@ -108,7 +105,7 @@
     TaskShortcutFactory APP_INFO = new TaskShortcutFactory() {
         @Override
         public List<SystemShortcut> getShortcuts(RecentsViewContainer container,
-                TaskIdAttributeContainer taskContainer) {
+                TaskContainer taskContainer) {
             TaskView taskView = taskContainer.getTaskView();
             AppInfo.SplitAccessibilityInfo accessibilityInfo =
                     new AppInfo.SplitAccessibilityInfo(taskView.containsMultipleTasks(),
@@ -131,7 +128,8 @@
 
         public SplitSelectSystemShortcut(RecentsViewContainer container, TaskView taskView,
                 SplitPositionOption option) {
-            super(option.iconResId, option.textResId, container, taskView.getItemInfo(), taskView);
+            super(option.iconResId, option.textResId, container, taskView.getFirstItemInfo(),
+                    taskView);
             mTaskView = taskView;
             mSplitPositionOption = option;
         }
@@ -152,8 +150,8 @@
 
         public SaveAppPairSystemShortcut(RecentsViewContainer container, GroupedTaskView taskView,
             int iconResId) {
-                super(iconResId, R.string.save_app_pair, container,
-                    taskView.getItemInfo(), taskView);
+            super(iconResId, R.string.save_app_pair, container, taskView.getFirstItemInfo(),
+                    taskView);
             mTaskView = taskView;
         }
 
@@ -176,14 +174,14 @@
         private final LauncherEvent mLauncherEvent;
 
         public FreeformSystemShortcut(int iconRes, int textRes, RecentsViewContainer container,
-                TaskIdAttributeContainer taskContainer, LauncherEvent launcherEvent) {
+                TaskContainer taskContainer, LauncherEvent launcherEvent) {
             super(iconRes, textRes, container, taskContainer.getItemInfo(),
                     taskContainer.getTaskView());
             mLauncherEvent = launcherEvent;
             mHandler = new Handler(Looper.getMainLooper());
             mTaskView = taskContainer.getTaskView();
             mRecentsView = container.getOverviewPanel();
-            mThumbnailView = taskContainer.getThumbnailView();
+            mThumbnailView = taskContainer.getThumbnailViewDeprecated();
         }
 
         @Override
@@ -199,7 +197,7 @@
         }
 
         private void startActivity() {
-            final Task.TaskKey taskKey = mTaskView.getTask().key;
+            final Task.TaskKey taskKey = mTaskView.getFirstTask().key;
             final int taskId = taskKey.id;
             final ActivityOptions options = makeLaunchOptions(mTarget);
             if (options != null) {
@@ -243,7 +241,7 @@
                 overridePendingAppTransitionMultiThumbFuture(
                         future, animStartedListener, mHandler, true /* scaleUp */,
                         taskKey.displayId);
-                mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getItemInfo())
+                mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getFirstItemInfo())
                         .log(mLauncherEvent);
             }
         }
@@ -292,7 +290,7 @@
     TaskShortcutFactory SPLIT_SELECT = new TaskShortcutFactory() {
         @Override
         public List<SystemShortcut> getShortcuts(RecentsViewContainer container,
-                TaskIdAttributeContainer taskContainer) {
+                TaskContainer taskContainer) {
             DeviceProfile deviceProfile = container.getDeviceProfile();
             final Task task = taskContainer.getTask();
             final int intentFlags = task.key.baseIntent.getFlags();
@@ -327,7 +325,7 @@
         @Nullable
         @Override
         public List<SystemShortcut> getShortcuts(RecentsViewContainer container,
-                TaskIdAttributeContainer taskContainer) {
+                TaskContainer taskContainer) {
             DeviceProfile deviceProfile = container.getDeviceProfile();
             final TaskView taskView = taskContainer.getTaskView();
             final RecentsView recentsView = taskView.getRecentsView();
@@ -336,24 +334,15 @@
                     recentsView.isTaskInExpectedScrollPosition(recentsView.indexOfChild(taskView));
             boolean shouldShowActionsButtonInstead =
                     isLargeTileFocusedTask && isInExpectedScrollPosition;
-            boolean hasUnpinnableApp = Arrays.stream(taskView.getTaskIdAttributeContainers())
-                    .anyMatch(att -> att != null && att.getItemInfo() != null
-                            && ((att.getItemInfo().runtimeStatusFlags
-                                & ItemInfoWithIcon.FLAG_NOT_PINNABLE) != 0));
 
             // No "save app pair" menu item if:
-            // - app pairs feature is not enabled
             // - we are in 3p launcher
-            // - the task in question is a single task
-            // - at least one app in app pair is unpinnable
             // - the Overview Actions Button should be visible
-            // - the task is not a GroupedTaskView
-            if (!FeatureFlags.enableAppPairs()
-                    || !recentsView.supportsAppPairs()
-                    || !taskView.containsMultipleTasks()
-                    || hasUnpinnableApp
+            // - the task view is not a valid save-able split pair
+            if (!recentsView.supportsAppPairs()
                     || shouldShowActionsButtonInstead
-                    || !(taskView instanceof GroupedTaskView)) {
+                    || !recentsView.getSplitSelectController().getAppPairsController()
+                            .canSaveAppPair(taskView)) {
                 return null;
             }
 
@@ -375,7 +364,7 @@
     TaskShortcutFactory FREE_FORM = new TaskShortcutFactory() {
         @Override
         public List<SystemShortcut> getShortcuts(RecentsViewContainer container,
-                TaskIdAttributeContainer taskContainer) {
+                TaskContainer taskContainer) {
             final Task task = taskContainer.getTask();
             if (!task.isDockable) {
                 return null;
@@ -401,7 +390,7 @@
     TaskShortcutFactory PIN = new TaskShortcutFactory() {
         @Override
         public List<SystemShortcut> getShortcuts(RecentsViewContainer container,
-                TaskIdAttributeContainer taskContainer) {
+                TaskContainer taskContainer) {
             if (!SystemUiProxy.INSTANCE.get(container.asContext()).isActive()) {
                 return null;
             }
@@ -423,7 +412,7 @@
         private final TaskView mTaskView;
 
         public PinSystemShortcut(RecentsViewContainer target,
-                TaskIdAttributeContainer taskContainer) {
+                TaskContainer taskContainer) {
             super(R.drawable.ic_pin, R.string.recent_task_option_pin, target,
                     taskContainer.getItemInfo(), taskContainer.getTaskView());
             mTaskView = taskContainer.getTaskView();
@@ -433,10 +422,10 @@
         public void onClick(View view) {
             if (mTaskView.launchTaskAnimated() != null) {
                 SystemUiProxy.INSTANCE.get(mTarget.asContext()).startScreenPinning(
-                        mTaskView.getTask().key.id);
+                        mTaskView.getFirstTask().key.id);
             }
             dismissTaskMenuView();
-            mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getItemInfo())
+            mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getFirstItemInfo())
                     .log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_PIN_TAP);
         }
     }
@@ -444,7 +433,7 @@
     TaskShortcutFactory INSTALL = new TaskShortcutFactory() {
         @Override
         public List<SystemShortcut> getShortcuts(RecentsViewContainer container,
-                TaskIdAttributeContainer taskContainer) {
+                TaskContainer taskContainer) {
             Task t = taskContainer.getTask();
             return InstantAppResolver.newInstance(container.asContext()).isInstantApp(
                     t.getTopComponent().getPackageName(), t.getKey().userId)
@@ -457,7 +446,7 @@
     TaskShortcutFactory WELLBEING = new TaskShortcutFactory() {
         @Override
         public List<SystemShortcut> getShortcuts(RecentsViewContainer container,
-                TaskIdAttributeContainer taskContainer) {
+                TaskContainer taskContainer) {
             SystemShortcut<ActivityContext> wellbeingShortcut =
                     WellbeingModel.SHORTCUT_FACTORY.getShortcut(container,
                             taskContainer.getItemInfo(), taskContainer.getTaskView());
@@ -468,13 +457,12 @@
     TaskShortcutFactory SCREENSHOT = new TaskShortcutFactory() {
         @Override
         public List<SystemShortcut> getShortcuts(RecentsViewContainer container,
-                TaskIdAttributeContainer taskContainer) {
+                TaskContainer taskContainer) {
             boolean isTablet = container.getDeviceProfile().isTablet;
             boolean isGridOnlyOverview = isTablet && Flags.enableGridOnlyOverview();
             // Extra conditions if it's not grid-only overview
             if (!isGridOnlyOverview) {
-                RecentsOrientedState orientedState =
-                        taskContainer.getTaskView().getRecentsView().getPagedViewOrientedState();
+                RecentsOrientedState orientedState = taskContainer.getTaskView().getOrientedState();
                 boolean isFakeLandscape = !orientedState.isRecentsActivityRotationAllowed()
                         && orientedState.getTouchRotation() != ROTATION_0;
                 if (!isFakeLandscape) {
@@ -482,10 +470,8 @@
                 }
             }
 
-            SystemShortcut screenshotShortcut =
-                    taskContainer.getThumbnailView().getTaskOverlay()
-                            .getScreenshotShortcut(container, taskContainer.getItemInfo(),
-                                    taskContainer.getTaskView());
+            SystemShortcut screenshotShortcut = taskContainer.getOverlay().getScreenshotShortcut(
+                    container, taskContainer.getItemInfo(), taskContainer.getTaskView());
             return createSingletonShortcutList(screenshotShortcut);
         }
 
@@ -498,13 +484,12 @@
     TaskShortcutFactory MODAL = new TaskShortcutFactory() {
         @Override
         public List<SystemShortcut> getShortcuts(RecentsViewContainer container,
-                TaskIdAttributeContainer taskContainer) {
+                TaskContainer taskContainer) {
             boolean isTablet = container.getDeviceProfile().isTablet;
             boolean isGridOnlyOverview = isTablet && Flags.enableGridOnlyOverview();
             // Extra conditions if it's not grid-only overview
             if (!isGridOnlyOverview) {
-                RecentsOrientedState orientedState =
-                        taskContainer.getTaskView().getRecentsView().getPagedViewOrientedState();
+                RecentsOrientedState orientedState = taskContainer.getTaskView().getOrientedState();
                 boolean isFakeLandscape = !orientedState.isRecentsActivityRotationAllowed()
                         && orientedState.getTouchRotation() != ROTATION_0;
                 if (!isFakeLandscape) {
@@ -517,9 +502,8 @@
             }
 
             SystemShortcut modalStateSystemShortcut =
-                    taskContainer.getThumbnailView().getTaskOverlay()
-                            .getModalStateSystemShortcut(
-                                    taskContainer.getItemInfo(), taskContainer.getTaskView());
+                    taskContainer.getOverlay().getModalStateSystemShortcut(
+                            taskContainer.getItemInfo(), taskContainer.getTaskView());
             return createSingletonShortcutList(modalStateSystemShortcut);
         }
     };
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index b7cbb47..7ebb767 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -148,12 +148,12 @@
      * @param callback The callback to receive the task after its data has been populated.
      * @return A cancelable handle to the request
      */
-    public CancellableTask updateThumbnailInBackground(
+    public CancellableTask<ThumbnailData> updateThumbnailInBackground(
             Task task, Consumer<ThumbnailData> callback) {
         Preconditions.assertUIThread();
 
         boolean lowResolution = !mHighResLoadingState.isEnabled();
-        if (task.thumbnail != null && task.thumbnail.thumbnail != null
+        if (task.thumbnail != null && task.thumbnail.getThumbnail() != null
                 && (!task.thumbnail.reducedResolution || lowResolution)) {
             // Nothing to load, the thumbnail is already high-resolution or matches what the
             // request, so just callback
@@ -184,12 +184,12 @@
         return newSize > oldSize;
     }
 
-    private CancellableTask updateThumbnailInBackground(TaskKey key, boolean lowResolution,
-            Consumer<ThumbnailData> callback) {
+    private CancellableTask<ThumbnailData> updateThumbnailInBackground(TaskKey key,
+            boolean lowResolution, Consumer<ThumbnailData> callback) {
         Preconditions.assertUIThread();
 
         ThumbnailData cachedThumbnail = mCache.getAndInvalidateIfModified(key);
-        if (cachedThumbnail != null &&  cachedThumbnail.thumbnail != null
+        if (cachedThumbnail != null &&  cachedThumbnail.getThumbnail() != null
                 && (!cachedThumbnail.reducedResolution || lowResolution)) {
             // Already cached, lets use that thumbnail
             callback.accept(cachedThumbnail);
@@ -200,7 +200,7 @@
                 () -> {
                     ThumbnailData thumbnailData = ActivityManagerWrapper.getInstance()
                             .getTaskThumbnail(key.id, lowResolution);
-                    return thumbnailData.thumbnail != null ? thumbnailData
+                    return thumbnailData.getThumbnail() != null ? thumbnailData
                             : ActivityManagerWrapper.getInstance().takeTaskThumbnail(key.id);
                 },
                 MAIN_EXECUTOR,
@@ -210,7 +210,7 @@
                     if (enableGridOnlyOverview() && result.reducedResolution
                             && getHighResLoadingState().isEnabled()) {
                         ThumbnailData newCachedThumbnail = mCache.getAndInvalidateIfModified(key);
-                        if (newCachedThumbnail != null && newCachedThumbnail.thumbnail != null
+                        if (newCachedThumbnail != null && newCachedThumbnail.getThumbnail() != null
                                 && !newCachedThumbnail.reducedResolution) {
                             return;
                         }
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index d89d399..ecd84f8 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -121,7 +121,7 @@
                 for (int i = 0; i < recentsView.getTaskViewCount(); i++) {
                     TaskView taskView = recentsView.getTaskViewAt(i);
                     if (recentsView.isTaskViewVisible(taskView)) {
-                        Task.TaskKey key = taskView.getTask().key;
+                        Task.TaskKey key = taskView.getFirstTask().key;
                         if (componentName.equals(key.getComponent()) && userId == key.userId) {
                             return taskView;
                         }
@@ -165,8 +165,8 @@
             @NonNull RemoteAnimationTarget[] nonAppTargets,
             @Nullable DepthController depthController,
             PendingAnimation out) {
-        boolean isQuickSwitch = v.isEndQuickswitchCuj();
-        v.setEndQuickswitchCuj(false);
+        boolean isQuickSwitch = v.isEndQuickSwitchCuj();
+        v.setEndQuickSwitchCuj(false);
 
         final RemoteAnimationTargets targets =
                 new RemoteAnimationTargets(appTargets, wallpaperTargets, nonAppTargets,
@@ -334,7 +334,7 @@
             // During animation we apply transformation on the thumbnailView (and not the rootView)
             // to follow the TaskViewSimulator. So the final matrix applied on the thumbnailView is:
             //    Mt K(0)` K(t) Mt`
-            TaskThumbnailViewDeprecated[] thumbnails = v.getThumbnails();
+            TaskThumbnailViewDeprecated[] thumbnails = v.getThumbnailViews();
 
             // In case simulator copies and thumbnail size do no match, ensure we get the lesser.
             // This ensures we do not create arrays with empty elements or attempt to references
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index f94a29c..4599f18 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -129,6 +129,7 @@
 import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.InputMonitorCompat;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
 import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
 import com.android.systemui.unfold.progress.IUnfoldAnimation;
 import com.android.wm.shell.back.IBackAnimation;
@@ -301,9 +302,9 @@
         }
 
         @BinderThread
-        public void onSystemUiStateChanged(int stateFlags) {
+        public void onSystemUiStateChanged(@SystemUiStateFlags long stateFlags) {
             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
-                int lastFlags = tis.mDeviceState.getSystemUiStateFlags();
+                long lastFlags = tis.mDeviceState.getSystemUiStateFlags();
                 tis.mDeviceState.setSystemUiFlags(stateFlags);
                 tis.onSystemUiFlagsChanged(lastFlags);
             }));
@@ -636,14 +637,14 @@
     }
 
     @UiThread
-    private void onSystemUiFlagsChanged(int lastSysUIFlags) {
+    private void onSystemUiFlagsChanged(@SystemUiStateFlags long lastSysUIFlags) {
         if (LockedUserState.get(this).isUserUnlocked()) {
-            int systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
+            long systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
             SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
             mOverviewComponentObserver.onSystemUiStateChanged();
             mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags);
 
-            int isShadeExpandedFlag =
+            long isShadeExpandedFlag =
                     SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
             boolean wasExpanded = (lastSysUIFlags & isShadeExpandedFlag) != 0;
             boolean isExpanded = (systemUiStateFlags & isShadeExpandedFlag) != 0;
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index 2e76356..94764a5 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -42,16 +42,14 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.launcher3.anim.AnimatedFloat;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.util.MultiPropertyFactory;
 import com.android.quickstep.RecentsActivity;
 import com.android.quickstep.views.ClearAllButton;
-import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.RecentsViewContainer;
 
 /**
  * State controller for fallback recents activity
@@ -99,7 +97,7 @@
                 clearAllButtonAlpha, LINEAR);
         float overviewButtonAlpha = state.hasOverviewActions() ? 1 : 0;
         setter.setFloat(mActivity.getActionsView().getVisibilityAlpha(),
-                MultiPropertyFactory.MULTI_PROPERTY_VALUE, overviewButtonAlpha, LINEAR);
+                AnimatedFloat.VALUE, overviewButtonAlpha, LINEAR);
 
         float[] scaleAndOffset = state.getOverviewScaleAndOffset(mActivity);
         setter.setFloat(mRecentsView, RECENTS_SCALE_PROPERTY, scaleAndOffset[0],
@@ -138,7 +136,7 @@
     }
 
     private Interpolator getOverviewInterpolator(RecentsState toState) {
-        return toState.overviewUi() ? INSTANT : FINAL_FRAME;
+        return toState.isRecentsViewVisible() ? INSTANT : FINAL_FRAME;
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index b79586b..096ed2c 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -247,7 +247,7 @@
         }
 
         // Set border after select mode changes to avoid showing border during state transition
-        if (!toState.overviewUi() || toState == MODAL_TASK) {
+        if (!toState.isRecentsViewVisible() || toState == MODAL_TASK) {
             setTaskBorderEnabled(false);
         }
 
@@ -267,7 +267,7 @@
             setOverviewSelectEnabled(false);
         }
 
-        if (finalState.overviewUi() && finalState != MODAL_TASK) {
+        if (finalState.isRecentsViewVisible() && finalState != MODAL_TASK) {
             setTaskBorderEnabled(true);
         }
 
@@ -298,7 +298,7 @@
     public boolean onTouchEvent(MotionEvent ev) {
         boolean result = super.onTouchEvent(ev);
         // Do not let touch escape to siblings below this view.
-        return result || mContainer.getStateManager().getState().overviewUi();
+        return result || mContainer.getStateManager().getState().isRecentsViewVisible();
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index 84937a2..ca9753f 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -26,7 +26,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.util.Themes;
-import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.RecentsViewContainer;
 
 /**
@@ -41,22 +40,23 @@
     private static final int FLAG_SHOW_AS_GRID = BaseState.getFlag(4);
     private static final int FLAG_SCRIM = BaseState.getFlag(5);
     private static final int FLAG_LIVE_TILE = BaseState.getFlag(6);
-    private static final int FLAG_OVERVIEW_UI = BaseState.getFlag(7);
+    private static final int FLAG_RECENTS_VIEW_VISIBLE = BaseState.getFlag(7);
     private static final int FLAG_TASK_THUMBNAIL_SPLASH = BaseState.getFlag(8);
 
     public static final RecentsState DEFAULT = new RecentsState(0,
             FLAG_DISABLE_RESTORE | FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_SHOW_AS_GRID
-                    | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_OVERVIEW_UI);
+                    | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_RECENTS_VIEW_VISIBLE);
     public static final RecentsState MODAL_TASK = new ModalState(1,
             FLAG_DISABLE_RESTORE | FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_MODAL
-                    | FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_OVERVIEW_UI);
+                    | FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_RECENTS_VIEW_VISIBLE);
     public static final RecentsState BACKGROUND_APP = new BackgroundAppState(2,
-            FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN | FLAG_OVERVIEW_UI
+            FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN
+                    | FLAG_RECENTS_VIEW_VISIBLE
                     | FLAG_TASK_THUMBNAIL_SPLASH);
     public static final RecentsState HOME = new RecentsState(3, 0);
     public static final RecentsState BG_LAUNCHER = new LauncherState(4, 0);
     public static final RecentsState OVERVIEW_SPLIT_SELECT = new RecentsState(5,
-            FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_OVERVIEW_UI | FLAG_CLOSE_POPUPS
+            FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_RECENTS_VIEW_VISIBLE | FLAG_CLOSE_POPUPS
                     | FLAG_DISABLE_RESTORE);
 
     public final int ordinal;
@@ -152,8 +152,8 @@
     /**
      * True if the state has overview panel visible.
      */
-    public boolean overviewUi() {
-        return hasFlag(FLAG_OVERVIEW_UI);
+    public boolean isRecentsViewVisible() {
+        return hasFlag(FLAG_RECENTS_VIEW_VISIBLE);
     }
 
     private static class ModalState extends RecentsState {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
index 6b3e6e9..9a25c32 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
@@ -28,6 +28,8 @@
 import android.content.res.Resources;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
 import android.view.InputDevice;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
@@ -52,6 +54,9 @@
  */
 public class TaskbarUnstashInputConsumer extends DelegateInputConsumer {
 
+    private static final int HOVER_TASKBAR_UNSTASH_TIMEOUT = 500;
+    private static final Handler sUnstashHandler = new Handler(Looper.getMainLooper());
+
     private final TaskbarActivityContext mTaskbarActivityContext;
     private final OverviewCommandHelper mOverviewCommandHelper;
     private final float mUnstashArea;
@@ -308,16 +313,25 @@
                 dp.heightPx);
 
         if (mBottomEdgeBounds.contains(x, y)) {
-            // If hovering stashed taskbar and then hover screen bottom edge, unstash it.
-            mTaskbarActivityContext.onSwipeToUnstashTaskbar();
-            mIsStashedTaskbarHovered = false;
+            // start a single unstash timeout if hovering bottom edge under the hinted taskbar.
+            if (!sUnstashHandler.hasMessagesOrCallbacks()) {
+                sUnstashHandler.postDelayed(() -> {
+                    mTaskbarActivityContext.onSwipeToUnstashTaskbar();
+                    mIsStashedTaskbarHovered = false;
+                }, HOVER_TASKBAR_UNSTASH_TIMEOUT);
+            }
         } else if (!isStashedTaskbarHovered(x, y)) {
-            // If exit hovering stashed taskbar, remove hint.
+            // If exit hovering stashed taskbar, remove hint and clear pending unstash calls.
+            sUnstashHandler.removeCallbacksAndMessages(null);
             startStashedTaskbarHover(/* isHovered = */ false);
+        } else {
+            sUnstashHandler.removeCallbacksAndMessages(null);
         }
     }
 
     private void updateUnhoveredTaskbarState(int x, int y) {
+        sUnstashHandler.removeCallbacksAndMessages(null);
+
         DeviceProfile dp = mTaskbarActivityContext.getDeviceProfile();
         mBottomEdgeBounds.set(
                 0,
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index e3e14ae..1d4160d 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -16,6 +16,10 @@
 
 package com.android.quickstep.logging;
 
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
 import static androidx.core.util.Preconditions.checkNotNull;
 import static androidx.core.util.Preconditions.checkState;
 
@@ -26,10 +30,17 @@
 import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.SEARCH_RESULT_CONTAINER;
 import static com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers.ContainerCase.DEVICE_SEARCH_RESULT_CONTAINER;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_SNAPSHOT;
+import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_0;
+import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_90;
+import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_180;
+import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_270;
 import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__ALLAPPS;
 import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__BACKGROUND;
 import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__HOME;
 import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__OVERVIEW;
+import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__PORTRAIT;
+import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__LANDSCAPE;
+import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__SEASCAPE;
 
 import android.content.Context;
 import android.text.TextUtils;
@@ -59,6 +70,7 @@
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.LogConfig;
 import com.android.launcher3.views.ActivityContext;
@@ -226,10 +238,15 @@
         private int mInputType = SysUiStatsLog.LAUNCHER_UICHANGED__INPUT_TYPE__UNKNOWN;
         private Optional<Integer> mFeatures = Optional.empty();
         private Optional<String> mPackageName = Optional.empty();
+        /**
+         * Indicates the current rotation of the display. Uses {@link android.view.Surface values.}
+         */
+        private final int mDisplayRotation;
 
         StatsCompatLogger(Context context, ActivityContext activityContext) {
             mContext = context;
             mActivityContext = Optional.ofNullable(activityContext);
+            mDisplayRotation = DisplayController.INSTANCE.get(mContext).getInfo().rotation;
         }
 
         @Override
@@ -392,6 +409,20 @@
                 case LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END:
                     InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_ALL_APPS_SCROLL);
                     break;
+                case LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_BEGIN:
+                    InteractionJankMonitorWrapper.begin(view, Cuj.CUJ_LAUNCHER_PRIVATE_SPACE_LOCK);
+                    break;
+                case LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_END:
+                    InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_PRIVATE_SPACE_LOCK);
+                    break;
+                case LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_BEGIN:
+                    InteractionJankMonitorWrapper.begin(
+                            view,
+                            Cuj.CUJ_LAUNCHER_PRIVATE_SPACE_UNLOCK);
+                    break;
+                case LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_END:
+                    InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_PRIVATE_SPACE_UNLOCK);
+                    break;
                 default:
                     break;
             }
@@ -488,7 +519,28 @@
                     getSearchAttributes(atomInfo) /* searchAttributes */,
                     getAttributes(atomInfo) /* attributes */,
                     inputType /* input_type */,
-                    atomInfo.getUserType() /* user_type */);
+                    atomInfo.getUserType() /* user_type */,
+                    getDisplayRotation() /* display_rotation */,
+                    getRecentsOrientationHandler(atomInfo) /* recents_orientation_handler */);
+        }
+
+        private int getDisplayRotation() {
+            return switch (mDisplayRotation) {
+                case ROTATION_90 -> LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_90;
+                case ROTATION_180 -> LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_180;
+                case ROTATION_270 -> LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_270;
+                default -> LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_0;
+            };
+        }
+
+        private int getRecentsOrientationHandler(LauncherAtom.ItemInfo itemInfo) {
+            var orientationHandler =
+                    itemInfo.getContainerInfo().getTaskSwitcherContainer().getOrientationHandler();
+            return switch (orientationHandler) {
+                case PORTRAIT -> LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__PORTRAIT;
+                case LANDSCAPE -> LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__LANDSCAPE;
+                case SEASCAPE -> LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__SEASCAPE;
+            };
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
index 1640104..8f8cc6e 100644
--- a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
@@ -42,6 +42,7 @@
 import com.android.launcher3.LauncherAnimUtils
 import com.android.launcher3.R
 import com.android.launcher3.Utilities
+import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer
 import com.android.launcher3.touch.PagedOrientationHandler.ChildBounds
 import com.android.launcher3.touch.PagedOrientationHandler.Float2DAction
 import com.android.launcher3.touch.PagedOrientationHandler.Int2DAction
@@ -611,6 +612,9 @@
     override fun getFloatingTaskPrimaryTranslation(floatingTask: View, dp: DeviceProfile): Float =
         floatingTask.translationY
 
+    override fun getHandlerTypeForLogging(): TaskSwitcherContainer.OrientationHandler =
+        TaskSwitcherContainer.OrientationHandler.LANDSCAPE
+
     /**
      * Retrieves split icons position
      *
diff --git a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
index 1be908b..f6284d5 100644
--- a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
+++ b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
@@ -46,9 +46,12 @@
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.touch.DefaultPagedViewHandler;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
 import com.android.launcher3.util.SplitConfigurationOptions;
@@ -802,4 +805,10 @@
                 ? floatingTask.getTranslationX()
                 : floatingTask.getTranslationY();
     }
+
+    @NonNull
+    @Override
+    public LauncherAtom.TaskSwitcherContainer.OrientationHandler getHandlerTypeForLogging() {
+        return LauncherAtom.TaskSwitcherContainer.OrientationHandler.PORTRAIT;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt b/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
index 6c82890..5bc1be8 100644
--- a/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
@@ -26,6 +26,7 @@
 import android.widget.FrameLayout
 import android.widget.LinearLayout
 import com.android.launcher3.DeviceProfile
+import com.android.launcher3.logger.LauncherAtom
 import com.android.launcher3.touch.PagedOrientationHandler
 import com.android.launcher3.touch.PagedOrientationHandler.Float2DAction
 import com.android.launcher3.touch.PagedOrientationHandler.Int2DAction
@@ -371,6 +372,8 @@
      */
     fun getFloatingTaskPrimaryTranslation(floatingTask: View, dp: DeviceProfile): Float
 
+    fun getHandlerTypeForLogging(): LauncherAtom.TaskSwitcherContainer.OrientationHandler
+
     companion object {
         @JvmField val PORTRAIT: RecentsPagedOrientationHandler = PortraitPagedViewHandler()
         @JvmField val LANDSCAPE: RecentsPagedOrientationHandler = LandscapePagedViewHandler()
diff --git a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
index 5bebf8c..46e4b0c 100644
--- a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
@@ -32,6 +32,7 @@
 import com.android.launcher3.Flags
 import com.android.launcher3.R
 import com.android.launcher3.Utilities
+import com.android.launcher3.logger.LauncherAtom
 import com.android.launcher3.touch.SingleAxisSwipeDetector
 import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
 import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT
@@ -395,4 +396,8 @@
             iconView.layoutParams = layoutParams
         }
     }
+
+    @Override
+    override fun getHandlerTypeForLogging(): LauncherAtom.TaskSwitcherContainer.OrientationHandler =
+        LauncherAtom.TaskSwitcherContainer.OrientationHandler.SEASCAPE
 }
diff --git a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewData.kt b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewData.kt
new file mode 100644
index 0000000..28212cf
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewData.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.viewmodel
+
+import kotlinx.coroutines.flow.MutableStateFlow
+
+// This is far from complete but serves the purpose of enabling refactoring in other areas
+class RecentsViewData {
+    val fullscreenProgress = MutableStateFlow(1f)
+
+    // This is typically a View concern but it is used to invalidate rendering in other Views
+    val scale = MutableStateFlow(1f)
+}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
index d51069f..8762976 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
@@ -17,22 +17,44 @@
 package com.android.quickstep.task.thumbnail
 
 import android.content.Context
+import android.content.res.Configuration
 import android.graphics.Canvas
+import android.graphics.Outline
 import android.graphics.Paint
 import android.graphics.PorterDuff
 import android.graphics.PorterDuffXfermode
 import android.util.AttributeSet
 import android.view.View
-import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.*
+import android.view.ViewOutlineProvider
+import com.android.launcher3.Utilities
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
+import com.android.quickstep.util.TaskCornerRadius
+import com.android.quickstep.views.RecentsView
+import com.android.quickstep.views.RecentsViewContainer
+import com.android.quickstep.views.TaskView
+import com.android.systemui.shared.system.QuickStepContract
 import kotlinx.coroutines.MainScope
 import kotlinx.coroutines.launch
 
 class TaskThumbnailView : View {
     // TODO(b/335649589): Ideally create and obtain this from DI. This ViewModel should be scoped
     //  to [TaskView], and also shared between [TaskView] and [TaskThumbnailView]
-    val viewModel = TaskThumbnailViewModel()
+    //  This is using a lazy for now because the dependencies cannot be obtained without DI.
+    val viewModel by lazy {
+        TaskThumbnailViewModel(
+            RecentsViewContainer.containerFromContext<RecentsViewContainer>(context)
+                .getOverviewPanel<RecentsView<*, *>>()
+                .mRecentsViewData,
+            (parent as TaskView).taskViewData
+        )
+    }
 
     private var uiState: TaskThumbnailUiState = Uninitialized
+    private var inheritedScale: Float = 1f
+
+    private var cornerRadius: Float = TaskCornerRadius.get(context)
+    private var fullscreenCornerRadius: Float = QuickStepContract.getWindowCornerRadius(context)
 
     constructor(context: Context?) : super(context)
     constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
@@ -51,6 +73,27 @@
                 invalidate()
             }
         }
+        MainScope().launch { viewModel.recentsFullscreenProgress.collect { invalidateOutline() } }
+        MainScope().launch {
+            viewModel.inheritedScale.collect { viewModelInheritedScale ->
+                inheritedScale = viewModelInheritedScale
+                invalidateOutline()
+            }
+        }
+
+        clipToOutline = true
+        outlineProvider =
+            object : ViewOutlineProvider() {
+                override fun getOutline(view: View, outline: Outline) {
+                    outline.setRoundRect(
+                        0,
+                        0,
+                        view.measuredWidth,
+                        view.measuredHeight,
+                        getCurrentCornerRadius()
+                    )
+                }
+            }
     }
 
     override fun onDraw(canvas: Canvas) {
@@ -60,19 +103,25 @@
         }
     }
 
-    private fun drawTransparentUiState(canvas: Canvas) {
-        canvas.drawRoundRect(
-            0f,
-            0f,
-            measuredWidth.toFloat(),
-            measuredHeight.toFloat(),
-            // TODO(b/334826840) add rounded corners
-            0f,
-            0f,
-            CLEAR_PAINT
-        )
+    override fun onConfigurationChanged(newConfig: Configuration?) {
+        super.onConfigurationChanged(newConfig)
+
+        cornerRadius = TaskCornerRadius.get(context)
+        fullscreenCornerRadius = QuickStepContract.getWindowCornerRadius(context)
+        invalidateOutline()
     }
 
+    private fun drawTransparentUiState(canvas: Canvas) {
+        canvas.drawRect(0f, 0f, measuredWidth.toFloat(), measuredHeight.toFloat(), CLEAR_PAINT)
+    }
+
+    private fun getCurrentCornerRadius() =
+        Utilities.mapRange(
+            viewModel.recentsFullscreenProgress.value,
+            cornerRadius,
+            fullscreenCornerRadius
+        ) / inheritedScale
+
     companion object {
         private val CLEAR_PAINT =
             Paint().apply { xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) }
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModel.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModel.kt
index 9925873..71bc865 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModel.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModel.kt
@@ -16,20 +16,32 @@
 
 package com.android.quickstep.task.thumbnail
 
+import com.android.quickstep.recents.viewmodel.RecentsViewData
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
+import com.android.quickstep.task.viewmodel.TaskViewData
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
 
-class TaskThumbnailViewModel {
-    private val _uiState: MutableStateFlow<TaskThumbnailUiState> =
-        MutableStateFlow(TaskThumbnailUiState.Uninitialized)
-    val uiState: StateFlow<TaskThumbnailUiState> = _uiState
+class TaskThumbnailViewModel(recentsViewData: RecentsViewData, taskViewData: TaskViewData) {
+    private val task = MutableStateFlow<TaskThumbnail?>(null)
+
+    val recentsFullscreenProgress = recentsViewData.fullscreenProgress
+    val inheritedScale =
+        combine(recentsViewData.scale, taskViewData.scale) { recentsScale, taskScale ->
+            recentsScale * taskScale
+        }
+    val uiState =
+        task.map { taskVal ->
+            when {
+                taskVal == null -> Uninitialized
+                taskVal.isRunning -> LiveTile
+                else -> Uninitialized
+            }
+        }
 
     fun bind(task: TaskThumbnail) {
-        _uiState.value =
-            if (task.isRunning) {
-                TaskThumbnailUiState.LiveTile
-            } else {
-                TaskThumbnailUiState.Uninitialized
-            }
+        this.task.value = task
     }
 }
diff --git a/quickstep/src/com/android/quickstep/task/viewmodel/TaskViewData.kt b/quickstep/src/com/android/quickstep/task/viewmodel/TaskViewData.kt
new file mode 100644
index 0000000..a8b5112
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/task/viewmodel/TaskViewData.kt
@@ -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.quickstep.task.viewmodel
+
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class TaskViewData {
+    // This is typically a View concern but it is used to invalidate rendering in other Views
+    val scale = MutableStateFlow(1f)
+}
diff --git a/quickstep/src/com/android/quickstep/util/AppPairsController.java b/quickstep/src/com/android/quickstep/util/AppPairsController.java
index a82031a..e4e2eb2 100644
--- a/quickstep/src/com/android/quickstep/util/AppPairsController.java
+++ b/quickstep/src/com/android/quickstep/util/AppPairsController.java
@@ -22,6 +22,7 @@
 import static com.android.internal.jank.Cuj.CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_TASKBAR;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_PAIR_LAUNCH;
 import static com.android.launcher3.model.data.AppInfo.PACKAGE_KEY_COMPARATOR;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_SUPPORTS_MULTI_INSTANCE;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
@@ -32,34 +33,38 @@
 
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
 import android.content.pm.LauncherApps;
-import android.content.pm.PackageManager;
 import android.util.Log;
 import android.util.Pair;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.jank.Cuj;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.allapps.AllAppsStore;
 import com.android.launcher3.apppairs.AppPairIcon;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.AppPairInfo;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.taskbar.TaskbarActivityContext;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
 import com.android.launcher3.views.ActivityContext;
 import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TaskUtils;
 import com.android.quickstep.TopTaskTracker;
 import com.android.quickstep.views.GroupedTaskView;
 import com.android.quickstep.views.TaskView;
@@ -101,6 +106,55 @@
     }
 
     /**
+     * Returns whether the specified GroupedTaskView can be saved as an app pair.
+     */
+    public boolean canSaveAppPair(TaskView taskView) {
+        if (mContext == null) {
+            // Can ignore as the activity is already destroyed
+            return false;
+        }
+
+        // Disallow saving app pairs if:
+        // - app pairs feature is not enabled
+        // - the task in question is a single task
+        // - at least one app in app pair is unpinnable
+        // - the task is not a GroupedTaskView
+        // - both tasks in the GroupedTaskView are from the same app and the app does not
+        //   support multi-instance
+        boolean hasUnpinnableApp = taskView.getTaskContainers().stream()
+                .anyMatch(att -> att != null && att.getItemInfo() != null
+                        && ((att.getItemInfo().runtimeStatusFlags
+                            & ItemInfoWithIcon.FLAG_NOT_PINNABLE) != 0));
+        if (!FeatureFlags.enableAppPairs()
+                || !taskView.containsMultipleTasks()
+                || hasUnpinnableApp
+                || !(taskView instanceof GroupedTaskView)) {
+            return false;
+        }
+
+        GroupedTaskView gtv = (GroupedTaskView) taskView;
+        List<TaskView.TaskContainer> containers = gtv.getTaskContainers();
+        ComponentKey taskKey1 = TaskUtils.getLaunchComponentKeyForTask(
+                containers.get(0).getTask().key);
+        ComponentKey taskKey2 = TaskUtils.getLaunchComponentKeyForTask(
+                containers.get(1).getTask().key);
+        AppInfo app1 = resolveAppInfoByComponent(taskKey1);
+        AppInfo app2 = resolveAppInfoByComponent(taskKey2);
+
+        if (app1 == null || app2 == null) {
+            // Disallow saving app pairs for apps that don't have a front-door in Launcher
+            return false;
+        }
+
+        if (PackageManagerHelper.isSameAppForMultiInstance(app1, app2)) {
+            if (!app1.supportsMultiInstance() || !app2.supportsMultiInstance()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
      * Creates a new app pair ItemInfo and adds it to the workspace.
      * <br>
      * We create WorkspaceItemInfos to save onto the app pair in the following way:
@@ -116,34 +170,26 @@
      */
     public void saveAppPair(GroupedTaskView gtv) {
         InteractionJankMonitorWrapper.begin(gtv, Cuj.CUJ_LAUNCHER_SAVE_APP_PAIR);
-        TaskView.TaskIdAttributeContainer[] attributes = gtv.getTaskIdAttributeContainers();
-        WorkspaceItemInfo recentsInfo1 = attributes[0].getItemInfo();
-        WorkspaceItemInfo recentsInfo2 = attributes[1].getItemInfo();
-        WorkspaceItemInfo app1 = lookupLaunchableItem(recentsInfo1.getComponentKey());
-        WorkspaceItemInfo app2 = lookupLaunchableItem(recentsInfo2.getComponentKey());
+        List<TaskView.TaskContainer> containers = gtv.getTaskContainers();
+        WorkspaceItemInfo recentsInfo1 = containers.get(0).getItemInfo();
+        WorkspaceItemInfo recentsInfo2 = containers.get(1).getItemInfo();
+        WorkspaceItemInfo app1 = resolveAppPairWorkspaceInfo(recentsInfo1);
+        WorkspaceItemInfo app2 = resolveAppPairWorkspaceInfo(recentsInfo2);
 
-        // If app lookup fails, use the WorkspaceItemInfo that we have, but try to override default
-        // intent with one from PackageManager.
-        if (app1 == null) {
-            Log.w(TAG, "Creating an app pair, but app lookup for " + recentsInfo1.title
-                    + " failed. Falling back to the WorkspaceItemInfo from Recents.");
-            app1 = convertRecentsItemToAppItem(recentsInfo1);
+        if (app1 == null || app2 == null) {
+            // This shouldn't happen if canSaveAppPair() is called above, but log an error and do
+            // not create the app pair if the workspace items can't be resolved
+            Log.w(TAG, "Failed to save app pair due to invalid apps ("
+                    + "app1=" + recentsInfo1.getComponentKey().componentName
+                    + " app2=" + recentsInfo2.getComponentKey().componentName + ")");
+            return;
         }
-        if (app2 == null) {
-            Log.w(TAG, "Creating an app pair, but app lookup for " + recentsInfo2.title
-                    + " failed. Falling back to the WorkspaceItemInfo from Recents.");
-            app2 = convertRecentsItemToAppItem(recentsInfo2);
-        }
-
-        // WorkspaceItemProcessor won't process these new ItemInfos until the next launcher restart,
-        // so update some flags now.
-        updateWorkspaceItemFlags(app1);
-        updateWorkspaceItemFlags(app2);
 
         @PersistentSnapPosition int snapPosition = gtv.getSnapPosition();
         if (!isPersistentSnapPosition(snapPosition)) {
-            // if we received an illegal snap position, log an error and do not create the app pair.
-            Log.wtf(TAG, "tried to save an app pair with illegal snapPosition " + snapPosition);
+            // If we received an illegal snap position, log an error and do not create the app pair
+            Log.wtf(TAG, "Tried to save an app pair with illegal snapPosition "
+                    + snapPosition);
             return;
         }
 
@@ -229,67 +275,38 @@
     }
 
     /**
+     * Returns an AppInfo associated with the app for the given ComponentKey, or null if no such
+     * package exists in the AllAppsStore.
+     */
+    @Nullable
+    private AppInfo resolveAppInfoByComponent(@NonNull ComponentKey key) {
+        AllAppsStore appsStore = ActivityContext.lookupContext(mContext)
+                .getAppsView().getAppsStore();
+
+        // First look up the app info in order of:
+        // - The exact activity for the recent task
+        // - The first(?) loaded activity from the package
+        AppInfo appInfo = appsStore.getApp(key);
+        if (appInfo == null) {
+            appInfo = appsStore.getApp(key, PACKAGE_KEY_COMPARATOR);
+        }
+        return appInfo;
+    }
+
+    /**
      * Creates a new launchable WorkspaceItemInfo of itemType=ITEM_TYPE_APPLICATION by looking the
      * ComponentKey up in the AllAppsStore. If no app is found, attempts a lookup by package
      * instead. If that lookup fails, returns null.
      */
     @Nullable
-    private WorkspaceItemInfo lookupLaunchableItem(@Nullable ComponentKey key) {
-        if (key == null) {
+    private WorkspaceItemInfo resolveAppPairWorkspaceInfo(
+            @NonNull WorkspaceItemInfo recentTaskInfo) {
+        // ComponentKey should never be null (see TaskView#getItemInfo)
+        AppInfo appInfo = resolveAppInfoByComponent(recentTaskInfo.getComponentKey());
+        if (appInfo == null) {
             return null;
         }
-
-        AllAppsStore appsStore = ActivityContext.lookupContext(mContext)
-                .getAppsView().getAppsStore();
-
-        // Lookup by ComponentKey
-        AppInfo appInfo = appsStore.getApp(key);
-        if (appInfo == null) {
-            // Lookup by package
-            appInfo = appsStore.getApp(key, PACKAGE_KEY_COMPARATOR);
-        }
-
-        return appInfo != null ? appInfo.makeWorkspaceItem(mContext) : null;
-    }
-
-    /**
-     * Updates flags for newly created WorkspaceItemInfos.
-     */
-    private void updateWorkspaceItemFlags(WorkspaceItemInfo wii) {
-        PackageManager pm = mContext.getPackageManager();
-        ActivityInfo ai = null;
-        try {
-            ai = pm.getActivityInfo(wii.getTargetComponent(), 0);
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.w(TAG, "PackageManager lookup failed.");
-        }
-
-        if (ai != null) {
-            wii.setNonResizeable(ai.resizeMode == ActivityInfo.RESIZE_MODE_UNRESIZEABLE);
-        }
-    }
-
-    /**
-     * Converts a WorkspaceItemInfo of itemType=ITEM_TYPE_TASK (from a Recents task) to a new
-     * WorkspaceItemInfo of itemType=ITEM_TYPE_APPLICATION.
-     */
-    private WorkspaceItemInfo convertRecentsItemToAppItem(WorkspaceItemInfo recentsItem) {
-        if (recentsItem.itemType != LauncherSettings.Favorites.ITEM_TYPE_TASK) {
-            Log.w(TAG, "Expected ItemInfo of type ITEM_TYPE_TASK, but received "
-                    + recentsItem.itemType);
-        }
-
-        WorkspaceItemInfo launchableItem = recentsItem.clone();
-        PackageManager p = mContext.getPackageManager();
-        Intent launchIntent = p.getLaunchIntentForPackage(recentsItem.getTargetPackage());
-        Log.w(TAG, "Initial intent from Recents: " + launchableItem.intent + "\n"
-                + "Intent from PackageManager: " + launchIntent);
-        if (launchIntent != null) {
-            // If lookup from PackageManager fails, just use the existing intent
-            launchableItem.intent = launchIntent;
-        }
-        launchableItem.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
-        return launchableItem;
+        return appInfo.makeWorkspaceItem(mContext);
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java b/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
index 328a727..f1e2dd7 100644
--- a/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
@@ -21,8 +21,11 @@
 import android.view.ViewGroup;
 import android.view.WindowManager;
 
+import androidx.annotation.MainThread;
+
 import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.dagger.UnfoldMain;
 import com.android.systemui.unfold.updates.RotationChangeProvider;
 
 import java.util.HashMap;
@@ -34,7 +37,7 @@
 public abstract class BaseUnfoldMoveFromCenterAnimator implements TransitionProgressListener {
 
     private final UnfoldMoveFromCenterAnimator mMoveFromCenterAnimation;
-    private final RotationChangeProvider mRotationChangeProvider;
+    @UnfoldMain private final RotationChangeProvider mRotationChangeProvider;
 
     private final Map<ViewGroup, Boolean> mOriginalClipToPadding = new HashMap<>();
     private final Map<ViewGroup, Boolean> mOriginalClipChildren = new HashMap<>();
@@ -48,7 +51,7 @@
     private Float mLastTransitionProgress = null;
 
     public BaseUnfoldMoveFromCenterAnimator(WindowManager windowManager,
-            RotationChangeProvider rotationChangeProvider) {
+            @UnfoldMain RotationChangeProvider rotationChangeProvider) {
         mMoveFromCenterAnimation = new UnfoldMoveFromCenterAnimator(windowManager,
                 new LauncherViewsMoveFromCenterTranslationApplier());
         mRotationChangeProvider = rotationChangeProvider;
@@ -143,8 +146,14 @@
     private class UnfoldMoveFromCenterRotationListener implements
             RotationChangeProvider.RotationListener {
 
+        @MainThread
         @Override
         public void onRotationChanged(@Rotation int newRotation) {
+            onRotationChangedInternal(newRotation);
+        }
+
+        @MainThread
+        private void onRotationChangedInternal(@Rotation int newRotation) {
             mMoveFromCenterAnimation.updateDisplayProperties(newRotation);
             updateRegisteredViewsIfNeeded();
         }
diff --git a/quickstep/src/com/android/quickstep/util/BorderAnimator.kt b/quickstep/src/com/android/quickstep/util/BorderAnimator.kt
index 44eb070..85238ed 100644
--- a/quickstep/src/com/android/quickstep/util/BorderAnimator.kt
+++ b/quickstep/src/com/android/quickstep/util/BorderAnimator.kt
@@ -86,7 +86,7 @@
         fun createSimpleBorderAnimator(
             @Px borderRadiusPx: Int,
             @Px borderWidthPx: Int,
-            boundsBuilder: (rect: Rect?) -> Unit,
+            boundsBuilder: (Rect) -> Unit,
             targetView: View,
             @ColorInt borderColor: Int = DEFAULT_BORDER_COLOR,
             appearanceDurationMs: Long = DEFAULT_APPEARANCE_ANIMATION_DURATION_MS,
@@ -250,7 +250,7 @@
     /** BorderAnimationParams that simply draws the border outside the bounds of the target view. */
     private class SimpleParams(
         @Px borderWidthPx: Int,
-        boundsBuilder: (rect: Rect?) -> Unit,
+        boundsBuilder: (Rect) -> Unit,
         targetView: View,
     ) : BorderAnimationParams(borderWidthPx, boundsBuilder, targetView) {
         override val alignmentAdjustmentInset = 0
diff --git a/quickstep/src/com/android/quickstep/util/DeviceConfigHelper.kt b/quickstep/src/com/android/quickstep/util/DeviceConfigHelper.kt
index 544c64d..d36dc7e 100644
--- a/quickstep/src/com/android/quickstep/util/DeviceConfigHelper.kt
+++ b/quickstep/src/com/android/quickstep/util/DeviceConfigHelper.kt
@@ -48,12 +48,14 @@
                 PropReader(
                     object : PropProvider {
                         override fun <T : Any> get(key: String, fallback: T): T {
-                            if (fallback is Int)
+                            if (fallback is Int) {
+                                allKeys.add(key)
                                 return DeviceConfig.getInt(NAMESPACE_LAUNCHER, key, fallback) as T
-                            else if (fallback is Boolean)
+                            } else if (fallback is Boolean) {
+                                allKeys.add(key)
                                 return DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, key, fallback)
                                     as T
-                            else return fallback
+                            } else return fallback
                         }
                     }
                 )
diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
index 4474f33..26668c8 100644
--- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
+++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
@@ -39,6 +39,7 @@
 import com.android.quickstep.util.unfold.PreemptiveUnfoldTransitionProgressProvider;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.dagger.UnfoldMain;
 import com.android.systemui.unfold.updates.RotationChangeProvider;
 import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
@@ -76,7 +77,7 @@
             QuickstepLauncher launcher,
             WindowManager windowManager,
             UnfoldTransitionProgressProvider unfoldTransitionProgressProvider,
-            RotationChangeProvider rotationChangeProvider) {
+            @UnfoldMain RotationChangeProvider rotationChangeProvider) {
         mLauncher = launcher;
 
         if (FeatureFlags.PREEMPTIVE_UNFOLD_ANIMATION_START.get()) {
diff --git a/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java b/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
index 5505bb3..769ccc0 100644
--- a/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
+++ b/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
@@ -565,7 +565,11 @@
                 final float bottomThreshold = deviceProfile.heightPx - padding.bottom;
 
                 if (targetRect.bottom > bottomThreshold) {
-                    tracking = TRACKING_BOTTOM;
+                    if (enableScalingRevealHomeAnimation()) {
+                        tracking = TRACKING_CENTER;
+                    } else {
+                        tracking = TRACKING_BOTTOM;
+                    }
                 } else if (targetRect.top < topThreshold) {
                     tracking = TRACKING_TOP;
                 } else {
diff --git a/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
index 1bf77f1..4513fa2 100644
--- a/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
+++ b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
@@ -17,11 +17,12 @@
 package com.android.quickstep.util
 
 import android.graphics.Matrix
+import android.graphics.Path
 import android.graphics.RectF
 import android.view.View
+import android.view.animation.PathInterpolator
 import androidx.core.graphics.transform
 import com.android.app.animation.Interpolators
-import com.android.app.animation.Interpolators.EMPHASIZED
 import com.android.app.animation.Interpolators.LINEAR
 import com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY
 import com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_WORKSPACE_STATE
@@ -53,6 +54,19 @@
         private const val MIN_ALPHA = 0f
         private const val MAX_SIZE = 1f
         private const val MIN_SIZE = 0.85f
+
+        /**
+         * Custom interpolator for both the home and wallpaper scaling. Necessary because EMPHASIZED
+         * is too aggressive, but EMPHASIZED_DECELERATE is too soft.
+         */
+        private val SCALE_INTERPOLATOR =
+            PathInterpolator(
+                Path().apply {
+                    moveTo(0f, 0f)
+                    cubicTo(0.045f, 0.0356f, 0.0975f, 0.2055f, 0.15f, 0.3952f)
+                    cubicTo(0.235f, 0.6855f, 0.235f, 1f, 1f, 1f)
+                }
+            )
     }
 
     private val animation = PendingAnimation(SCALE_DURATION_MS)
@@ -78,20 +92,20 @@
         val hotseat = launcher.hotseat
 
         // Scale the Workspace and Hotseat around the same pivot.
+        workspace.setPivotToScaleWithSelf(hotseat)
         animation.addFloat(
             workspace,
             WORKSPACE_SCALE_PROPERTY_FACTORY[SCALE_INDEX_WORKSPACE_STATE],
             MIN_SIZE,
             MAX_SIZE,
-            EMPHASIZED,
+            SCALE_INTERPOLATOR,
         )
-        workspace.setPivotToScaleWithSelf(hotseat)
         animation.addFloat(
             hotseat,
             HOTSEAT_SCALE_PROPERTY_FACTORY[SCALE_INDEX_WORKSPACE_STATE],
             MIN_SIZE,
             MAX_SIZE,
-            EMPHASIZED,
+            SCALE_INTERPOLATOR,
         )
 
         // Fade in quickly at the beginning of the animation, so the content doesn't look like it's
@@ -114,11 +128,11 @@
 
         // Match the Wallpaper animation to the rest of the content.
         val depthController = (launcher as? QuickstepLauncher)?.depthController
-        transitionConfig.setInterpolator(StateAnimationConfig.ANIM_DEPTH, EMPHASIZED)
+        transitionConfig.setInterpolator(StateAnimationConfig.ANIM_DEPTH, SCALE_INTERPOLATOR)
         depthController?.setStateWithAnimation(LauncherState.NORMAL, transitionConfig, animation)
 
         // Make sure that the contrast scrim animates correctly if needed.
-        transitionConfig.setInterpolator(StateAnimationConfig.ANIM_SCRIM_FADE, EMPHASIZED)
+        transitionConfig.setInterpolator(StateAnimationConfig.ANIM_SCRIM_FADE, SCALE_INTERPOLATOR)
         launcher.workspace.stateTransitionAnimation.setScrim(
             animation,
             LauncherState.NORMAL,
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index ee2c2e1..8243ede 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -69,7 +69,7 @@
 import com.android.quickstep.views.SplitInstructionsView
 import com.android.quickstep.views.TaskThumbnailViewDeprecated
 import com.android.quickstep.views.TaskView
-import com.android.quickstep.views.TaskView.TaskIdAttributeContainer
+import com.android.quickstep.views.TaskView.TaskContainer
 import com.android.quickstep.views.TaskViewIcon
 import com.android.wm.shell.shared.TransitionUtil
 import java.util.Optional
@@ -114,12 +114,12 @@
         } else if (splitSelectStateController.isDismissingFromSplitPair) {
             // Initiating split from overview, but on a split pair
             val taskView = taskViewSupplier.get()
-            for (container: TaskIdAttributeContainer in taskView.taskIdAttributeContainers) {
+            for (container: TaskContainer in taskView.taskContainers) {
                 if (container.task.getKey().getId() == splitSelectStateController.initialTaskId) {
                     val drawable = getDrawable(container.iconView, splitSelectSource)
                     return SplitAnimInitProps(
-                        container.thumbnailView,
-                        container.thumbnailView.thumbnail,
+                        container.thumbnailViewDeprecated,
+                        container.thumbnailViewDeprecated.thumbnail,
                         drawable!!,
                         fadeWithThumbnail = true,
                         isStagedTask = true,
@@ -134,15 +134,17 @@
         } else {
             // Initiating split from overview on fullscreen task TaskView
             val taskView = taskViewSupplier.get()
-            val drawable = getDrawable(taskView.iconView, splitSelectSource)
-            return SplitAnimInitProps(
-                taskView.thumbnail,
-                taskView.thumbnail.thumbnail,
-                drawable!!,
-                fadeWithThumbnail = true,
-                isStagedTask = true,
-                taskView.iconView.asView()
-            )
+            taskView.taskContainers.first().let {
+                val drawable = getDrawable(it.iconView, splitSelectSource)
+                return SplitAnimInitProps(
+                    it.thumbnailViewDeprecated,
+                    it.thumbnailViewDeprecated.thumbnail,
+                    drawable!!,
+                    fadeWithThumbnail = true,
+                    isStagedTask = true,
+                    iconView = it.iconView.asView()
+                )
+            }
         }
     }
 
@@ -173,14 +175,14 @@
      *   (opposite of that representing [taskIdAttributeContainer])
      */
     fun addInitialSplitFromPair(
-        taskIdAttributeContainer: TaskIdAttributeContainer,
+        taskIdAttributeContainer: TaskContainer,
         builder: PendingAnimation,
         deviceProfile: DeviceProfile,
         taskViewWidth: Int,
         taskViewHeight: Int,
         isPrimaryTaskSplitting: Boolean
     ) {
-        val thumbnail = taskIdAttributeContainer.thumbnailView
+        val thumbnail = taskIdAttributeContainer.thumbnailViewDeprecated
         val iconView: View = taskIdAttributeContainer.iconView.asView()
         builder.add(ObjectAnimator.ofFloat(thumbnail, TaskThumbnailViewDeprecated.SPLASH_ALPHA, 1f))
         thumbnail.setShowSplashForSplitSelection(true)
@@ -561,8 +563,13 @@
                 // Launch split app pair animation
                 composeIconSplitLaunchAnimator(launchingIconView, info, t, finishCallback)
             } else {
-                composeFullscreenIconSplitLaunchAnimator(launchingIconView, info, t,
-                        finishCallback, appPairLaunchingAppIndex)
+                composeFullscreenIconSplitLaunchAnimator(
+                    launchingIconView,
+                    info,
+                    t,
+                    finishCallback,
+                    appPairLaunchingAppIndex
+                )
             }
         } else {
             // Fallback case: simple fade-in animation
@@ -629,18 +636,22 @@
 
     /**
      * @return -1 if [transitionInfo] contains both apps of the app pair to be animated, otherwise
-     *         the integer index corresponding to [launchingIconView]'s contents for the single app
-     *         to be animated
+     *   the integer index corresponding to [launchingIconView]'s contents for the single app to be
+     *   animated
      */
-    fun hasChangesForBothAppPairs(launchingIconView: AppPairIcon,
-                                          transitionInfo: TransitionInfo) : Int {
+    fun hasChangesForBothAppPairs(
+        launchingIconView: AppPairIcon,
+        transitionInfo: TransitionInfo
+    ): Int {
         val intent1 = launchingIconView.info.getFirstApp().intent.component?.packageName
         val intent2 = launchingIconView.info.getSecondApp().intent.component?.packageName
         var launchFullscreenAppIndex = -1
         for (change in transitionInfo.changes) {
             val taskInfo: RunningTaskInfo = change.taskInfo ?: continue
-            if (TransitionUtil.isOpeningType(change.mode) &&
-                    taskInfo.windowingMode == WINDOWING_MODE_FULLSCREEN) {
+            if (
+                TransitionUtil.isOpeningType(change.mode) &&
+                    taskInfo.windowingMode == WINDOWING_MODE_FULLSCREEN
+            ) {
                 val baseIntent = taskInfo.baseIntent.component?.packageName
                 if (baseIntent == intent1) {
                     if (launchFullscreenAppIndex > -1) {
@@ -695,8 +706,12 @@
         // If launching an app pair from Taskbar inside of an app context (no access to Launcher),
         // use the scale-up animation
         if (launchingIconView.context is TaskbarActivityContext) {
-            composeScaleUpLaunchAnimation(transitionInfo, t, finishCallback,
-                    WINDOWING_MODE_MULTI_WINDOW)
+            composeScaleUpLaunchAnimation(
+                transitionInfo,
+                t,
+                finishCallback,
+                WINDOWING_MODE_MULTI_WINDOW
+            )
             return
         }
 
@@ -767,28 +782,32 @@
         floatingView.bringToFront()
 
         launchAnimation.play(
-                getIconLaunchValueAnimator(t, dp, finishCallback, launcher, floatingView,
-                        rootCandidate))
+            getIconLaunchValueAnimator(t, dp, finishCallback, launcher, floatingView, rootCandidate)
+        )
         launchAnimation.start()
     }
 
     /**
-     * Similar to [composeIconSplitLaunchAnimator], but instructs [FloatingAppPairView] to animate
-     * a single fullscreen icon + background instead of for a pair
+     * Similar to [composeIconSplitLaunchAnimator], but instructs [FloatingAppPairView] to animate a
+     * single fullscreen icon + background instead of for a pair
      */
     @VisibleForTesting
     fun composeFullscreenIconSplitLaunchAnimator(
-            launchingIconView: AppPairIcon,
-            transitionInfo: TransitionInfo,
-            t: Transaction,
-            finishCallback: Runnable,
-            launchFullscreenIndex: Int
+        launchingIconView: AppPairIcon,
+        transitionInfo: TransitionInfo,
+        t: Transaction,
+        finishCallback: Runnable,
+        launchFullscreenIndex: Int
     ) {
         // If launching an app pair from Taskbar inside of an app context (no access to Launcher),
         // use the scale-up animation
         if (launchingIconView.context is TaskbarActivityContext) {
-            composeScaleUpLaunchAnimation(transitionInfo, t, finishCallback,
-                    WINDOWING_MODE_FULLSCREEN)
+            composeScaleUpLaunchAnimation(
+                transitionInfo,
+                t,
+                finishCallback,
+                WINDOWING_MODE_FULLSCREEN
+            )
             return
         }
 
@@ -799,16 +818,18 @@
         // Create an AnimatorSet that will run both shell and launcher transitions together
         val launchAnimation = AnimatorSet()
 
-        val appInfo = launchingIconView.info
-                .getContents()[launchFullscreenIndex] as WorkspaceItemInfo
+        val appInfo =
+            launchingIconView.info.getContents()[launchFullscreenIndex] as WorkspaceItemInfo
         val intentToLaunch = appInfo.intent.component?.packageName
         var rootCandidate: Change? = null
         for (change in transitionInfo.changes) {
             val taskInfo: RunningTaskInfo = change.taskInfo ?: continue
             val baseIntent = taskInfo.baseIntent.component?.packageName
-            if (TransitionUtil.isOpeningType(change.mode) &&
+            if (
+                TransitionUtil.isOpeningType(change.mode) &&
                     taskInfo.windowingMode == WINDOWING_MODE_FULLSCREEN &&
-                    baseIntent == intentToLaunch) {
+                    baseIntent == intentToLaunch
+            ) {
                 rootCandidate = change
             }
         }
@@ -832,26 +853,28 @@
         appIcon.setBounds(0, 0, dp.iconSizePx, dp.iconSizePx)
 
         val floatingView =
-                FloatingAppPairView.getFloatingAppPairView(
-                        launcher,
-                        drawableArea,
-                        appIcon,
-                        null /*appIcon2*/,
-                        0 /*dividerPos*/
-                )
+            FloatingAppPairView.getFloatingAppPairView(
+                launcher,
+                drawableArea,
+                appIcon,
+                null /*appIcon2*/,
+                0 /*dividerPos*/
+            )
         floatingView.bringToFront()
         launchAnimation.play(
-                getIconLaunchValueAnimator(t, dp, finishCallback, launcher, floatingView,
-                        rootCandidate))
+            getIconLaunchValueAnimator(t, dp, finishCallback, launcher, floatingView, rootCandidate)
+        )
         launchAnimation.start()
     }
 
-    private fun getIconLaunchValueAnimator(t: Transaction,
-                                           dp: com.android.launcher3.DeviceProfile,
-                                           finishCallback: Runnable,
-                                           launcher: QuickstepLauncher,
-                                           floatingView: FloatingAppPairView,
-                                           rootCandidate: Change) : ValueAnimator {
+    private fun getIconLaunchValueAnimator(
+        t: Transaction,
+        dp: com.android.launcher3.DeviceProfile,
+        finishCallback: Runnable,
+        launcher: QuickstepLauncher,
+        floatingView: FloatingAppPairView,
+        rootCandidate: Change
+    ): ValueAnimator {
         val progressUpdater = ValueAnimator.ofFloat(0f, 1f)
         val timings = AnimUtils.getDeviceAppPairLaunchTimings(dp.isTablet)
         progressUpdater.setDuration(timings.getDuration().toLong())
@@ -860,12 +883,12 @@
         // Shell animation: the apps are revealed toward end of the launch animation
         progressUpdater.addUpdateListener { valueAnimator: ValueAnimator ->
             val progress =
-                    Interpolators.clampToProgress(
-                            Interpolators.LINEAR,
-                            valueAnimator.animatedFraction,
-                            timings.appRevealStartOffset,
-                            timings.appRevealEndOffset
-                    )
+                Interpolators.clampToProgress(
+                    Interpolators.LINEAR,
+                    valueAnimator.animatedFraction,
+                    timings.appRevealStartOffset,
+                    timings.appRevealEndOffset
+                )
 
             // Set the alpha of the shell layer (2 apps + divider)
             t.setAlpha(rootCandidate.leash, progress)
@@ -873,65 +896,65 @@
         }
 
         progressUpdater.addUpdateListener(
-                object : MultiValueUpdateListener() {
-                    var mDx =
-                            FloatProp(
-                                    floatingView.startingPosition.left,
-                                    dp.widthPx / 2f - floatingView.startingPosition.width() / 2f,
-                                    Interpolators.clampToProgress(
-                                            timings.getStagedRectXInterpolator(),
-                                            timings.stagedRectSlideStartOffset,
-                                            timings.stagedRectSlideEndOffset
-                                    )
-                            )
-                    var mDy =
-                            FloatProp(
-                                    floatingView.startingPosition.top,
-                                    dp.heightPx / 2f - floatingView.startingPosition.height() / 2f,
-                                    Interpolators.clampToProgress(
-                                            Interpolators.EMPHASIZED,
-                                            timings.stagedRectSlideStartOffset,
-                                            timings.stagedRectSlideEndOffset
-                                    )
-                            )
-                    var mScaleX =
-                            FloatProp(
-                                    1f /* start */,
-                                    dp.widthPx / floatingView.startingPosition.width(),
-                                    Interpolators.clampToProgress(
-                                            Interpolators.EMPHASIZED,
-                                            timings.stagedRectSlideStartOffset,
-                                            timings.stagedRectSlideEndOffset
-                                    )
-                            )
-                    var mScaleY =
-                            FloatProp(
-                                    1f /* start */,
-                                    dp.heightPx / floatingView.startingPosition.height(),
-                                    Interpolators.clampToProgress(
-                                            Interpolators.EMPHASIZED,
-                                            timings.stagedRectSlideStartOffset,
-                                            timings.stagedRectSlideEndOffset
-                                    )
-                            )
+            object : MultiValueUpdateListener() {
+                var mDx =
+                    FloatProp(
+                        floatingView.startingPosition.left,
+                        dp.widthPx / 2f - floatingView.startingPosition.width() / 2f,
+                        Interpolators.clampToProgress(
+                            timings.getStagedRectXInterpolator(),
+                            timings.stagedRectSlideStartOffset,
+                            timings.stagedRectSlideEndOffset
+                        )
+                    )
+                var mDy =
+                    FloatProp(
+                        floatingView.startingPosition.top,
+                        dp.heightPx / 2f - floatingView.startingPosition.height() / 2f,
+                        Interpolators.clampToProgress(
+                            Interpolators.EMPHASIZED,
+                            timings.stagedRectSlideStartOffset,
+                            timings.stagedRectSlideEndOffset
+                        )
+                    )
+                var mScaleX =
+                    FloatProp(
+                        1f /* start */,
+                        dp.widthPx / floatingView.startingPosition.width(),
+                        Interpolators.clampToProgress(
+                            Interpolators.EMPHASIZED,
+                            timings.stagedRectSlideStartOffset,
+                            timings.stagedRectSlideEndOffset
+                        )
+                    )
+                var mScaleY =
+                    FloatProp(
+                        1f /* start */,
+                        dp.heightPx / floatingView.startingPosition.height(),
+                        Interpolators.clampToProgress(
+                            Interpolators.EMPHASIZED,
+                            timings.stagedRectSlideStartOffset,
+                            timings.stagedRectSlideEndOffset
+                        )
+                    )
 
-                    override fun onUpdate(percent: Float, initOnly: Boolean) {
-                        floatingView.progress = percent
-                        floatingView.x = mDx.value
-                        floatingView.y = mDy.value
-                        floatingView.scaleX = mScaleX.value
-                        floatingView.scaleY = mScaleY.value
-                        floatingView.invalidate()
-                    }
+                override fun onUpdate(percent: Float, initOnly: Boolean) {
+                    floatingView.progress = percent
+                    floatingView.x = mDx.value
+                    floatingView.y = mDy.value
+                    floatingView.scaleX = mScaleX.value
+                    floatingView.scaleY = mScaleY.value
+                    floatingView.invalidate()
                 }
+            }
         )
         progressUpdater.addListener(
-                object : AnimatorListenerAdapter() {
-                    override fun onAnimationEnd(animation: Animator) {
-                        safeRemoveViewFromDragLayer(launcher, floatingView)
-                        finishCallback.run()
-                    }
+            object : AnimatorListenerAdapter() {
+                override fun onAnimationEnd(animation: Animator) {
+                    safeRemoveViewFromDragLayer(launcher, floatingView)
+                    finishCallback.run()
                 }
+            }
         )
 
         return progressUpdater
@@ -939,9 +962,8 @@
 
     /**
      * This is a scale-up-and-fade-in animation (34% to 100%) for launching an app in Overview when
-     * there is no visible associated tile to expand from.
-     * [windowingMode] helps determine whether we are looking for a split or a single fullscreen
-     * [Change]
+     * there is no visible associated tile to expand from. [windowingMode] helps determine whether
+     * we are looking for a split or a single fullscreen [Change]
      */
     @VisibleForTesting
     fun composeScaleUpLaunchAnimation(
@@ -1095,12 +1117,12 @@
         animator.setDuration(QuickstepTransitionManager.SPLIT_LAUNCH_DURATION.toLong())
         animator.addUpdateListener { valueAnimator: ValueAnimator ->
             val progress =
-                    Interpolators.clampToProgress(
-                            Interpolators.LINEAR,
-                            valueAnimator.animatedFraction,
-                            0.8f,
-                            1f
-                    )
+                Interpolators.clampToProgress(
+                    Interpolators.LINEAR,
+                    valueAnimator.animatedFraction,
+                    0.8f,
+                    1f
+                )
             for (leash in openingTargets) {
                 animTransaction.setAlpha(leash, progress)
             }
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
index 06edb14..8258ab8 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
@@ -38,36 +38,40 @@
 import java.io.PrintWriter
 
 /**
- * Holds/transforms/signs/seals/delivers information for the transient state of the user
- * selecting a first app to start split with and then choosing a second app.
- * This class DOES NOT associate itself with drag-and-drop split screen starts because they come
- * from the bad part of town.
+ * Holds/transforms/signs/seals/delivers information for the transient state of the user selecting a
+ * first app to start split with and then choosing a second app. This class DOES NOT associate
+ * itself with drag-and-drop split screen starts because they come from the bad part of town.
  *
  * After setting the correct fields for initial/second.* variables, this converts them into the
- * correct [PendingIntent] and [ShortcutInfo] objects where applicable and sends the necessary
- * data back via [getSplitLaunchData]. Note: there should be only one "initial" field and one
- * "second" field set, with the rest remaining null. (Exception: [Intent] and [UserHandle] are
- * always passed in together as a set, and are converted to a single [PendingIntent] or
+ * correct [PendingIntent] and [ShortcutInfo] objects where applicable and sends the necessary data
+ * back via [getSplitLaunchData]. Note: there should be only one "initial" field and one "second"
+ * field set, with the rest remaining null. (Exception: [Intent] and [UserHandle] are always passed
+ * in together as a set, and are converted to a single [PendingIntent] or
  * [ShortcutInfo]+[PendingIntent] before launch.)
  *
  * [SplitLaunchType] indicates the type of tasks/apps/intents being launched given the provided
  * state
  */
-class SplitSelectDataHolder(
-        var context: Context?
-) {
+class SplitSelectDataHolder(var context: Context?) {
     val TAG = SplitSelectDataHolder::class.simpleName
 
     /**
-     * Order of the constant indicates the order of which task/app was selected.
-     * Ex. SPLIT_TASK_SHORTCUT means primary split app identified by task, secondary is shortcut
+     * Order of the constant indicates the order of which task/app was selected. Ex.
+     * SPLIT_TASK_SHORTCUT means primary split app identified by task, secondary is shortcut
      * SPLIT_SHORTCUT_TASK means primary split app is determined by shortcut, secondary is task
      */
     companion object {
-        @IntDef(SPLIT_TASK_TASK, SPLIT_TASK_PENDINGINTENT, SPLIT_TASK_SHORTCUT,
-                SPLIT_PENDINGINTENT_TASK, SPLIT_PENDINGINTENT_PENDINGINTENT, SPLIT_SHORTCUT_TASK,
-                SPLIT_SINGLE_TASK_FULLSCREEN, SPLIT_SINGLE_INTENT_FULLSCREEN,
-                SPLIT_SINGLE_SHORTCUT_FULLSCREEN)
+        @IntDef(
+            SPLIT_TASK_TASK,
+            SPLIT_TASK_PENDINGINTENT,
+            SPLIT_TASK_SHORTCUT,
+            SPLIT_PENDINGINTENT_TASK,
+            SPLIT_PENDINGINTENT_PENDINGINTENT,
+            SPLIT_SHORTCUT_TASK,
+            SPLIT_SINGLE_TASK_FULLSCREEN,
+            SPLIT_SINGLE_INTENT_FULLSCREEN,
+            SPLIT_SINGLE_SHORTCUT_FULLSCREEN
+        )
         @Retention(AnnotationRetention.SOURCE)
         annotation class SplitLaunchType
 
@@ -84,8 +88,7 @@
         const val SPLIT_SINGLE_SHORTCUT_FULLSCREEN = 8
     }
 
-    @StagePosition
-    private var initialStagePosition: Int = STAGE_POSITION_UNDEFINED
+    @StagePosition private var initialStagePosition: Int = STAGE_POSITION_UNDEFINED
     private var itemInfo: ItemInfo? = null
     private var secondItemInfo: ItemInfo? = null
     private var splitEvent: EventEnum? = null
@@ -108,12 +111,16 @@
 
     /**
      * @param alreadyRunningTask if set to [android.app.ActivityTaskManager.INVALID_TASK_ID]
-     * then @param intent will be used to launch the initial task
+     *   then @param intent will be used to launch the initial task
      * @param intent will be ignored if @param alreadyRunningTask is set
      */
-    fun setInitialTaskSelect(intent: Intent?, @StagePosition stagePosition: Int,
-                             itemInfo: ItemInfo?, splitEvent: EventEnum?,
-                             alreadyRunningTask: Int) {
+    fun setInitialTaskSelect(
+        intent: Intent?,
+        @StagePosition stagePosition: Int,
+        itemInfo: ItemInfo?,
+        splitEvent: EventEnum?,
+        alreadyRunningTask: Int
+    ) {
         if (alreadyRunningTask != INVALID_TASK_ID) {
             initialTaskId = alreadyRunningTask
         } else {
@@ -127,15 +134,21 @@
      * To be called after first task selected from using a split shortcut from the fullscreen
      * running app.
      */
-    fun setInitialTaskSelect(info: RunningTaskInfo,
-                             @StagePosition stagePosition: Int, itemInfo: ItemInfo?,
-                             splitEvent: EventEnum?) {
+    fun setInitialTaskSelect(
+        info: RunningTaskInfo,
+        @StagePosition stagePosition: Int,
+        itemInfo: ItemInfo?,
+        splitEvent: EventEnum?
+    ) {
         initialTaskId = info.taskId
         setInitialData(stagePosition, splitEvent, itemInfo)
     }
 
-    private fun setInitialData(@StagePosition stagePosition: Int,
-                               event: EventEnum?, item: ItemInfo?) {
+    private fun setInitialData(
+        @StagePosition stagePosition: Int,
+        event: EventEnum?,
+        item: ItemInfo?
+    ) {
         itemInfo = item
         initialStagePosition = stagePosition
         splitEvent = event
@@ -143,6 +156,7 @@
 
     /**
      * To be called as soon as user selects the second task (even if animations aren't complete)
+     *
      * @param taskId The second task that will be launched.
      */
     fun setSecondTask(taskId: Int, itemInfo: ItemInfo) {
@@ -152,6 +166,7 @@
 
     /**
      * To be called as soon as user selects the second app (even if animations aren't complete)
+     *
      * @param intent The second intent that will be launched.
      * @param user The user of that intent.
      */
@@ -162,8 +177,9 @@
     }
 
     /**
-     * To be called as soon as user selects the second app (even if animations aren't complete)
-     * Sets [secondUser] from that of the pendingIntent
+     * To be called as soon as user selects the second app (even if animations aren't complete) Sets
+     * [secondUser] from that of the pendingIntent
+     *
      * @param pendingIntent The second PendingIntent that will be launched.
      */
     fun setSecondTask(pendingIntent: PendingIntent, itemInfo: ItemInfo) {
@@ -173,9 +189,9 @@
     }
 
     /**
-     * Similar to [setSecondTask] except this is to be called for widgets which can pass through
-     * an extra intent from their RemoteResponse.
-     * See [android.widget.RemoteViews.RemoteResponse.getLaunchOptions].first
+     * Similar to [setSecondTask] except this is to be called for widgets which can pass through an
+     * extra intent from their RemoteResponse. See
+     * [android.widget.RemoteViews.RemoteResponse.getLaunchOptions].first
      */
     fun setSecondWidget(pendingIntent: PendingIntent, widgetIntent: Intent?, itemInfo: ItemInfo) {
         setSecondTask(pendingIntent, itemInfo)
@@ -184,8 +200,7 @@
 
     private fun getShortcutInfo(intent: Intent?, user: UserHandle?): ShortcutInfo? {
         val intentPackage = intent?.getPackage() ?: return null
-        val shortcutId = intent.getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID)
-                ?: return null
+        val shortcutId = intent.getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID) ?: return null
         try {
             val context: Context =
                 if (user != null) {
@@ -200,9 +215,7 @@
         return null
     }
 
-    /**
-     * Converts intents to pendingIntents, associating the [user] with the intent if provided
-     */
+    /** Converts intents to pendingIntents, associating the [user] with the intent if provided */
     private fun getPendingIntent(intent: Intent?, user: UserHandle?): PendingIntent? {
         if (intent != initialIntent && intent != secondIntent) {
             throw IllegalStateException("Invalid intent to convert to PendingIntent")
@@ -211,12 +224,21 @@
         return if (intent == null) {
             null
         } else if (user != null) {
-            PendingIntent.getActivityAsUser(context, 0, intent,
-                    PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT,
-                    null /* options */, user)
+            PendingIntent.getActivityAsUser(
+                context,
+                0,
+                intent,
+                PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT,
+                null /* options */,
+                user
+            )
         } else {
-            PendingIntent.getActivity(context, 0, intent,
-                    PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT)
+            PendingIntent.getActivity(
+                context,
+                0,
+                intent,
+                PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT
+            )
         }
     }
 
@@ -224,7 +246,7 @@
      * @return [SplitLaunchData] with the necessary fields populated as determined by
      *   [SplitLaunchData.splitLaunchType]. This is to be used for launching splitscreen
      */
-    fun getSplitLaunchData() : SplitLaunchData {
+    fun getSplitLaunchData(): SplitLaunchData {
         // Convert all intents to shortcut infos to see if determine if we launch shortcut or intent
         convertIntentsToFinalTypes()
         val splitLaunchType = getSplitLaunchType()
@@ -241,7 +263,7 @@
      *   [SplitLaunchData.splitLaunchType]. This is to be used for launching an initially selected
      *   split task in fullscreen
      */
-    fun getFullscreenLaunchData() : SplitLaunchData {
+    fun getFullscreenLaunchData(): SplitLaunchData {
         // Convert all intents to shortcut infos to determine if we launch shortcut or intent
         convertIntentsToFinalTypes()
         val splitLaunchType = getFullscreenLaunchType()
@@ -249,21 +271,22 @@
         return generateSplitLaunchData(splitLaunchType)
     }
 
-    private fun generateSplitLaunchData(@SplitLaunchType splitLaunchType: Int) : SplitLaunchData {
+    private fun generateSplitLaunchData(@SplitLaunchType splitLaunchType: Int): SplitLaunchData {
         return SplitLaunchData(
-                splitLaunchType,
-                initialTaskId,
-                secondTaskId,
-                initialPendingIntent,
-                secondPendingIntent,
-                widgetSecondIntent,
-                initialUser?.identifier ?: -1,
-                secondUser?.identifier ?: -1,
-                initialShortcut,
-                secondShortcut,
-                itemInfo,
-                splitEvent,
-                initialStagePosition)
+            splitLaunchType,
+            initialTaskId,
+            secondTaskId,
+            initialPendingIntent,
+            secondPendingIntent,
+            widgetSecondIntent,
+            initialUser?.identifier ?: -1,
+            secondUser?.identifier ?: -1,
+            initialShortcut,
+            secondShortcut,
+            itemInfo,
+            splitEvent,
+            initialStagePosition
+        )
     }
 
     /**
@@ -273,8 +296,7 @@
      * Note that both [initialIntent] and [secondIntent] will be nullified on method return
      *
      * One caveat is that if [secondPendingIntent] is set, we will use that and *not* attempt to
-     * convert [secondIntent].
-     * This also leaves [widgetSecondIntent] untouched.
+     * convert [secondIntent]. This also leaves [widgetSecondIntent] untouched.
      */
     private fun convertIntentsToFinalTypes() {
         initialShortcut = getShortcutInfo(initialIntent, initialUser)
@@ -297,8 +319,8 @@
     }
 
     /**
-     * Only valid data fields at this point should be tasks, shortcuts, or pendingIntents
-     * Intents need to be converted in [convertIntentsToFinalTypes] prior to calling this method
+     * Only valid data fields at this point should be tasks, shortcuts, or pendingIntents Intents
+     * need to be converted in [convertIntentsToFinalTypes] prior to calling this method
      */
     @VisibleForTesting
     @SplitLaunchType
@@ -354,41 +376,40 @@
     }
 
     data class SplitLaunchData(
-            @SplitLaunchType
-            val splitLaunchType: Int,
-            var initialTaskId: Int = INVALID_TASK_ID,
-            var secondTaskId: Int = INVALID_TASK_ID,
-            var initialPendingIntent: PendingIntent? = null,
-            var secondPendingIntent: PendingIntent? = null,
-            var widgetSecondIntent: Intent? = null,
-            var initialUserId: Int = -1,
-            var secondUserId: Int = -1,
-            var initialShortcut: ShortcutInfo? = null,
-            var secondShortcut: ShortcutInfo? = null,
-            var itemInfo: ItemInfo? = null,
-            var splitEvent: EventEnum? = null,
-            val initialStagePosition: Int = STAGE_POSITION_UNDEFINED
+        @SplitLaunchType val splitLaunchType: Int,
+        var initialTaskId: Int = INVALID_TASK_ID,
+        var secondTaskId: Int = INVALID_TASK_ID,
+        var initialPendingIntent: PendingIntent? = null,
+        var secondPendingIntent: PendingIntent? = null,
+        var widgetSecondIntent: Intent? = null,
+        var initialUserId: Int = -1,
+        var secondUserId: Int = -1,
+        var initialShortcut: ShortcutInfo? = null,
+        var secondShortcut: ShortcutInfo? = null,
+        var itemInfo: ItemInfo? = null,
+        var splitEvent: EventEnum? = null,
+        val initialStagePosition: Int = STAGE_POSITION_UNDEFINED
     )
 
     /**
-     * @return `true` if first task has been selected and waiting for the second task to be
-     * chosen
+     * @return `true` if first task has been selected and waiting for the second task to be chosen
      */
     fun isSplitSelectActive(): Boolean {
         return isInitialTaskIntentSet() && !isSecondTaskIntentSet()
     }
 
     /**
-     * @return `true` if the first and second task have been chosen and split is waiting to
-     * be launched
+     * @return `true` if the first and second task have been chosen and split is waiting to be
+     *   launched
      */
     fun isBothSplitAppsConfirmed(): Boolean {
         return isInitialTaskIntentSet() && isSecondTaskIntentSet()
     }
 
     private fun isInitialTaskIntentSet(): Boolean {
-        return initialTaskId != INVALID_TASK_ID || initialIntent != null ||
-                initialPendingIntent != null
+        return initialTaskId != INVALID_TASK_ID ||
+            initialIntent != null ||
+            initialPendingIntent != null
     }
 
     fun getInitialTaskId(): Int {
@@ -416,8 +437,9 @@
     }
 
     private fun isSecondTaskIntentSet(): Boolean {
-        return secondTaskId != INVALID_TASK_ID || secondIntent != null
-                || secondPendingIntent != null
+        return secondTaskId != INVALID_TASK_ID ||
+            secondIntent != null ||
+            secondPendingIntent != null
     }
 
     fun resetState() {
@@ -437,7 +459,7 @@
     }
 
     fun dump(prefix: String, writer: PrintWriter) {
-        writer.println("$prefix ${javaClass.simpleName}")
+        writer.println("$prefix SplitSelectDataHolder")
         writer.println("$prefix\tinitialStagePosition= $initialStagePosition")
         writer.println("$prefix\tinitialTaskId= $initialTaskId")
         writer.println("$prefix\tsecondTaskId= $secondTaskId")
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index df1879e..7e7c794 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -983,6 +983,7 @@
         void onDestroy() {
             SystemUiProxy.INSTANCE.get(mLauncher).unregisterSplitSelectListener(
                     mSplitSelectListener);
+            mSplitSelectListener = null;
         }
 
         /**
diff --git a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
index 85d4f4b..5e42b90 100644
--- a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
@@ -136,7 +136,7 @@
             RectF startingTaskRect = new RectF();
             final FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(
                     mLauncher, mLauncher.getDragLayer(),
-                    controller.screenshotTask(runningTaskInfo.taskId).thumbnail,
+                    controller.screenshotTask(runningTaskInfo.taskId).getThumbnail(),
                     null /* icon */, startingTaskRect);
             RecentsModel.INSTANCE.get(mLauncher.getApplicationContext())
                     .getIconCache()
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index f823aff..99f10a7 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -46,7 +46,7 @@
  * when swiping up (in gesture navigation mode).
  */
 public class SwipePipToHomeAnimator extends RectFSpringAnim {
-    private static final String TAG = SwipePipToHomeAnimator.class.getSimpleName();
+    private static final String TAG = "SwipePipToHomeAnimator";
 
     private static final float END_PROGRESS = 1.0f;
 
diff --git a/quickstep/src/com/android/quickstep/util/TaskKeyByLastActiveTimeCache.java b/quickstep/src/com/android/quickstep/util/TaskKeyByLastActiveTimeCache.java
index 21c9e09..69137cc 100644
--- a/quickstep/src/com/android/quickstep/util/TaskKeyByLastActiveTimeCache.java
+++ b/quickstep/src/com/android/quickstep/util/TaskKeyByLastActiveTimeCache.java
@@ -37,7 +37,7 @@
  * @param <V> Type of object stored in the cache
  */
 public class TaskKeyByLastActiveTimeCache<V> implements TaskKeyCache<V> {
-    private static final String TAG = TaskKeyByLastActiveTimeCache.class.getSimpleName();
+    private static final String TAG = "TaskKeyByLastActiveTimeCache";
     private final AtomicInteger mMaxSize;
     private final Map<Integer, Entry<V>> mMap;
     // To sort task id by last active time
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 9da4985..49f4e5f 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -551,7 +551,7 @@
      * TaskView
      */
     public float getCurrentCornerRadius() {
-        float visibleRadius = mCurrentFullscreenParams.mCurrentDrawnCornerRadius;
+        float visibleRadius = mCurrentFullscreenParams.getCurrentDrawnCornerRadius();
         mTempPoint[0] = visibleRadius;
         mTempPoint[1] = 0;
         mInversePositionMatrix.mapVectors(mTempPoint);
diff --git a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
index 4aea1b8..a740e0b 100644
--- a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
@@ -21,6 +21,7 @@
 
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.systemui.unfold.dagger.UnfoldMain;
 import com.android.systemui.unfold.updates.RotationChangeProvider;
 
 /**
@@ -30,8 +31,9 @@
 
     private final QuickstepLauncher mLauncher;
 
-    public UnfoldMoveFromCenterHotseatAnimator(QuickstepLauncher launcher,
-            WindowManager windowManager, RotationChangeProvider rotationChangeProvider) {
+    public UnfoldMoveFromCenterHotseatAnimator(
+            QuickstepLauncher launcher, WindowManager windowManager,
+            @UnfoldMain RotationChangeProvider rotationChangeProvider) {
         super(windowManager, rotationChangeProvider);
         mLauncher = launcher;
     }
diff --git a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
index 0ec3ae0..6e330bb 100644
--- a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
@@ -22,6 +22,7 @@
 import com.android.launcher3.ShortcutAndWidgetContainer;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.systemui.unfold.dagger.UnfoldMain;
 import com.android.systemui.unfold.updates.RotationChangeProvider;
 
 /**
@@ -31,8 +32,9 @@
 
     private final QuickstepLauncher mLauncher;
 
-    public UnfoldMoveFromCenterWorkspaceAnimator(QuickstepLauncher launcher,
-            WindowManager windowManager, RotationChangeProvider rotationChangeProvider) {
+    public UnfoldMoveFromCenterWorkspaceAnimator(
+            QuickstepLauncher launcher, WindowManager windowManager,
+            @UnfoldMain RotationChangeProvider rotationChangeProvider) {
         super(windowManager, rotationChangeProvider);
         mLauncher = launcher;
     }
diff --git a/quickstep/src/com/android/quickstep/views/DesktopAppSelectView.java b/quickstep/src/com/android/quickstep/views/DesktopAppSelectView.java
deleted file mode 100644
index 6a9a268..0000000
--- a/quickstep/src/com/android/quickstep/views/DesktopAppSelectView.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * 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.
- */
-package com.android.quickstep.views;
-
-import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE;
-import static com.android.app.animation.Interpolators.LINEAR;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.LinearLayout;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.uioverrides.QuickstepLauncher;
-
-/**
- * Floating view show on launcher home screen that notifies the user that an app will be launched to
- * the desktop.
- */
-public class DesktopAppSelectView extends LinearLayout {
-
-    private static final int SHOW_INITIAL_HEIGHT_DP = 7;
-    private static final int SHOW_CONTAINER_SCALE_DURATION = 333;
-    private static final int SHOW_CONTAINER_ALPHA_DURATION = 83;
-    private static final int SHOW_CONTENT_ALPHA_DELAY = 67;
-    private static final int SHOW_CONTENT_ALPHA_DURATION = 83;
-    private static final int HIDE_DURATION = 83;
-
-    private final RecentsViewContainer mContainer;
-
-    private View mText;
-    private View mCloseButton;
-    @Nullable
-    private Runnable mOnCloseCallback;
-    private AnimatorSet mShowAnimation;
-    private Animator mHideAnimation;
-
-    public DesktopAppSelectView(Context context) {
-        this(context, null);
-    }
-
-    public DesktopAppSelectView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public DesktopAppSelectView(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public DesktopAppSelectView(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        mContainer = RecentsViewContainer.containerFromContext(context);
-    }
-
-    /**
-     * Show the popup on launcher home screen
-     *
-     * @param onCloseCallback optional callback that is called when user clicks the close button
-     * @return the created view
-     */
-    public static DesktopAppSelectView show(Launcher launcher, @Nullable Runnable onCloseCallback) {
-        DesktopAppSelectView view = (DesktopAppSelectView) launcher.getLayoutInflater().inflate(
-                R.layout.floating_desktop_app_select, launcher.getDragLayer(), false);
-        view.setOnCloseClickCallback(onCloseCallback);
-        view.show();
-        return view;
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mText = findViewById(R.id.desktop_app_select_text);
-        mCloseButton = findViewById(R.id.close_button);
-        mCloseButton.setOnClickListener(v -> {
-            if (mHideAnimation == null) {
-                hide();
-                if (mOnCloseCallback != null) {
-                    mOnCloseCallback.run();
-                }
-            }
-        });
-    }
-
-    private void show() {
-        mContainer.getDragLayer().addView(this);
-
-        // Set up initial values
-        getBackground().setAlpha(0);
-        mText.setAlpha(0);
-        mCloseButton.setAlpha(0);
-        int initialHeightPx = Utilities.dpToPx(SHOW_INITIAL_HEIGHT_DP);
-        int finalHeight = getResources().getDimensionPixelSize(
-                R.dimen.desktop_mode_floating_app_select_height);
-        float initialScale = initialHeightPx / (float) finalHeight;
-        setScaleY(initialScale);
-        setPivotY(0);
-
-        // Animate the container
-        ValueAnimator containerBackground = ValueAnimator.ofInt(0, 255);
-        containerBackground.addUpdateListener(
-                animation -> getBackground().setAlpha((Integer) animation.getAnimatedValue()));
-        containerBackground.setDuration(SHOW_CONTAINER_ALPHA_DURATION);
-        containerBackground.setInterpolator(LINEAR);
-
-        ObjectAnimator containerSize = ObjectAnimator.ofFloat(this, SCALE_Y, 1f);
-        containerSize.setDuration(SHOW_CONTAINER_SCALE_DURATION);
-        containerSize.setInterpolator(EMPHASIZED_DECELERATE);
-
-        // Animate the contents
-        ObjectAnimator textAlpha = ObjectAnimator.ofFloat(mText, ALPHA, 1);
-        ObjectAnimator buttonAlpha = ObjectAnimator.ofFloat(mCloseButton, ALPHA, 1);
-        AnimatorSet contentAlpha = new AnimatorSet();
-        contentAlpha.playTogether(textAlpha, buttonAlpha);
-        contentAlpha.setStartDelay(SHOW_CONTENT_ALPHA_DELAY);
-        contentAlpha.setDuration(SHOW_CONTENT_ALPHA_DURATION);
-        contentAlpha.setInterpolator(LINEAR);
-
-        // Start the animation
-        mShowAnimation = new AnimatorSet();
-        mShowAnimation.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                super.onAnimationEnd(animation);
-                mShowAnimation = null;
-            }
-        });
-        mShowAnimation.playTogether(containerBackground, containerSize, contentAlpha);
-        mShowAnimation.start();
-    }
-
-    /**
-     * Hide the floating view
-     */
-    public void hide() {
-        if (mShowAnimation != null) {
-            mShowAnimation.cancel();
-        }
-        mHideAnimation = ObjectAnimator.ofFloat(this, ALPHA, 0);
-        mHideAnimation.setDuration(HIDE_DURATION).setInterpolator(LINEAR);
-        mHideAnimation.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                super.onAnimationEnd(animation);
-                mContainer.getDragLayer().removeView(DesktopAppSelectView.this);
-                mHideAnimation = null;
-            }
-        });
-        mHideAnimation.start();
-    }
-
-    /**
-     * Add a callback that is called when close button is clicked
-     */
-    public void setOnCloseClickCallback(@Nullable Runnable callback) {
-        mOnCloseCallback = callback;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
deleted file mode 100644
index 81c889c..0000000
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
+++ /dev/null
@@ -1,515 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.views;
-
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
-
-import android.content.Context;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.RoundRectShape;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.desktop.DesktopRecentsTransitionController;
-import com.android.launcher3.util.CancellableTask;
-import com.android.launcher3.util.RunnableList;
-import com.android.quickstep.BaseContainerInterface;
-import com.android.quickstep.RecentsModel;
-import com.android.quickstep.TaskThumbnailCache;
-import com.android.quickstep.util.RecentsOrientedState;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.QuickStepContract;
-
-import kotlin.Unit;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * TaskView that contains all tasks that are part of the desktop.
- */
-// TODO(b/249371338): TaskView needs to be refactored to have better support for N tasks.
-public class DesktopTaskView extends TaskView {
-
-    private static final String TAG = DesktopTaskView.class.getSimpleName();
-
-    private static final boolean DEBUG = false;
-
-    @NonNull
-    private List<Task> mTasks = new ArrayList<>();
-
-    private final ArrayList<TaskThumbnailViewDeprecated> mSnapshotViews = new ArrayList<>();
-
-    /** Maps {@code taskIds} to corresponding {@link TaskThumbnailViewDeprecated}s */
-    private final SparseArray<TaskThumbnailViewDeprecated> mSnapshotViewMap = new SparseArray<>();
-
-    private final ArrayList<CancellableTask<?>> mPendingThumbnailRequests = new ArrayList<>();
-
-    private final TaskView.FullscreenDrawParams mSnapshotDrawParams;
-
-    private View mBackgroundView;
-
-    private int mChildCountAtInflation;
-
-    private final PointF mTempPointF = new PointF();
-
-    public DesktopTaskView(Context context) {
-        this(context, null);
-    }
-
-    public DesktopTaskView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public DesktopTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        mSnapshotDrawParams = new FullscreenDrawParams(context) {
-            @Override
-            public float computeTaskCornerRadius(Context context) {
-                return QuickStepContract.getWindowCornerRadius(context);
-            }
-
-            @Override
-            public float computeWindowCornerRadius(Context context) {
-                return QuickStepContract.getWindowCornerRadius(context);
-            }
-        };
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        mBackgroundView = findViewById(R.id.background);
-
-        int topMarginPx =
-                mContainer.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
-        FrameLayout.LayoutParams params = (LayoutParams) mBackgroundView.getLayoutParams();
-        params.topMargin = topMarginPx;
-        mBackgroundView.setLayoutParams(params);
-
-        float[] outerRadii = new float[8];
-        Arrays.fill(outerRadii, getTaskCornerRadius());
-        RoundRectShape shape = new RoundRectShape(outerRadii, null, null);
-        ShapeDrawable background = new ShapeDrawable(shape);
-        background.setTint(getResources().getColor(android.R.color.system_neutral2_300,
-                getContext().getTheme()));
-        mBackgroundView.setBackground(background);
-
-        Drawable icon = getResources().getDrawable(R.drawable.ic_desktop, getContext().getTheme());
-        Drawable iconBackground = getResources().getDrawable(R.drawable.bg_circle,
-                getContext().getTheme());
-        setIcon(mIconView, new LayerDrawable(new Drawable[]{iconBackground, icon}));
-
-        mChildCountAtInflation = getChildCount();
-    }
-
-    @Override
-    public Unit getThumbnailBounds(@NonNull Rect bounds, boolean relativeToDragLayer) {
-        if (relativeToDragLayer) {
-            mContainer.getDragLayer().getDescendantRectRelativeToSelf(mBackgroundView, bounds);
-        } else {
-            bounds.set(mBackgroundView.getLeft(), mBackgroundView.getTop(),
-                    mBackgroundView.getRight(),
-                    mBackgroundView.getBottom());
-        }
-        return Unit.INSTANCE;
-    }
-
-    @Override
-    public void bind(Task task, RecentsOrientedState orientedState) {
-        bind(Collections.singletonList(task), orientedState);
-    }
-
-    /**
-     * Updates this desktop task to the gives task list defined in {@code tasks}
-     */
-    public void bind(List<Task> tasks, RecentsOrientedState orientedState) {
-        if (DEBUG) {
-            StringBuilder sb = new StringBuilder();
-            sb.append("bind tasks=").append(tasks.size()).append("\n");
-            for (Task task : tasks) {
-                sb.append(" key=").append(task.key).append("\n");
-            }
-            Log.d(TAG, sb.toString());
-        }
-        cancelPendingLoadTasks();
-
-        mTasks = new ArrayList<>(tasks);
-        mSnapshotViewMap.clear();
-
-        // Ensure there are equal number of snapshot views and tasks.
-        // More tasks than views, add views. More views than tasks, remove views.
-        // TODO(b/251586230): use a ViewPool for creating TaskThumbnailViews
-        if (mSnapshotViews.size() > mTasks.size()) {
-            int diff = mSnapshotViews.size() - mTasks.size();
-            for (int i = 0; i < diff; i++) {
-                TaskThumbnailViewDeprecated snapshotView = mSnapshotViews.remove(0);
-                removeView(snapshotView);
-            }
-        } else if (mSnapshotViews.size() < mTasks.size()) {
-            int diff = mTasks.size() - mSnapshotViews.size();
-            for (int i = 0; i < diff; i++) {
-                TaskThumbnailViewDeprecated snapshotView =
-                        new TaskThumbnailViewDeprecated(getContext());
-                mSnapshotViews.add(snapshotView);
-                // Add snapshots from to position after the initial child views.
-                addView(snapshotView, mChildCountAtInflation,
-                        new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
-            }
-        }
-
-        for (int i = 0; i < mTasks.size(); i++) {
-            Task task = mTasks.get(i);
-            TaskThumbnailViewDeprecated snapshotView = mSnapshotViews.get(i);
-            snapshotView.bind(task);
-            mSnapshotViewMap.put(task.key.id, snapshotView);
-        }
-
-        updateTaskIdContainer();
-        updateTaskIdAttributeContainer();
-
-        setOrientationState(orientedState);
-    }
-
-    private void updateTaskIdContainer() {
-        mTaskIdContainer = new int[mTasks.size()];
-        for (int i = 0; i < mTasks.size(); i++) {
-            mTaskIdContainer[i] = mTasks.get(i).key.id;
-        }
-    }
-
-    private void updateTaskIdAttributeContainer() {
-        mTaskIdAttributeContainer = new TaskIdAttributeContainer[mTasks.size()];
-        for (int i = 0; i < mTasks.size(); i++) {
-            Task task = mTasks.get(i);
-            TaskThumbnailViewDeprecated thumbnailView = mSnapshotViewMap.get(task.key.id);
-            mTaskIdAttributeContainer[i] = createAttributeContainer(task, thumbnailView);
-        }
-    }
-
-    private TaskIdAttributeContainer createAttributeContainer(Task task,
-            TaskThumbnailViewDeprecated thumbnailView) {
-        return new TaskIdAttributeContainer(task, thumbnailView, mIconView,
-                STAGE_POSITION_UNDEFINED);
-    }
-
-    @Nullable
-    @Override
-    public Task getTask() {
-        // TODO(b/249371338): returning first task. This won't work well with multiple tasks.
-        return mTasks.size() > 0 ? mTasks.get(0) : null;
-    }
-
-    @Override
-    public TaskThumbnailViewDeprecated getThumbnail() {
-        // TODO(b/249371338): returning single thumbnail. This won't work well with multiple tasks.
-        Task task = getTask();
-        if (task != null) {
-            return mSnapshotViewMap.get(task.key.id);
-        }
-        // Return the place holder snapshot views. Callers expect this to be non-null
-        return mTaskThumbnailViewDeprecated;
-    }
-
-    @Override
-    public void onTaskListVisibilityChanged(boolean visible, int changes) {
-        cancelPendingLoadTasks();
-        if (visible) {
-            RecentsModel model = RecentsModel.INSTANCE.get(getContext());
-            TaskThumbnailCache thumbnailCache = model.getThumbnailCache();
-
-            if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
-                for (Task task : mTasks) {
-                    CancellableTask<?> thumbLoadRequest =
-                            thumbnailCache.updateThumbnailInBackground(task, thumbnailData -> {
-                                TaskThumbnailViewDeprecated thumbnailView =
-                                        mSnapshotViewMap.get(task.key.id);
-                                if (thumbnailView != null) {
-                                    thumbnailView.setThumbnail(task, thumbnailData);
-                                }
-                            });
-                    if (thumbLoadRequest != null) {
-                        mPendingThumbnailRequests.add(thumbLoadRequest);
-                    }
-                }
-            }
-        } else {
-            if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
-                for (Task task : mTasks) {
-                    TaskThumbnailViewDeprecated thumbnailView = mSnapshotViewMap.get(task.key.id);
-                    if (thumbnailView != null) {
-                        thumbnailView.setThumbnail(null, null);
-                    }
-                    // Reset the task thumbnail ref
-                    task.thumbnail = null;
-                }
-            }
-        }
-    }
-
-    @Override
-    protected void setThumbnailOrientation(RecentsOrientedState orientationState) {
-        // no-op
-    }
-
-    @Override
-    protected void cancelPendingLoadTasks() {
-        for (CancellableTask<?> cancellableTask : mPendingThumbnailRequests) {
-            cancellableTask.cancel();
-        }
-        mPendingThumbnailRequests.clear();
-    }
-
-    @Nullable
-    @Override
-    public RunnableList launchTaskAnimated() {
-        RunnableList endCallback = new RunnableList();
-
-        RecentsView recentsView = getRecentsView();
-        DesktopRecentsTransitionController recentsController =
-                recentsView.getDesktopRecentsController();
-        if (recentsController != null) {
-            recentsController.launchDesktopFromRecents(this,
-                    success -> endCallback.executeAllAndDestroy());
-            Log.d(TAG, "launchTaskAnimated - launchDesktopFromRecents: " + Arrays.toString(
-                    getTaskIds()));
-        } else {
-            Log.d(TAG, "launchTaskAnimated - recentsController is null: " + Arrays.toString(
-                    getTaskIds()));
-        }
-
-        // Callbacks get run from recentsView for case when recents animation already running
-        recentsView.addSideTaskLaunchCallback(endCallback);
-        return endCallback;
-    }
-
-    @Override
-    public void launchTask(@NonNull Consumer<Boolean> callback, boolean isQuickswitch) {
-        launchTasks();
-        callback.accept(true);
-    }
-
-    @Override
-    void refreshThumbnails(@Nullable HashMap<Integer, ThumbnailData> thumbnailDatas) {
-        // Sets new thumbnails based on the incoming data and refreshes the rest.
-        // Create a copy of the thumbnail map, so we can track thumbnails that need refreshing.
-        SparseArray<TaskThumbnailViewDeprecated> thumbnailsToRefresh = mSnapshotViewMap.clone();
-        if (thumbnailDatas != null) {
-            for (Task task : mTasks) {
-                int key = task.key.id;
-                TaskThumbnailViewDeprecated thumbnailView = thumbnailsToRefresh.get(key);
-                ThumbnailData thumbnailData = thumbnailDatas.get(key);
-                if (thumbnailView != null && thumbnailData != null) {
-                    thumbnailView.setThumbnail(task, thumbnailData);
-                    // Remove this thumbnail from the list that should be refreshed.
-                    thumbnailsToRefresh.remove(key);
-                }
-            }
-        }
-
-        // Refresh the rest that were not updated.
-        for (int i = 0; i < thumbnailsToRefresh.size(); i++) {
-            thumbnailsToRefresh.valueAt(i).refresh();
-        }
-    }
-
-    @Override
-    public TaskThumbnailViewDeprecated[] getThumbnails() {
-        TaskThumbnailViewDeprecated[] thumbnails =
-                new TaskThumbnailViewDeprecated[mSnapshotViewMap.size()];
-        for (int i = 0; i < thumbnails.length; i++) {
-            thumbnails[i] = mSnapshotViewMap.valueAt(i);
-        }
-        return thumbnails;
-    }
-
-    @Override
-    public void onRecycle() {
-        resetPersistentViewTransforms();
-        // Clear any references to the thumbnail (it will be re-read either from the cache or the
-        // system on next bind)
-        for (Task task : mTasks) {
-            TaskThumbnailViewDeprecated thumbnailView = mSnapshotViewMap.get(task.key.id);
-            if (thumbnailView != null) {
-                thumbnailView.setThumbnail(task, null);
-            }
-        }
-        setOverlayEnabled(false);
-        onTaskListVisibilityChanged(false);
-        setVisibility(VISIBLE);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        int containerWidth = MeasureSpec.getSize(widthMeasureSpec);
-        int containerHeight = MeasureSpec.getSize(heightMeasureSpec);
-
-        setMeasuredDimension(containerWidth, containerHeight);
-
-        int thumbnailTopMarginPx = mContainer.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
-        containerHeight -= thumbnailTopMarginPx;
-
-        int thumbnails = mSnapshotViewMap.size();
-        if (thumbnails == 0) {
-            return;
-        }
-
-        BaseContainerInterface.getTaskDimension(mContext, mContainer.getDeviceProfile(),
-                mTempPointF);
-        int windowWidth = (int) mTempPointF.x;
-        int windowHeight = (int) mTempPointF.y;
-
-        float scaleWidth = containerWidth / (float) windowWidth;
-        float scaleHeight = containerHeight / (float) windowHeight;
-
-        if (DEBUG) {
-            Log.d(TAG,
-                    "onMeasure: container=[" + containerWidth + "," + containerHeight + "] window=["
-                            + windowWidth + "," + windowHeight + "] scale=[" + scaleWidth + ","
-                            + scaleHeight + "]");
-        }
-
-        // Desktop tile is a shrunk down version of launcher and freeform task thumbnails.
-        for (int i = 0; i < mTasks.size(); i++) {
-            Task task = mTasks.get(i);
-            Rect taskSize = task.appBounds;
-            if (taskSize == null) {
-                // Default to quarter of the desktop if we did not get app bounds.
-                taskSize = new Rect(0, 0, windowWidth / 4, windowHeight / 4);
-            }
-
-            int thumbWidth = (int) (taskSize.width() * scaleWidth);
-            int thumbHeight = (int) (taskSize.height() * scaleHeight);
-
-            TaskThumbnailViewDeprecated thumbnailView = mSnapshotViewMap.get(task.key.id);
-            if (thumbnailView != null) {
-                thumbnailView.measure(MeasureSpec.makeMeasureSpec(thumbWidth, MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(thumbHeight, MeasureSpec.EXACTLY));
-
-                // Position the task to the same position as it would be on the desktop
-                Point positionInParent = task.positionInParent;
-                if (positionInParent == null) {
-                    positionInParent = new Point(0, 0);
-                }
-                int taskX = (int) (positionInParent.x * scaleWidth);
-                int taskY = (int) (positionInParent.y * scaleHeight);
-                // move task down by margin size
-                taskY += thumbnailTopMarginPx;
-                thumbnailView.setX(taskX);
-                thumbnailView.setY(taskY);
-
-                if (DEBUG) {
-                    Log.d(TAG, "onMeasure: task=" + task.key + " thumb=[" + thumbWidth + ","
-                            + thumbHeight + "]" + " pos=[" + taskX + "," + taskY + "]");
-                }
-            }
-        }
-    }
-
-    @Override
-    public void setOverlayEnabled(boolean overlayEnabled) {
-        // TODO(b/330685808) support overlay for Screenshot action
-    }
-
-    @Override
-    public void setFullscreenProgress(float progress) {
-        // TODO(b/249371338): this copies parent implementation and makes it work for N thumbs
-        progress = Utilities.boundToRange(progress, 0, 1);
-        mFullscreenProgress = progress;
-        mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
-        if (mFullscreenProgress > 0) {
-            // Don't show background while we are transitioning to/from fullscreen
-            mBackgroundView.setVisibility(INVISIBLE);
-        } else {
-            mBackgroundView.setVisibility(VISIBLE);
-        }
-        for (int i = 0; i < mSnapshotViewMap.size(); i++) {
-            TaskThumbnailViewDeprecated thumbnailView = mSnapshotViewMap.valueAt(i);
-            thumbnailView.getTaskOverlay().setFullscreenProgress(progress);
-        }
-        // Animate icons and DWB banners in/out, except in QuickSwitch state, when tiles are
-        // oversized and banner would look disproportionately large.
-        if (mContainer.<RecentsView<?, ?>>getOverviewPanel().getStateManager().getState()
-                != BACKGROUND_APP) {
-            setIconsAndBannersTransitionProgress(progress, true);
-        }
-        updateSnapshotRadius();
-    }
-
-    @Override
-    protected void updateSnapshotRadius() {
-        super.updateSnapshotRadius();
-        for (int i = 0; i < mSnapshotViewMap.size(); i++) {
-            if (i == 0) {
-                // All snapshots share the same params. Only update it with the first snapshot.
-                updateFullscreenParams(mSnapshotDrawParams);
-            }
-            mSnapshotViewMap.valueAt(i).setFullscreenParams(mSnapshotDrawParams);
-        }
-    }
-
-    @Override
-    public void setColorTint(float amount, int tintColor) {
-        for (int i = 0; i < mSnapshotViewMap.size(); i++) {
-            mSnapshotViewMap.valueAt(i).setDimAlpha(amount);
-        }
-    }
-
-    @Override
-    protected void applyThumbnailSplashAlpha() {
-        for (int i = 0; i < mSnapshotViewMap.size(); i++) {
-            mSnapshotViewMap.valueAt(i).setSplashAlpha(mTaskThumbnailSplashAlpha);
-        }
-    }
-
-    @Override
-    void setThumbnailVisibility(int visibility, int taskId) {
-        for (int i = 0; i < mSnapshotViewMap.size(); i++) {
-            mSnapshotViewMap.valueAt(i).setVisibility(visibility);
-        }
-    }
-
-    @Override
-    protected boolean confirmSecondSplitSelectApp() {
-        // Desktop tile can't be in split screen
-        return false;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
new file mode 100644
index 0000000..936f6a1
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.views
+
+import android.content.Context
+import android.graphics.Point
+import android.graphics.PointF
+import android.graphics.Rect
+import android.graphics.drawable.LayerDrawable
+import android.graphics.drawable.ShapeDrawable
+import android.graphics.drawable.shapes.RoundRectShape
+import android.util.AttributeSet
+import android.util.Log
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.view.updateLayoutParams
+import com.android.launcher3.R
+import com.android.launcher3.util.RunnableList
+import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.launcher3.util.TransformingTouchDelegate
+import com.android.launcher3.util.ViewPool
+import com.android.launcher3.util.rects.set
+import com.android.quickstep.BaseContainerInterface
+import com.android.quickstep.TaskOverlayFactory
+import com.android.quickstep.util.RecentsOrientedState
+import com.android.systemui.shared.recents.model.Task
+
+/** TaskView that contains all tasks that are part of the desktop. */
+class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
+    TaskView(context, attrs) {
+
+    private val snapshotDrawParams =
+        object : FullscreenDrawParams(context) {
+            // DesktopTaskView thumbnail's corner radius is independent of fullscreenProgress.
+            override fun computeTaskCornerRadius(context: Context) =
+                computeWindowCornerRadius(context)
+        }
+    private val taskThumbnailViewPool =
+        ViewPool<TaskThumbnailViewDeprecated>(
+            context,
+            this,
+            R.layout.task_thumbnail,
+            VIEW_POOL_MAX_SIZE,
+            VIEW_POOL_INITIAL_SIZE
+        )
+    private val tempPointF = PointF()
+    private val tempRect = Rect()
+    private lateinit var backgroundView: View
+    private lateinit var iconView: TaskViewIcon
+    private var childCountAtInflation = 0
+
+    override fun onFinishInflate() {
+        super.onFinishInflate()
+        backgroundView =
+            findViewById<View>(R.id.background)!!.apply {
+                updateLayoutParams<LayoutParams> {
+                    topMargin = container.deviceProfile.overviewTaskThumbnailTopMarginPx
+                }
+                background =
+                    ShapeDrawable(RoundRectShape(FloatArray(8) { taskCornerRadius }, null, null))
+                        .apply {
+                            setTint(
+                                resources.getColor(
+                                    android.R.color.system_neutral2_300,
+                                    context.theme
+                                )
+                            )
+                        }
+            }
+        iconView =
+            getOrInflateIconView(R.id.icon).apply {
+                val iconBackground = resources.getDrawable(R.drawable.bg_circle, context.theme)
+                val icon = resources.getDrawable(R.drawable.ic_desktop, context.theme)
+                setIcon(this, LayerDrawable(arrayOf(iconBackground, icon)))
+            }
+        childCountAtInflation = childCount
+    }
+
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+        val containerWidth = MeasureSpec.getSize(widthMeasureSpec)
+        var containerHeight = MeasureSpec.getSize(heightMeasureSpec)
+        setMeasuredDimension(containerWidth, containerHeight)
+
+        if (taskContainers.isEmpty()) {
+            return
+        }
+
+        val thumbnailTopMarginPx = container.deviceProfile.overviewTaskThumbnailTopMarginPx
+        containerHeight -= thumbnailTopMarginPx
+
+        BaseContainerInterface.getTaskDimension(mContext, container.deviceProfile, tempPointF)
+        val windowWidth = tempPointF.x.toInt()
+        val windowHeight = tempPointF.y.toInt()
+        val scaleWidth = containerWidth / windowWidth.toFloat()
+        val scaleHeight = containerHeight / windowHeight.toFloat()
+        if (DEBUG) {
+            Log.d(
+                TAG,
+                "onMeasure: container=[$containerWidth,$containerHeight] " +
+                    "window=[$windowWidth,$windowHeight] scale=[$scaleWidth,$scaleHeight]"
+            )
+        }
+
+        // Desktop tile is a shrunk down version of launcher and freeform task thumbnails.
+        taskContainers.forEach {
+            // Default to quarter of the desktop if we did not get app bounds.
+            val taskSize =
+                it.task.appBounds
+                    ?: tempRect.apply {
+                        left = 0
+                        top = 0
+                        right = windowWidth / 4
+                        bottom = windowHeight / 4
+                    }
+            val thumbWidth = (taskSize.width() * scaleWidth).toInt()
+            val thumbHeight = (taskSize.height() * scaleHeight).toInt()
+            it.thumbnailViewDeprecated.measure(
+                MeasureSpec.makeMeasureSpec(thumbWidth, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(thumbHeight, MeasureSpec.EXACTLY)
+            )
+
+            // Position the task to the same position as it would be on the desktop
+            val positionInParent = it.task.positionInParent ?: ORIGIN
+            val taskX = (positionInParent.x * scaleWidth).toInt()
+            var taskY = (positionInParent.y * scaleHeight).toInt()
+            // move task down by margin size
+            taskY += thumbnailTopMarginPx
+            it.thumbnailViewDeprecated.x = taskX.toFloat()
+            it.thumbnailViewDeprecated.y = taskY.toFloat()
+            if (DEBUG) {
+                Log.d(
+                    TAG,
+                    "onMeasure: task=${it.task.key} thumb=[$thumbWidth,$thumbHeight]" +
+                        " pos=[$taskX,$taskY]"
+                )
+            }
+        }
+    }
+
+    override fun onRecycle() {
+        super.onRecycle()
+        visibility = VISIBLE
+    }
+
+    /** Updates this desktop task to the gives task list defined in `tasks` */
+    fun bind(
+        tasks: List<Task>,
+        orientedState: RecentsOrientedState,
+        taskOverlayFactory: TaskOverlayFactory
+    ) {
+        if (DEBUG) {
+            val sb = StringBuilder()
+            sb.append("bind tasks=").append(tasks.size).append("\n")
+            tasks.forEach { sb.append(" key=${it.key}\n") }
+            Log.d(TAG, sb.toString())
+        }
+        cancelPendingLoadTasks()
+
+        if (!isTaskContainersInitialized()) {
+            taskContainers = arrayListOf()
+        }
+        val taskContainers = taskContainers as ArrayList
+        taskContainers.ensureCapacity(tasks.size)
+        tasks.forEachIndexed { index, task ->
+            val thumbnailViewDeprecated: TaskThumbnailViewDeprecated
+            if (index >= taskContainers.size) {
+                thumbnailViewDeprecated = taskThumbnailViewPool.view
+                // Add thumbnailView from to position after the initial child views.
+                addView(
+                    thumbnailViewDeprecated,
+                    childCountAtInflation,
+                    LayoutParams(
+                        ViewGroup.LayoutParams.WRAP_CONTENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT
+                    )
+                )
+            } else {
+                thumbnailViewDeprecated = taskContainers[index].thumbnailViewDeprecated
+            }
+            val taskContainer =
+                TaskContainer(
+                        task,
+                        // TODO(b/338360089): Support new TTV for DesktopTaskView
+                        thumbnailView = null,
+                        thumbnailViewDeprecated,
+                        iconView,
+                        TransformingTouchDelegate(iconView.asView()),
+                        SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
+                        digitalWellBeingToast = null,
+                        showWindowsView = null,
+                        taskOverlayFactory
+                    )
+                    .apply { thumbnailViewDeprecated.bind(task, overlay) }
+            if (index >= taskContainers.size) {
+                taskContainers.add(taskContainer)
+            } else {
+                taskContainers[index] = taskContainer
+            }
+        }
+        repeat(taskContainers.size - tasks.size) {
+            with(taskContainers.removeLast()) {
+                removeView(thumbnailViewDeprecated)
+                taskThumbnailViewPool.recycle(thumbnailViewDeprecated)
+            }
+        }
+
+        setOrientationState(orientedState)
+    }
+
+    override fun needsUpdate(dataChange: Int, flag: Int) =
+        if (flag == FLAG_UPDATE_THUMBNAIL) super.needsUpdate(dataChange, flag) else false
+
+    // thumbnailView is laid out differently and is handled in onMeasure
+    override fun updateThumbnailSize() {}
+
+    override fun getThumbnailBounds(bounds: Rect, relativeToDragLayer: Boolean) {
+        if (relativeToDragLayer) {
+            container.dragLayer.getDescendantRectRelativeToSelf(backgroundView, bounds)
+        } else {
+            bounds.set(backgroundView)
+        }
+    }
+
+    override fun launchTaskAnimated(): RunnableList? {
+        val recentsView = recentsView ?: return null
+        val endCallback = RunnableList()
+        val desktopController = recentsView.desktopRecentsController
+        checkNotNull(desktopController) { "recentsController is null" }
+        desktopController.launchDesktopFromRecents(this) { endCallback.executeAllAndDestroy() }
+        Log.d(TAG, "launchTaskAnimated - launchDesktopFromRecents: ${taskIds.contentToString()}")
+
+        // Callbacks get run from recentsView for case when recents animation already running
+        recentsView.addSideTaskLaunchCallback(endCallback)
+        return endCallback
+    }
+
+    override fun launchTask(callback: (launched: Boolean) -> Unit, isQuickSwitch: Boolean) {
+        launchTasks()
+        callback(true)
+    }
+
+    // Desktop tile can't be in split screen
+    override fun confirmSecondSplitSelectApp(): Boolean = false
+
+    // TODO(b/330685808) support overlay for Screenshot action
+    override fun setOverlayEnabled(overlayEnabled: Boolean) {}
+
+    override fun onFullscreenProgressChanged(fullscreenProgress: Float) {
+        // Don't show background while we are transitioning to/from fullscreen
+        backgroundView.visibility = if (fullscreenProgress > 0) INVISIBLE else VISIBLE
+    }
+
+    override fun updateCurrentFullscreenParams() {
+        super.updateCurrentFullscreenParams()
+        updateFullscreenParams(snapshotDrawParams)
+    }
+
+    override fun getThumbnailFullscreenParams() = snapshotDrawParams
+
+    companion object {
+        private const val TAG = "DesktopTaskView"
+        private const val DEBUG = false
+        private const val VIEW_POOL_MAX_SIZE = 10
+        // As DesktopTaskView is inflated in background, use initialSize=0 to avoid initPool.
+        private const val VIEW_POOL_INITIAL_SIZE = 0
+        private val ORIGIN = Point(0, 0)
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
index 82ba30b..a8ebe51 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -79,7 +79,7 @@
     static final Intent OPEN_APP_USAGE_SETTINGS_TEMPLATE = new Intent(ACTION_APP_USAGE_SETTINGS);
     static final int MINUTE_MS = 60000;
 
-    private static final String TAG = DigitalWellBeingToast.class.getSimpleName();
+    private static final String TAG = "DigitalWellBeingToast";
 
     private final RecentsViewContainer mContainer;
     private final TaskView mTaskView;
@@ -320,12 +320,12 @@
                 (FrameLayout.LayoutParams) mBanner.getLayoutParams();
         DeviceProfile deviceProfile = mContainer.getDeviceProfile();
         layoutParams.bottomMargin = ((ViewGroup.MarginLayoutParams)
-                mTaskView.getThumbnail().getLayoutParams()).bottomMargin;
+                mTaskView.getFirstThumbnailViewDeprecated().getLayoutParams()).bottomMargin;
         RecentsPagedOrientationHandler orientationHandler = mTaskView.getPagedOrientationHandler();
         Pair<Float, Float> translations = orientationHandler
                 .getDwbLayoutTranslations(mTaskView.getMeasuredWidth(),
                         mTaskView.getMeasuredHeight(), mSplitBounds, deviceProfile,
-                        mTaskView.getThumbnails(), mTask.key.id, mBanner);
+                        mTaskView.getThumbnailViews(), mTask.key.id, mBanner);
         mSplitOffsetTranslationX = translations.first;
         mSplitOffsetTranslationY = translations.second;
         updateTranslationY();
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
deleted file mode 100644
index b2a8503..0000000
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ /dev/null
@@ -1,564 +0,0 @@
-package com.android.quickstep.views;
-
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-
-import static com.android.launcher3.Flags.enableOverviewIconMenu;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.quickstep.util.SplitScreenUtils.convertLauncherSplitBoundsToShell;
-
-import android.app.ActivityTaskManager;
-import android.content.Context;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.Pair;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewStub;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.jank.Cuj;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.CancellableTask;
-import com.android.launcher3.util.RunnableList;
-import com.android.launcher3.util.SplitConfigurationOptions;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
-import com.android.launcher3.util.TransformingTouchDelegate;
-import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.RecentsModel;
-import com.android.quickstep.TaskIconCache;
-import com.android.quickstep.TaskThumbnailCache;
-import com.android.quickstep.util.RecentsOrientedState;
-import com.android.quickstep.util.SplitSelectStateController;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.recents.utilities.PreviewPositionHelper;
-import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
-import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
-
-import kotlin.Unit;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Optional;
-import java.util.function.Consumer;
-
-/**
- * TaskView that contains and shows thumbnails for not one, BUT TWO(!!) tasks
- *
- * That's right. If you call within the next 5 minutes we'll go ahead and double your order and
- * send you !! TWO !! Tasks along with their TaskThumbnailViews complimentary. On. The. House.
- * And not only that, we'll even clean up your thumbnail request if you don't like it.
- * All the benefits of one TaskView, except DOUBLED!
- *
- * (Icon loading sold separately, fees may apply. Shipping & Handling for Overlays not included).
- */
-public class GroupedTaskView extends TaskView {
-
-    private static final String TAG = GroupedTaskView.class.getSimpleName();
-    @Nullable
-    private Task mSecondaryTask;
-    // TODO(b/336612373): Support new TTV for GroupedTaskView
-    private TaskThumbnailViewDeprecated mSnapshotView2;
-    private TaskViewIcon mIconView2;
-    @Nullable
-    private CancellableTask<ThumbnailData> mThumbnailLoadRequest2;
-    @Nullable
-    private CancellableTask mIconLoadRequest2;
-    private final float[] mIcon2CenterCoords = new float[2];
-    private TransformingTouchDelegate mIcon2TouchDelegate;
-    @Nullable
-    private SplitBounds mSplitBoundsConfig;
-    private final DigitalWellBeingToast mDigitalWellBeingToast2;
-
-    public GroupedTaskView(Context context) {
-        this(context, null);
-    }
-
-    public GroupedTaskView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public GroupedTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        mDigitalWellBeingToast2 = new DigitalWellBeingToast(mContainer, this);
-    }
-
-    @Override
-    public Unit getThumbnailBounds(@NonNull Rect bounds, boolean relativeToDragLayer) {
-        if (mSplitBoundsConfig == null) {
-            super.getThumbnailBounds(bounds, relativeToDragLayer);
-            return Unit.INSTANCE;
-        }
-        if (relativeToDragLayer) {
-            Rect firstThumbnailBounds = new Rect();
-            Rect secondThumbnailBounds = new Rect();
-            BaseDragLayer dragLayer = mContainer.getDragLayer();
-            dragLayer.getDescendantRectRelativeToSelf(
-                    mTaskThumbnailViewDeprecated, firstThumbnailBounds);
-            dragLayer.getDescendantRectRelativeToSelf(mSnapshotView2, secondThumbnailBounds);
-
-            bounds.set(firstThumbnailBounds);
-            bounds.union(secondThumbnailBounds);
-        } else {
-            bounds.set(getSnapshotViewBounds(mTaskThumbnailViewDeprecated));
-            bounds.union(getSnapshotViewBounds(mSnapshotView2));
-        }
-        return Unit.INSTANCE;
-    }
-
-    private Rect getSnapshotViewBounds(@NonNull View snapshotView) {
-        int snapshotViewX = Math.round(snapshotView.getX());
-        int snapshotViewY = Math.round(snapshotView.getY());
-        return new Rect(snapshotViewX,
-                snapshotViewY,
-                snapshotViewX + snapshotView.getWidth(),
-                snapshotViewY + snapshotView.getHeight());
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mSnapshotView2 = findViewById(R.id.bottomright_snapshot);
-        ViewStub iconViewStub2 = findViewById(R.id.bottomRight_icon);
-        if (enableOverviewIconMenu()) {
-            iconViewStub2.setLayoutResource(R.layout.icon_app_chip_view);
-        } else {
-            iconViewStub2.setLayoutResource(R.layout.icon_view);
-        }
-        mIconView2 = (TaskViewIcon) iconViewStub2.inflate();
-        mIcon2TouchDelegate = new TransformingTouchDelegate(mIconView2.asView());
-    }
-
-    public void bind(Task primary, Task secondary, RecentsOrientedState orientedState,
-            @Nullable SplitBounds splitBoundsConfig) {
-        super.bind(primary, orientedState);
-        mSecondaryTask = secondary;
-        mTaskIdContainer = new int[]{mTaskIdContainer[0], secondary.key.id};
-        mTaskIdAttributeContainer = new TaskIdAttributeContainer[]{
-                mTaskIdAttributeContainer[0],
-                new TaskIdAttributeContainer(secondary, mSnapshotView2,
-                        mIconView2, STAGE_POSITION_BOTTOM_OR_RIGHT)};
-        mTaskIdAttributeContainer[0].setStagePosition(
-                SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT);
-        mSnapshotView2.bind(secondary);
-        mSplitBoundsConfig = splitBoundsConfig;
-        if (mSplitBoundsConfig == null) {
-            return;
-        }
-        mTaskThumbnailViewDeprecated.getPreviewPositionHelper().setSplitBounds(
-                convertLauncherSplitBoundsToShell(splitBoundsConfig),
-                PreviewPositionHelper.STAGE_POSITION_TOP_OR_LEFT);
-        mSnapshotView2.getPreviewPositionHelper().setSplitBounds(
-                convertLauncherSplitBoundsToShell(splitBoundsConfig),
-                PreviewPositionHelper.STAGE_POSITION_BOTTOM_OR_RIGHT);
-    }
-
-    /**
-     * Sets up an on-click listener and the visibility for show_windows icon on top of each task.
-     */
-    @Override
-    public void setUpShowAllInstancesListener() {
-        // sets up the listener for the left/top task
-        super.setUpShowAllInstancesListener();
-        if (mTaskIdAttributeContainer.length < 2) {
-            return;
-        }
-
-        // right/bottom task's base package name
-        String taskPackageName = mTaskIdAttributeContainer[1].getTask().key.getPackageName();
-
-        // icon of the right/bottom task
-        View showWindowsView = findViewById(R.id.show_windows_right);
-        updateFilterCallback(showWindowsView, getFilterUpdateCallback(taskPackageName));
-    }
-
-    @Override
-    public void onTaskListVisibilityChanged(boolean visible, int changes) {
-        super.onTaskListVisibilityChanged(visible, changes);
-        if (visible) {
-            RecentsModel model = RecentsModel.INSTANCE.get(getContext());
-            TaskThumbnailCache thumbnailCache = model.getThumbnailCache();
-            TaskIconCache iconCache = model.getIconCache();
-
-            if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
-                mThumbnailLoadRequest2 = thumbnailCache.updateThumbnailInBackground(mSecondaryTask,
-                        thumbnailData -> mSnapshotView2.setThumbnail(
-                                mSecondaryTask, thumbnailData
-                        ));
-            }
-
-            if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
-                mIconLoadRequest2 = iconCache.updateIconInBackground(mSecondaryTask,
-                        (task) -> {
-                            setIcon(mIconView2, task.icon);
-                            if (enableOverviewIconMenu()) {
-                                setText(mIconView2, task.title);
-                            }
-                            mDigitalWellBeingToast2.initialize(mSecondaryTask);
-                            mDigitalWellBeingToast2.setSplitConfiguration(mSplitBoundsConfig);
-                            mDigitalWellBeingToast.setSplitConfiguration(mSplitBoundsConfig);
-                        });
-            }
-        } else {
-            if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
-                mSnapshotView2.setThumbnail(null, null);
-                // Reset the task thumbnail reference as well (it will be fetched from the cache or
-                // reloaded next time we need it)
-                mSecondaryTask.thumbnail = null;
-            }
-            if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
-                setIcon(mIconView2, null);
-                if (enableOverviewIconMenu()) {
-                    setText(mIconView2, null);
-                }
-            }
-        }
-    }
-
-    public void updateSplitBoundsConfig(SplitBounds splitBounds) {
-        mSplitBoundsConfig = splitBounds;
-        invalidate();
-    }
-
-    @Nullable
-    public SplitBounds getSplitBoundsConfig() {
-        return mSplitBoundsConfig;
-    }
-
-    /**
-     * Returns the {@link PersistentSnapPosition} of this pair of tasks.
-     */
-    public @PersistentSnapPosition int getSnapPosition() {
-        if (mSplitBoundsConfig == null) {
-            throw new IllegalStateException("mSplitBoundsConfig is null");
-        }
-
-        return mSplitBoundsConfig.snapPosition;
-    }
-
-    @Override
-    public boolean offerTouchToChildren(MotionEvent event) {
-        computeAndSetIconTouchDelegate(mIconView2, mIcon2CenterCoords, mIcon2TouchDelegate);
-        if (mIcon2TouchDelegate.onTouchEvent(event)) {
-            return true;
-        }
-
-        return super.offerTouchToChildren(event);
-    }
-
-    @Override
-    protected void cancelPendingLoadTasks() {
-        super.cancelPendingLoadTasks();
-        if (mThumbnailLoadRequest2 != null) {
-            mThumbnailLoadRequest2.cancel();
-            mThumbnailLoadRequest2 = null;
-        }
-        if (mIconLoadRequest2 != null) {
-            mIconLoadRequest2.cancel();
-            mIconLoadRequest2 = null;
-        }
-    }
-
-    @Nullable
-    @Override
-    public RunnableList launchTaskAnimated() {
-        if (mTask == null || mSecondaryTask == null) {
-            return null;
-        }
-
-        RunnableList endCallback = new RunnableList();
-        RecentsView recentsView = getRecentsView();
-        // Callbacks run from remote animation when recents animation not currently running
-        InteractionJankMonitorWrapper.begin(this, Cuj.CUJ_SPLIT_SCREEN_ENTER,
-                "Enter form GroupedTaskView");
-        launchTaskInternal(success -> {
-            endCallback.executeAllAndDestroy();
-            InteractionJankMonitorWrapper.end(Cuj.CUJ_SPLIT_SCREEN_ENTER);
-        }, false /* freezeTaskList */, true /*launchingExistingTaskview*/);
-
-        // Callbacks get run from recentsView for case when recents animation already running
-        recentsView.addSideTaskLaunchCallback(endCallback);
-        return endCallback;
-    }
-
-    @Override
-    public void launchTask(@NonNull Consumer<Boolean> callback, boolean isQuickswitch) {
-        launchTaskInternal(callback, isQuickswitch, false /*launchingExistingTaskview*/);
-    }
-
-    /**
-     * @param launchingExistingTaskView {@link SplitSelectStateController#launchExistingSplitPair}
-     * uses existence of GroupedTaskView as control flow of how to animate in the incoming task. If
-     * we're launching from overview (from overview thumbnails) then pass in {@code true},
-     * otherwise pass in {@code false} for case like quickswitching from home to task
-     */
-    private void launchTaskInternal(@NonNull Consumer<Boolean> callback, boolean isQuickswitch,
-            boolean launchingExistingTaskView) {
-        getRecentsView().getSplitSelectController().launchExistingSplitPair(
-                launchingExistingTaskView ? this : null, mTask.key.id,
-                mSecondaryTask.key.id, SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT,
-                callback, isQuickswitch, getSnapPosition());
-        Log.d(TAG, "launchTaskInternal - launchExistingSplitPair: " + Arrays.toString(
-                getTaskIds()));
-    }
-
-    @Override
-    void refreshThumbnails(@Nullable HashMap<Integer, ThumbnailData> thumbnailDatas) {
-        super.refreshThumbnails(thumbnailDatas);
-        if (mSecondaryTask != null && thumbnailDatas != null) {
-            final ThumbnailData thumbnailData = thumbnailDatas.get(mSecondaryTask.key.id);
-            if (thumbnailData != null) {
-                mSnapshotView2.setThumbnail(mSecondaryTask, thumbnailData);
-                return;
-            }
-        }
-
-        mSnapshotView2.refresh();
-    }
-
-    @Override
-    public TaskThumbnailViewDeprecated[] getThumbnails() {
-        return new TaskThumbnailViewDeprecated[]{mTaskThumbnailViewDeprecated, mSnapshotView2};
-    }
-
-    /**
-     * Returns taskId that split selection was initiated with,
-     * {@link ActivityTaskManager#INVALID_TASK_ID} if no tasks in this TaskView are part of
-     * split selection
-     */
-    protected int getThisTaskCurrentlyInSplitSelection() {
-        int initialTaskId = getRecentsView().getSplitSelectController().getInitialTaskId();
-        return containsTaskId(initialTaskId) ? initialTaskId : INVALID_TASK_ID;
-    }
-
-    @Override
-    protected int getLastSelectedChildTaskIndex() {
-        SplitSelectStateController splitSelectController =
-                getRecentsView().getSplitSelectController();
-        if (splitSelectController.isDismissingFromSplitPair()) {
-            // return the container index of the task that wasn't initially selected to split with
-            // because that is the only remaining app that can be selected. The coordinate checks
-            // below aren't reliable since both of those views may be gone/transformed
-            int initSplitTaskId = getThisTaskCurrentlyInSplitSelection();
-            if (initSplitTaskId != INVALID_TASK_ID) {
-                return initSplitTaskId == mTask.key.id ? 1 : 0;
-            }
-        }
-
-        // Check which of the two apps was selected
-        if (isCoordInView(mIconView2.asView(), mLastTouchDownPosition)
-                || isCoordInView(mSnapshotView2, mLastTouchDownPosition)) {
-            return 1;
-        }
-        return super.getLastSelectedChildTaskIndex();
-    }
-
-    private boolean isCoordInView(View v, PointF position) {
-        float[] localPos = new float[]{position.x, position.y};
-        Utilities.mapCoordInSelfToDescendant(v, this, localPos);
-        return Utilities.pointInView(v, localPos[0], localPos[1], 0f /* slop */);
-    }
-
-    @Override
-    public void onRecycle() {
-        super.onRecycle();
-        mSnapshotView2.setThumbnail(mSecondaryTask, null);
-        mSplitBoundsConfig = null;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
-        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-        setMeasuredDimension(widthSize, heightSize);
-        if (mSplitBoundsConfig == null || mTaskThumbnailViewDeprecated == null
-                || mSnapshotView2 == null) {
-            return;
-        }
-        int initSplitTaskId = getThisTaskCurrentlyInSplitSelection();
-        if (initSplitTaskId == INVALID_TASK_ID) {
-            getPagedOrientationHandler().measureGroupedTaskViewThumbnailBounds(
-                    mTaskThumbnailViewDeprecated,
-                    mSnapshotView2, widthSize, heightSize, mSplitBoundsConfig,
-                    mContainer.getDeviceProfile(), getLayoutDirection() == LAYOUT_DIRECTION_RTL);
-            // Should we be having a separate translation step apart from the measuring above?
-            // The following only applies to large screen for now, but for future reference
-            // we'd want to abstract this out in PagedViewHandlers to get the primary/secondary
-            // translation directions
-            mTaskThumbnailViewDeprecated.applySplitSelectTranslateX(
-                    mTaskThumbnailViewDeprecated.getTranslationX());
-            mTaskThumbnailViewDeprecated.applySplitSelectTranslateY(
-                    mTaskThumbnailViewDeprecated.getTranslationY());
-            mSnapshotView2.applySplitSelectTranslateX(mSnapshotView2.getTranslationX());
-            mSnapshotView2.applySplitSelectTranslateY(mSnapshotView2.getTranslationY());
-        } else {
-            // Currently being split with this taskView, let the non-split selected thumbnail
-            // take up full thumbnail area
-            Optional<TaskIdAttributeContainer> nonSplitContainer = Arrays.stream(
-                    mTaskIdAttributeContainer).filter(
-                            container -> container.getTask().key.id != initSplitTaskId).findAny();
-            nonSplitContainer.ifPresent(
-                    taskIdAttributeContainer -> taskIdAttributeContainer.getThumbnailView().measure(
-                            widthMeasureSpec, MeasureSpec.makeMeasureSpec(
-                                    heightSize - mContainer.getDeviceProfile()
-                                            .overviewTaskThumbnailTopMarginPx,
-                                    MeasureSpec.EXACTLY)));
-        }
-        if (!enableOverviewIconMenu()) {
-            updateIconPlacement();
-        }
-    }
-
-    @Override
-    public void setOverlayEnabled(boolean overlayEnabled) {
-        if (FeatureFlags.enableAppPairs()) {
-            super.setOverlayEnabled(overlayEnabled);
-        } else {
-            // Intentional no-op to prevent setting smart actions overlay on thumbnails
-        }
-    }
-
-    @Override
-    public void setOrientationState(RecentsOrientedState orientationState) {
-        DeviceProfile deviceProfile = mContainer.getDeviceProfile();
-        if (enableOverviewIconMenu() && mSplitBoundsConfig != null) {
-            ViewGroup.LayoutParams layoutParams = getLayoutParams();
-            Pair<Point, Point> groupedTaskViewSizes =
-                    orientationState.getOrientationHandler().getGroupedTaskViewSizes(
-                            deviceProfile,
-                            mSplitBoundsConfig,
-                            layoutParams.width,
-                            layoutParams.height
-                    );
-            int iconViewMarginStart = getResources().getDimensionPixelSize(
-                    R.dimen.task_thumbnail_icon_menu_expanded_top_start_margin);
-            int iconViewBackgroundMarginStart = getResources().getDimensionPixelSize(
-                    R.dimen.task_thumbnail_icon_menu_background_margin_top_start);
-            int iconMargins = (iconViewMarginStart + iconViewBackgroundMarginStart) * 2;
-            ((IconAppChipView) mIconView).setMaxWidth(groupedTaskViewSizes.first.x - iconMargins);
-            ((IconAppChipView) mIconView2).setMaxWidth(groupedTaskViewSizes.second.x - iconMargins);
-        }
-        // setMaxWidth() needs to be called before mIconView.setIconOrientation which is called in
-        // the super below.
-        super.setOrientationState(orientationState);
-
-        boolean isGridTask = deviceProfile.isTablet && !isFocusedTask();
-        mIconView2.setIconOrientation(orientationState, isGridTask);
-        updateIconPlacement();
-        updateSecondaryDwbPlacement();
-    }
-
-    private void updateIconPlacement() {
-        if (mSplitBoundsConfig == null) {
-            return;
-        }
-
-        DeviceProfile deviceProfile = mContainer.getDeviceProfile();
-        int taskIconHeight = deviceProfile.overviewTaskIconSizePx;
-        boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
-
-        if (enableOverviewIconMenu()) {
-            ViewGroup.LayoutParams layoutParams = getLayoutParams();
-            Pair<Point, Point> groupedTaskViewSizes =
-                    getPagedOrientationHandler()
-                            .getGroupedTaskViewSizes(
-                                    deviceProfile,
-                                    mSplitBoundsConfig,
-                                    layoutParams.width,
-                                    layoutParams.height
-                            );
-
-            getPagedOrientationHandler().setSplitIconParams(mIconView.asView(), mIconView2.asView(),
-                    taskIconHeight, groupedTaskViewSizes.first.x, groupedTaskViewSizes.first.y,
-                    getLayoutParams().height, getLayoutParams().width, isRtl, deviceProfile,
-                    mSplitBoundsConfig);
-        } else {
-            getPagedOrientationHandler().setSplitIconParams(mIconView.asView(), mIconView2.asView(),
-                    taskIconHeight, mTaskThumbnailViewDeprecated.getMeasuredWidth(),
-                    mTaskThumbnailViewDeprecated.getMeasuredHeight(), getMeasuredHeight(),
-                    getMeasuredWidth(),
-                    isRtl, deviceProfile, mSplitBoundsConfig);
-        }
-    }
-
-    private void updateSecondaryDwbPlacement() {
-        if (mSecondaryTask == null) {
-            return;
-        }
-        mDigitalWellBeingToast2.initialize(mSecondaryTask);
-    }
-
-    @Override
-    protected void updateSnapshotRadius() {
-        super.updateSnapshotRadius();
-        mSnapshotView2.setFullscreenParams(mCurrentFullscreenParams);
-    }
-
-    @Override
-    protected void setIconsAndBannersTransitionProgress(float progress, boolean invert) {
-        super.setIconsAndBannersTransitionProgress(progress, invert);
-        // Value set by super call
-        float scale = mIconView.getAlpha();
-        mIconView2.setContentAlpha(scale);
-        mDigitalWellBeingToast2.updateBannerOffset(1f - scale);
-    }
-
-    @Override
-    public void setColorTint(float amount, int tintColor) {
-        super.setColorTint(amount, tintColor);
-        mIconView2.setIconColorTint(tintColor, amount);
-        mSnapshotView2.setDimAlpha(amount);
-        mDigitalWellBeingToast2.setBannerColorTint(tintColor, amount);
-    }
-
-    @Override
-    protected void applyThumbnailSplashAlpha() {
-        super.applyThumbnailSplashAlpha();
-        mSnapshotView2.setSplashAlpha(mTaskThumbnailSplashAlpha);
-    }
-
-    @Override
-    protected void refreshTaskThumbnailSplash() {
-        super.refreshTaskThumbnailSplash();
-        mSnapshotView2.refreshSplashView();
-    }
-
-    @Override
-    protected void resetViewTransforms() {
-        super.resetViewTransforms();
-        mSnapshotView2.resetViewTransforms();
-    }
-
-    /**
-     * Sets visibility for thumbnails and associated elements (DWB banners).
-     * IconView is unaffected.
-     *
-     * When setting INVISIBLE, sets the visibility for the last selected child task.
-     * When setting VISIBLE (as a reset), sets the visibility for both tasks.
-     */
-    @Override
-    void setThumbnailVisibility(int visibility, int taskId) {
-        if (visibility == VISIBLE) {
-            mTaskThumbnailViewDeprecated.setVisibility(visibility);
-            mDigitalWellBeingToast.setBannerVisibility(visibility);
-            mSnapshotView2.setVisibility(visibility);
-            mDigitalWellBeingToast2.setBannerVisibility(visibility);
-        } else if (mTaskIdContainer.length > 0 && mTaskIdContainer[0] == taskId) {
-            mTaskThumbnailViewDeprecated.setVisibility(visibility);
-            mDigitalWellBeingToast.setBannerVisibility(visibility);
-        } else {
-            mSnapshotView2.setVisibility(visibility);
-            mDigitalWellBeingToast2.setBannerVisibility(visibility);
-        }
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
new file mode 100644
index 0000000..efbfa09
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
@@ -0,0 +1,346 @@
+/*
+ * 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.views
+
+import android.app.ActivityTaskManager.INVALID_TASK_ID
+import android.content.Context
+import android.graphics.PointF
+import android.util.AttributeSet
+import android.util.Log
+import android.view.View
+import com.android.internal.jank.Cuj
+import com.android.launcher3.Flags.enableOverviewIconMenu
+import com.android.launcher3.R
+import com.android.launcher3.Utilities
+import com.android.launcher3.config.FeatureFlags
+import com.android.launcher3.util.RunnableList
+import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
+import com.android.quickstep.TaskOverlayFactory
+import com.android.quickstep.util.RecentsOrientedState
+import com.android.quickstep.util.SplitScreenUtils.Companion.convertLauncherSplitBoundsToShell
+import com.android.quickstep.util.SplitSelectStateController
+import com.android.systemui.shared.recents.model.Task
+import com.android.systemui.shared.recents.utilities.PreviewPositionHelper
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper
+import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition
+
+/**
+ * TaskView that contains and shows thumbnails for not one, BUT TWO(!!) tasks
+ *
+ * That's right. If you call within the next 5 minutes we'll go ahead and double your order and send
+ * you !! TWO !! Tasks along with their TaskThumbnailViews complimentary. On. The. House. And not
+ * only that, we'll even clean up your thumbnail request if you don't like it. All the benefits of
+ * one TaskView, except DOUBLED!
+ *
+ * (Icon loading sold separately, fees may apply. Shipping & Handling for Overlays not included).
+ */
+class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
+    TaskView(context, attrs) {
+    // TODO(b/336612373): Support new TTV for GroupedTaskView
+    var splitBoundsConfig: SplitConfigurationOptions.SplitBounds? = null
+        private set
+
+    @get:PersistentSnapPosition
+    val snapPosition: Int
+        /** Returns the [PersistentSnapPosition] of this pair of tasks. */
+        get() = splitBoundsConfig?.snapPosition ?: STAGE_POSITION_UNDEFINED
+
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
+        val heightSize = MeasureSpec.getSize(heightMeasureSpec)
+        setMeasuredDimension(widthSize, heightSize)
+        val splitBoundsConfig = splitBoundsConfig ?: return
+        val initSplitTaskId = getThisTaskCurrentlyInSplitSelection()
+        if (initSplitTaskId == INVALID_TASK_ID) {
+            pagedOrientationHandler.measureGroupedTaskViewThumbnailBounds(
+                taskContainers[0].thumbnailViewDeprecated,
+                taskContainers[1].thumbnailViewDeprecated,
+                widthSize,
+                heightSize,
+                splitBoundsConfig,
+                container.deviceProfile,
+                layoutDirection == LAYOUT_DIRECTION_RTL
+            )
+            // Should we be having a separate translation step apart from the measuring above?
+            // The following only applies to large screen for now, but for future reference
+            // we'd want to abstract this out in PagedViewHandlers to get the primary/secondary
+            // translation directions
+            taskContainers[0]
+                .thumbnailViewDeprecated
+                .applySplitSelectTranslateX(taskContainers[0].thumbnailViewDeprecated.translationX)
+            taskContainers[0]
+                .thumbnailViewDeprecated
+                .applySplitSelectTranslateY(taskContainers[0].thumbnailViewDeprecated.translationY)
+            taskContainers[1]
+                .thumbnailViewDeprecated
+                .applySplitSelectTranslateX(taskContainers[1].thumbnailViewDeprecated.translationX)
+            taskContainers[1]
+                .thumbnailViewDeprecated
+                .applySplitSelectTranslateY(taskContainers[1].thumbnailViewDeprecated.translationY)
+        } else {
+            // Currently being split with this taskView, let the non-split selected thumbnail
+            // take up full thumbnail area
+            taskContainers
+                .firstOrNull { it.task.key.id != initSplitTaskId }
+                ?.thumbnailViewDeprecated
+                ?.measure(
+                    widthMeasureSpec,
+                    MeasureSpec.makeMeasureSpec(
+                        heightSize - container.deviceProfile.overviewTaskThumbnailTopMarginPx,
+                        MeasureSpec.EXACTLY
+                    )
+                )
+        }
+        if (!enableOverviewIconMenu()) {
+            updateIconPlacement()
+        }
+    }
+
+    override fun onRecycle() {
+        super.onRecycle()
+        splitBoundsConfig = null
+    }
+
+    fun bind(
+        primaryTask: Task,
+        secondaryTask: Task,
+        orientedState: RecentsOrientedState,
+        taskOverlayFactory: TaskOverlayFactory,
+        splitBoundsConfig: SplitConfigurationOptions.SplitBounds?,
+    ) {
+        cancelPendingLoadTasks()
+        taskContainers =
+            listOf(
+                createTaskContainer(
+                    primaryTask,
+                    R.id.snapshot,
+                    R.id.icon,
+                    R.id.show_windows,
+                    STAGE_POSITION_TOP_OR_LEFT,
+                    taskOverlayFactory
+                ),
+                createTaskContainer(
+                    secondaryTask,
+                    R.id.bottomright_snapshot,
+                    R.id.bottomRight_icon,
+                    R.id.show_windows_right,
+                    STAGE_POSITION_BOTTOM_OR_RIGHT,
+                    taskOverlayFactory
+                )
+            )
+        this.splitBoundsConfig =
+            splitBoundsConfig?.also {
+                taskContainers[0]
+                    .thumbnailViewDeprecated
+                    .previewPositionHelper
+                    .setSplitBounds(
+                        convertLauncherSplitBoundsToShell(it),
+                        PreviewPositionHelper.STAGE_POSITION_TOP_OR_LEFT
+                    )
+                taskContainers[1]
+                    .thumbnailViewDeprecated
+                    .previewPositionHelper
+                    .setSplitBounds(
+                        convertLauncherSplitBoundsToShell(it),
+                        PreviewPositionHelper.STAGE_POSITION_BOTTOM_OR_RIGHT
+                    )
+            }
+        setOrientationState(orientedState)
+    }
+
+    override fun setOrientationState(orientationState: RecentsOrientedState) {
+        if (enableOverviewIconMenu()) {
+            splitBoundsConfig?.let {
+                val groupedTaskViewSizes =
+                    orientationState.orientationHandler.getGroupedTaskViewSizes(
+                        container.deviceProfile,
+                        it,
+                        layoutParams.width,
+                        layoutParams.height
+                    )
+                val iconViewMarginStart =
+                    resources.getDimensionPixelSize(
+                        R.dimen.task_thumbnail_icon_menu_expanded_top_start_margin
+                    )
+                val iconViewBackgroundMarginStart =
+                    resources.getDimensionPixelSize(
+                        R.dimen.task_thumbnail_icon_menu_background_margin_top_start
+                    )
+                val iconMargins = (iconViewMarginStart + iconViewBackgroundMarginStart) * 2
+                // setMaxWidth() needs to be called before mIconView.setIconOrientation which is
+                // called in the super below.
+                (taskContainers[0].iconView as IconAppChipView).setMaxWidth(
+                    groupedTaskViewSizes.first.x - iconMargins
+                )
+                (taskContainers[1].iconView as IconAppChipView).setMaxWidth(
+                    groupedTaskViewSizes.second.x - iconMargins
+                )
+            }
+        }
+        super.setOrientationState(orientationState)
+        updateIconPlacement()
+    }
+
+    private fun updateIconPlacement() {
+        val splitBoundsConfig = splitBoundsConfig ?: return
+        val taskIconHeight = container.deviceProfile.overviewTaskIconSizePx
+        val isRtl = layoutDirection == LAYOUT_DIRECTION_RTL
+        if (enableOverviewIconMenu()) {
+            val groupedTaskViewSizes =
+                pagedOrientationHandler.getGroupedTaskViewSizes(
+                    container.deviceProfile,
+                    splitBoundsConfig,
+                    layoutParams.width,
+                    layoutParams.height
+                )
+            pagedOrientationHandler.setSplitIconParams(
+                taskContainers[0].iconView.asView(),
+                taskContainers[1].iconView.asView(),
+                taskIconHeight,
+                groupedTaskViewSizes.first.x,
+                groupedTaskViewSizes.first.y,
+                layoutParams.height,
+                layoutParams.width,
+                isRtl,
+                container.deviceProfile,
+                splitBoundsConfig
+            )
+        } else {
+            pagedOrientationHandler.setSplitIconParams(
+                taskContainers[0].iconView.asView(),
+                taskContainers[1].iconView.asView(),
+                taskIconHeight,
+                taskContainers[0].thumbnailViewDeprecated.measuredWidth,
+                taskContainers[0].thumbnailViewDeprecated.measuredHeight,
+                measuredHeight,
+                measuredWidth,
+                isRtl,
+                container.deviceProfile,
+                splitBoundsConfig
+            )
+        }
+    }
+
+    fun updateSplitBoundsConfig(splitBounds: SplitConfigurationOptions.SplitBounds?) {
+        splitBoundsConfig = splitBounds
+        invalidate()
+    }
+
+    override fun launchTaskAnimated(): RunnableList? {
+        if (taskContainers.isEmpty()) {
+            Log.d(TAG, "launchTaskAnimated - task is not bound")
+            return null
+        }
+        val recentsView = recentsView ?: return null
+        val endCallback = RunnableList()
+        // Callbacks run from remote animation when recents animation not currently running
+        InteractionJankMonitorWrapper.begin(
+            this,
+            Cuj.CUJ_SPLIT_SCREEN_ENTER,
+            "Enter form GroupedTaskView"
+        )
+        launchTaskInternal(isQuickSwitch = false, launchingExistingTaskView = true) {
+            endCallback.executeAllAndDestroy()
+            InteractionJankMonitorWrapper.end(Cuj.CUJ_SPLIT_SCREEN_ENTER)
+        }
+
+        // Callbacks get run from recentsView for case when recents animation already running
+        recentsView.addSideTaskLaunchCallback(endCallback)
+        return endCallback
+    }
+
+    override fun launchTask(callback: (launched: Boolean) -> Unit, isQuickSwitch: Boolean) {
+        launchTaskInternal(isQuickSwitch, false, callback /*launchingExistingTaskview*/)
+    }
+
+    /**
+     * @param launchingExistingTaskView [SplitSelectStateController.launchExistingSplitPair] uses
+     *   existence of GroupedTaskView as control flow of how to animate in the incoming task. If
+     *   we're launching from overview (from overview thumbnails) then pass in `true`, otherwise
+     *   pass in `false` for case like quickswitching from home to task
+     */
+    private fun launchTaskInternal(
+        isQuickSwitch: Boolean,
+        launchingExistingTaskView: Boolean,
+        callback: (launched: Boolean) -> Unit
+    ) {
+        recentsView?.let {
+            it.splitSelectController.launchExistingSplitPair(
+                if (launchingExistingTaskView) this else null,
+                taskContainers[0].task.key.id,
+                taskContainers[1].task.key.id,
+                STAGE_POSITION_TOP_OR_LEFT,
+                callback,
+                isQuickSwitch,
+                snapPosition
+            )
+            Log.d(TAG, "launchTaskInternal - launchExistingSplitPair: ${taskIds.contentToString()}")
+        }
+    }
+
+    /**
+     * Returns taskId that split selection was initiated with, [INVALID_TASK_ID] if no tasks in this
+     * TaskView are part of split selection
+     */
+    private fun getThisTaskCurrentlyInSplitSelection(): Int {
+        val initialTaskId = recentsView?.splitSelectController?.initialTaskId
+        return if (initialTaskId != null && containsTaskId(initialTaskId)) initialTaskId
+        else INVALID_TASK_ID
+    }
+
+    override fun getLastSelectedChildTaskIndex(): Int {
+        if (recentsView?.splitSelectController?.isDismissingFromSplitPair == true) {
+            // return the container index of the task that wasn't initially selected to split
+            // with because that is the only remaining app that can be selected. The coordinate
+            // checks below aren't reliable since both of those views may be gone/transformed
+            val initSplitTaskId = getThisTaskCurrentlyInSplitSelection()
+            if (initSplitTaskId != INVALID_TASK_ID) {
+                return if (initSplitTaskId == taskContainers[0].task.key.id) 1 else 0
+            }
+        }
+
+        // Check which of the two apps was selected
+        if (
+            taskContainers[1].iconView.asView().containsPoint(lastTouchDownPosition) ||
+                taskContainers[1].thumbnailViewDeprecated.containsPoint(lastTouchDownPosition)
+        ) {
+            return 1
+        }
+        return super.getLastSelectedChildTaskIndex()
+    }
+
+    private fun View.containsPoint(position: PointF): Boolean {
+        val localPos = floatArrayOf(position.x, position.y)
+        Utilities.mapCoordInSelfToDescendant(this, this@GroupedTaskView, localPos)
+        return Utilities.pointInView(this, localPos[0], localPos[1], 0f /* slop */)
+    }
+
+    override fun setOverlayEnabled(overlayEnabled: Boolean) {
+        if (FeatureFlags.enableAppPairs()) {
+            super.setOverlayEnabled(overlayEnabled)
+        } else {
+            // Intentional no-op to prevent setting smart actions overlay on thumbnails
+        }
+    }
+
+    companion object {
+        private const val TAG = "GroupedTaskView"
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 8d1907f..5284b44 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -56,6 +56,8 @@
 import com.android.quickstep.util.SplitSelectStateController;
 import com.android.systemui.shared.recents.model.Task;
 
+import kotlin.Unit;
+
 /**
  * {@link RecentsView} used in Launcher activity
  */
@@ -107,7 +109,7 @@
     }
 
     @Override
-    protected void onTaskLaunchAnimationEnd(boolean success) {
+    protected Unit onTaskLaunchAnimationEnd(boolean success) {
         if (success) {
             getStateManager().moveToRestState();
         } else {
@@ -115,6 +117,7 @@
             mContainer.getAllAppsController().setState(state);
         }
         super.onTaskLaunchAnimationEnd(success);
+        return Unit.INSTANCE;
     }
 
     @Override
@@ -143,7 +146,7 @@
 
     @Override
     public void onStateTransitionStart(LauncherState toState) {
-        setOverviewStateEnabled(toState.overviewUi);
+        setOverviewStateEnabled(toState.isRecentsViewVisible);
 
         setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mContainer.getDeviceProfile()));
         setOverviewFullscreenEnabled(toState.getOverviewFullscreenProgress() == 1);
@@ -154,7 +157,7 @@
         }
 
         // Set border after select mode changes to avoid showing border during state transition
-        if (!toState.overviewUi || toState == OVERVIEW_MODAL_TASK) {
+        if (!toState.isRecentsViewVisible || toState == OVERVIEW_MODAL_TASK) {
             setTaskBorderEnabled(false);
         }
 
@@ -178,7 +181,7 @@
             setOverviewSelectEnabled(false);
         }
 
-        if (finalState.overviewUi && finalState != OVERVIEW_MODAL_TASK) {
+        if (finalState.isRecentsViewVisible && finalState != OVERVIEW_MODAL_TASK) {
             setTaskBorderEnabled(true);
         }
 
@@ -203,7 +206,7 @@
     public boolean onTouchEvent(MotionEvent ev) {
         boolean result = super.onTouchEvent(ev);
         // Do not let touch escape to siblings below this view.
-        return result || getStateManager().getState().overviewUi;
+        return result || getStateManager().getState().isRecentsViewVisible;
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 5188d4a..83a2ceb 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -33,9 +33,8 @@
 import com.android.launcher3.Flags;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.anim.AnimatedFloat;
 import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.NavigationMode;
 import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks;
@@ -43,13 +42,13 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
 
 /**
  * View for showing action buttons in Overview
  */
 public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayout
         implements OnClickListener, Insettable {
-
     private final Rect mInsets = new Rect();
 
     @IntDef(flag = true, value = {
@@ -89,30 +88,30 @@
     private static final int INDEX_HIDDEN_FLAGS_ALPHA = 3;
     private static final int INDEX_SHARE_TARGET_ALPHA = 4;
     private static final int INDEX_SCROLL_ALPHA = 5;
-    private static final int NUM_ALPHAS = 6;
-
-    public @interface ScreenshotButtonHiddenFlags { }
-    public static final int FLAG_MULTIPLE_TASKS_HIDE_SCREENSHOT = 1 << 0;
+    private static final int INDEX_GROUPED_ALPHA = 6;
+    private static final int INDEX_3P_LAUNCHER = 7;
+    private static final int NUM_ALPHAS = 8;
 
     public @interface SplitButtonHiddenFlags { }
     public static final int FLAG_SMALL_SCREEN_HIDE_SPLIT = 1 << 0;
-    public static final int FLAG_MULTIPLE_TASKS_HIDE_SPLIT = 1 << 1;
 
-    public @interface SplitButtonDisabledFlags { }
-    public static final int FLAG_SINGLE_TASK_DISABLE_SPLIT = 1 << 0;
+    /**
+     * Holds an AnimatedFloat for each alpha property, used to set or animate alpha values in
+     * {@link #mMultiValueAlphas}.
+     */
+    private final AnimatedFloat[] mAlphaProperties = new AnimatedFloat[NUM_ALPHAS];
 
-    public @interface AppPairButtonHiddenFlags { }
-    public static final int FLAG_SINGLE_TASK_HIDE_APP_PAIR = 1 << 0;
-    public static final int FLAG_SMALL_SCREEN_HIDE_APP_PAIR = 1 << 1;
-    public static final int FLAG_3P_LAUNCHER_HIDE_APP_PAIR = 1 << 2;
+    /** Holds MultiValueAlpha values for all actions bars */
+    private final MultiValueAlpha[] mMultiValueAlphas = new MultiValueAlpha[2];
+    /** Index used for single-task actions in the mMultiValueAlphas array */
+    private static final int ACTIONS_ALPHAS = 0;
+    /** Index used for grouped-task actions in the mMultiValueAlphas array */
+    private static final int GROUP_ACTIONS_ALPHAS = 1;
 
-    private MultiValueAlpha mMultiValueAlpha;
-
+    /** Container for the action buttons below a focused, non-split Overview tile. */
     protected LinearLayout mActionButtons;
-    // The screenshot button is implemented as a Button in launcher3 and NexusLauncher, but is an
-    // ImageButton in go launcher (does not share a common class with Button). Take care when
-    // casting this.
-    private View mScreenshotButton;
+    /** Container for the action buttons below a focused, split Overview tile. */
+    protected LinearLayout mGroupActionButtons;
     private Button mSplitButton;
     private Button mSaveAppPairButton;
 
@@ -122,21 +121,17 @@
     @ActionsDisabledFlags
     protected int mDisabledFlags;
 
-    @ScreenshotButtonHiddenFlags
-    private int mScreenshotButtonHiddenFlags;
-
     @SplitButtonHiddenFlags
     private int mSplitButtonHiddenFlags;
 
-    @AppPairButtonHiddenFlags
-    private int mAppPairButtonHiddenFlags;
-
     @Nullable
     protected T mCallbacks;
 
     @Nullable
     protected DeviceProfile mDp;
     private final Rect mTaskSize = new Rect();
+    private boolean mIsGroupedTask = false;
+    private boolean mCanSaveAppPair = false;
 
     public OverviewActionsView(Context context) {
         this(context, null);
@@ -153,12 +148,31 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        // Initialize 2 view containers: one for single tasks, one for grouped tasks.
+        // These will take up the same space on the screen and alternate visibility as needed.
         mActionButtons = findViewById(R.id.action_buttons);
-        mMultiValueAlpha = new MultiValueAlpha(mActionButtons, NUM_ALPHAS);
-        mMultiValueAlpha.setUpdateVisibility(true);
+        mGroupActionButtons = findViewById(R.id.group_action_buttons);
+        // Initialize a list to hold alphas for mActionButtons and mGroupActionButtons.
+        mMultiValueAlphas[ACTIONS_ALPHAS] = new MultiValueAlpha(mActionButtons, NUM_ALPHAS);
+        mMultiValueAlphas[GROUP_ACTIONS_ALPHAS] =
+                new MultiValueAlpha(mGroupActionButtons, NUM_ALPHAS);
+        Arrays.stream(mMultiValueAlphas).forEach(a -> a.setUpdateVisibility(true));
+        // To control alpha simultaneously on mActionButtons and mGroupActionButtons, we set up an
+        // AnimatedFloat for each alpha property.
+        for (int i = 0; i < NUM_ALPHAS; i++) {
+            final int index = i;
+            mAlphaProperties[index] = new AnimatedFloat(() -> {
+                for (MultiValueAlpha multiValueAlpha : mMultiValueAlphas) {
+                    multiValueAlpha.get(index).setValue(mAlphaProperties[index].value);
+                }
+            }, 1f /* initialValue */);
+        }
 
-        mScreenshotButton = findViewById(R.id.action_screenshot);
-        mScreenshotButton.setOnClickListener(this);
+        // The screenshot button is implemented as a Button in launcher3 and NexusLauncher, but is
+        // an ImageButton in go launcher (does not share a common class with Button). Take care when
+        // casting this.
+        View screenshotButton = findViewById(R.id.action_screenshot);
+        screenshotButton.setOnClickListener(this);
         mSplitButton = findViewById(R.id.action_split);
         mSplitButton.setOnClickListener(this);
         mSaveAppPairButton = findViewById(R.id.action_save_app_pair);
@@ -209,7 +223,7 @@
             mHiddenFlags &= ~visibilityFlags;
         }
         boolean isHidden = mHiddenFlags != 0;
-        mMultiValueAlpha.get(INDEX_HIDDEN_FLAGS_ALPHA).setValue(isHidden ? 0 : 1);
+        mAlphaProperties[INDEX_HIDDEN_FLAGS_ALPHA].updateValue(isHidden ? 0 : 1);
     }
 
     /**
@@ -234,14 +248,13 @@
      * Updates a batch of flags to hide and show actions buttons when a grouped task (split screen)
      * is focused.
      * @param isGroupedTask True if the focused task is a grouped task.
+     * @param canSaveAppPair True if the focused task is a grouped task and can be saved as an app
+     *                      pair.
      */
-    public void updateForGroupedTask(boolean isGroupedTask) {
-        // Update flags to see if split button should be hidden.
-        updateSplitButtonHiddenFlags(FLAG_MULTIPLE_TASKS_HIDE_SPLIT, isGroupedTask);
-        // Update flags to see if screenshot button should be hidden.
-        updateScreenshotButtonHiddenFlags(FLAG_MULTIPLE_TASKS_HIDE_SCREENSHOT, isGroupedTask);
-        // Update flags to see if save app pair button should be hidden.
-        updateAppPairButtonHiddenFlags(FLAG_SINGLE_TASK_HIDE_APP_PAIR, !isGroupedTask);
+    public void updateForGroupedTask(boolean isGroupedTask, boolean canSaveAppPair) {
+        mIsGroupedTask = isGroupedTask;
+        mCanSaveAppPair = canSaveAppPair;
+        updateActionButtonsVisibility();
     }
 
     /**
@@ -251,36 +264,30 @@
         assert mDp != null;
         // Update flags to see if split button should be hidden.
         updateSplitButtonHiddenFlags(FLAG_SMALL_SCREEN_HIDE_SPLIT, !mDp.isTablet);
-        // Update flags to see if save app pair button should be hidden.
-        updateAppPairButtonHiddenFlags(FLAG_SMALL_SCREEN_HIDE_APP_PAIR, !mDp.isTablet);
+        updateActionButtonsVisibility();
+    }
+
+    private void updateActionButtonsVisibility() {
+        assert mDp != null;
+        boolean showSingleTaskActions = !mIsGroupedTask;
+        boolean showGroupActions = mIsGroupedTask && mDp.isTablet && mCanSaveAppPair;
+        getActionsAlphas().get(INDEX_GROUPED_ALPHA).setValue(showSingleTaskActions ? 1 : 0);
+        getGroupActionsAlphas().get(INDEX_GROUPED_ALPHA).setValue(showGroupActions ? 1 : 0);
     }
 
     /**
      * Updates flags to hide and show actions buttons for 1p/3p launchers.
      */
     public void updateFor3pLauncher(boolean is3pLauncher) {
-        updateAppPairButtonHiddenFlags(FLAG_3P_LAUNCHER_HIDE_APP_PAIR, is3pLauncher);
+        getGroupActionsAlphas().get(INDEX_3P_LAUNCHER).setValue(is3pLauncher ? 0 : 1);
     }
 
-    /**
-     * Updates the proper flags to indicate whether the "Screenshot" button should be hidden.
-     *
-     * @param flag   The flag to update.
-     * @param enable Whether to enable the hidden flag: True will cause view to be hidden.
-     */
-    private void updateScreenshotButtonHiddenFlags(@ScreenshotButtonHiddenFlags int flag,
-            boolean enable) {
-        if (mScreenshotButton == null) return;
-        if (enable) {
-            mScreenshotButtonHiddenFlags |= flag;
-        } else {
-            mScreenshotButtonHiddenFlags &= ~flag;
-        }
-        int desiredVisibility = mScreenshotButtonHiddenFlags == 0 ? VISIBLE : GONE;
-        if (mScreenshotButton.getVisibility() != desiredVisibility) {
-            mScreenshotButton.setVisibility(desiredVisibility);
-            mActionButtons.requestLayout();
-        }
+    private MultiValueAlpha getActionsAlphas() {
+        return mMultiValueAlphas[ACTIONS_ALPHAS];
+    }
+
+    private MultiValueAlpha getGroupActionsAlphas() {
+        return mMultiValueAlphas[GROUP_ACTIONS_ALPHAS];
     }
 
     /**
@@ -304,56 +311,32 @@
         }
     }
 
-    /**
-     * Updates the proper flags to indicate whether the "Save app pair" button should be disabled.
-     *
-     * @param flag   The flag to update.
-     * @param enable Whether to enable the hidden flag: True will cause view to be hidden.
-     */
-    private void updateAppPairButtonHiddenFlags(
-            @AppPairButtonHiddenFlags int flag, boolean enable) {
-        if (!FeatureFlags.enableAppPairs()) {
-            return;
-        }
-
-        if (mSaveAppPairButton == null) return;
-        if (enable) {
-            mAppPairButtonHiddenFlags |= flag;
-        } else {
-            mAppPairButtonHiddenFlags &= ~flag;
-        }
-        int desiredVisibility = mAppPairButtonHiddenFlags == 0 ? VISIBLE : GONE;
-        if (mSaveAppPairButton.getVisibility() != desiredVisibility) {
-            mSaveAppPairButton.setVisibility(desiredVisibility);
-            mActionButtons.requestLayout();
-        }
+    public AnimatedFloat getContentAlpha() {
+        return mAlphaProperties[INDEX_CONTENT_ALPHA];
     }
 
-    public MultiProperty getContentAlpha() {
-        return mMultiValueAlpha.get(INDEX_CONTENT_ALPHA);
+    public AnimatedFloat getVisibilityAlpha() {
+        return mAlphaProperties[INDEX_VISIBILITY_ALPHA];
     }
 
-    public MultiProperty getVisibilityAlpha() {
-        return mMultiValueAlpha.get(INDEX_VISIBILITY_ALPHA);
+    public AnimatedFloat getFullscreenAlpha() {
+        return mAlphaProperties[INDEX_FULLSCREEN_ALPHA];
     }
 
-    public MultiProperty getFullscreenAlpha() {
-        return mMultiValueAlpha.get(INDEX_FULLSCREEN_ALPHA);
+    public AnimatedFloat getShareTargetAlpha() {
+        return mAlphaProperties[INDEX_SHARE_TARGET_ALPHA];
     }
 
-    public MultiProperty getShareTargetAlpha() {
-        return mMultiValueAlpha.get(INDEX_SHARE_TARGET_ALPHA);
-    }
-
-    public MultiProperty getIndexScrollAlpha() {
-        return mMultiValueAlpha.get(INDEX_SCROLL_ALPHA);
+    public AnimatedFloat getIndexScrollAlpha() {
+        return mAlphaProperties[INDEX_SCROLL_ALPHA];
     }
 
     /**
      * Returns the visibility of the overview actions buttons.
      */
-    public @Visibility int getActionsButtonVisibility() {
-        return mActionButtons.getVisibility();
+    public boolean areActionsButtonsVisible() {
+        return mActionButtons.getVisibility() == View.VISIBLE
+                || mGroupActionButtons.getVisibility() == View.VISIBLE;
     }
 
     /**
@@ -366,10 +349,17 @@
 
     /** Updates vertical margins for different navigation mode or configuration changes. */
     public void updateVerticalMargin(NavigationMode mode) {
+        updateActionBarPosition(mActionButtons);
+        updateActionBarPosition(mGroupActionButtons);
+    }
+
+    /** Positions actions buttons according to device settings and insets. */
+    private void updateActionBarPosition(LinearLayout actionBar) {
         if (mDp == null) {
             return;
         }
-        LayoutParams actionParams = (LayoutParams) mActionButtons.getLayoutParams();
+
+        LayoutParams actionParams = (LayoutParams) actionBar.getLayoutParams();
         actionParams.setMargins(
                 actionParams.leftMargin, mDp.overviewActionsTopMarginPx,
                 actionParams.rightMargin, getBottomMargin());
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 077cd1b..4e5d646 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -35,6 +35,7 @@
 import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
 import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
 import static com.android.launcher3.Flags.enableGridOnlyOverview;
+import static com.android.launcher3.Flags.enableRefactorTaskThumbnail;
 import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
@@ -44,6 +45,7 @@
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.Utilities.squaredTouchSlop;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_ACTIONS_SPLIT;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_ORIENTATION_CHANGED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_CLEAR_ALL;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
@@ -144,6 +146,7 @@
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.desktop.DesktopRecentsTransitionController;
+import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.statehandlers.DepthController;
@@ -167,7 +170,6 @@
 import com.android.launcher3.util.VibratorWrapper;
 import com.android.launcher3.util.ViewPool;
 import com.android.quickstep.BaseContainerInterface;
-import com.android.quickstep.DesktopModeStatus;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.OverviewCommandHelper;
 import com.android.quickstep.RecentsAnimationController;
@@ -186,6 +188,7 @@
 import com.android.quickstep.TopTaskTracker;
 import com.android.quickstep.ViewUtils;
 import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
+import com.android.quickstep.recents.viewmodel.RecentsViewData;
 import com.android.quickstep.util.ActiveGestureErrorDetector;
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.AnimUtils;
@@ -204,7 +207,7 @@
 import com.android.quickstep.util.TaskVisualsChangeListener;
 import com.android.quickstep.util.TransformParams;
 import com.android.quickstep.util.VibrationConstants;
-import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
+import com.android.quickstep.views.TaskView.TaskContainer;
 import com.android.systemui.plugins.ResourceProvider;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -214,6 +217,9 @@
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.wm.shell.common.pip.IPipAnimationListener;
+import com.android.wm.shell.shared.DesktopModeStatus;
+
+import kotlin.Unit;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -376,6 +382,9 @@
                 public void setValue(RecentsView view, float scale) {
                     view.setScaleX(scale);
                     view.setScaleY(scale);
+                    if (enableRefactorTaskThumbnail()) {
+                        view.mRecentsViewData.getScale().setValue(scale);
+                    }
                     view.mLastComputedTaskStartPushOutDistance = null;
                     view.mLastComputedTaskEndPushOutDistance = null;
                     view.runActionOnRemoteHandles(new Consumer<RemoteTargetHandle>() {
@@ -446,6 +455,8 @@
 
     private static final float FOREGROUND_SCRIM_TINT = 0.32f;
 
+    public final RecentsViewData mRecentsViewData = new RecentsViewData();
+
     protected final RecentsOrientedState mOrientationState;
     protected final BaseContainerInterface<STATE_TYPE, CONTAINER_TYPE> mSizeStrategy;
     @Nullable
@@ -591,7 +602,7 @@
             if (taskView == null) {
                 return;
             }
-            Task.TaskKey taskKey = taskView.getTask().key;
+            Task.TaskKey taskKey = taskView.getFirstTask().key;
             UI_HELPER_EXECUTOR.execute(new CancellableTask<>(
                     () -> PackageManagerWrapper.getInstance()
                             .getActivityInfo(taskKey.getComponent(), taskKey.userId) == null,
@@ -991,12 +1002,12 @@
         if (mHandleTaskStackChanges) {
             TaskView taskView = getTaskViewByTaskId(taskId);
             if (taskView != null) {
-                for (TaskIdAttributeContainer container :
-                        taskView.getTaskIdAttributeContainers()) {
+                for (TaskContainer container : taskView.getTaskContainers()) {
                     if (container == null || taskId != container.getTask().key.id) {
                         continue;
                     }
-                    container.getThumbnailView().setThumbnail(container.getTask(), thumbnailData);
+                    container.getThumbnailViewDeprecated().setThumbnail(container.getTask(),
+                            thumbnailData);
                 }
             }
         }
@@ -1007,11 +1018,11 @@
     public void onTaskIconChanged(String pkg, UserHandle user) {
         for (int i = 0; i < getTaskViewCount(); i++) {
             TaskView tv = requireTaskViewAt(i);
-            Task task = tv.getTask();
-            if (task != null && task.key != null && pkg.equals(task.key.getPackageName())
-                    && task.key.userId == user.getIdentifier()) {
+            Task task = tv.getFirstTask();
+            if (pkg.equals(task.key.getPackageName()) && task.key.userId == user.getIdentifier()) {
                 task.icon = null;
-                if (tv.getIconView().getDrawable() != null) {
+                if (tv.getTaskContainers().stream().anyMatch(
+                        container -> container.getIconView().getDrawable() != null)) {
                     tv.onTaskListVisibilityChanged(true /* visible */);
                 }
             }
@@ -1042,13 +1053,13 @@
                 continue;
             }
             // taskView could be a GroupedTaskView, so select the relevant task by ID
-            TaskIdAttributeContainer taskAttributes = taskView.getTaskAttributesById(id);
+            TaskContainer taskAttributes = taskView.getTaskContainerById(id);
             if (taskAttributes == null) {
                 continue;
             }
             Task task = taskAttributes.getTask();
             TaskThumbnailViewDeprecated taskThumbnailViewDeprecated =
-                    taskAttributes.getThumbnailView();
+                    taskAttributes.getThumbnailViewDeprecated();
             taskThumbnailViewDeprecated.setThumbnail(task, thumbnail, refreshNow);
             // thumbnailData can contain 1-2 ids, but they should correspond to the same
             // TaskView, so overwriting is ok
@@ -1731,7 +1742,7 @@
 
         int[] currentTaskIds;
         TaskView currentTaskView = getTaskViewAt(mCurrentPage);
-        if (currentTaskView != null && currentTaskView.getTask() != null) {
+        if (currentTaskView != null) {
             currentTaskIds = currentTaskView.getTaskIds();
         } else {
             currentTaskIds = new int[0];
@@ -1761,12 +1772,12 @@
 
         // If we are entering Overview as a result of initiating a split from somewhere else
         // (e.g. split from Home), we need to make sure the staged app is not drawn as a thumbnail.
-        int stagedTaskIdToBeRemovedFromGrid;
+        int stagedTaskIdToBeRemoved;
         if (isSplitSelectionActive()) {
-            stagedTaskIdToBeRemovedFromGrid = mSplitSelectStateController.getInitialTaskId();
+            stagedTaskIdToBeRemoved = mSplitSelectStateController.getInitialTaskId();
             updateCurrentTaskActionsVisibility();
         } else {
-            stagedTaskIdToBeRemovedFromGrid = INVALID_TASK_ID;
+            stagedTaskIdToBeRemoved = INVALID_TASK_ID;
         }
         // update the map of instance counts
         mFilterState.updateInstanceCountMap(taskGroups);
@@ -1778,46 +1789,36 @@
         // taskGroups backwards populates the thumbnail grid from least recent to most recent.
         for (int i = taskGroups.size() - 1; i >= 0; i--) {
             GroupTask groupTask = taskGroups.get(i);
-            boolean isRemovalNeeded = stagedTaskIdToBeRemovedFromGrid != INVALID_TASK_ID
-                    && groupTask.containsTask(stagedTaskIdToBeRemovedFromGrid);
+            boolean isRemovalNeeded = stagedTaskIdToBeRemoved != INVALID_TASK_ID
+                    && groupTask.containsTask(stagedTaskIdToBeRemoved);
 
-            TaskView taskView;
-            if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
-                // If we need to remove half of a pair of tasks, force a TaskView with Type.SINGLE
-                // to be a temporary container for the remaining task.
-                taskView = getTaskViewFromPool(TaskView.Type.SINGLE);
-            } else {
-                taskView = getTaskViewFromPool(groupTask.taskViewType);
+            if (isRemovalNeeded && !groupTask.hasMultipleTasks()) {
+                // If the task we need to remove is not part of a pair, avoiding creating the
+                // TaskView.
+                continue;
             }
 
-            addView(taskView);
-
-            if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
-                if (groupTask.task1.key.id == stagedTaskIdToBeRemovedFromGrid) {
-                    taskView.bind(groupTask.task2, mOrientationState);
-                } else {
-                    taskView.bind(groupTask.task1, mOrientationState);
-                }
-            } else if (isRemovalNeeded) {
-                // If the task we need to remove is not part of a pair, bind it to the TaskView
-                // first (to prevent problems), then remove the whole thing.
-                taskView.bind(groupTask.task1, mOrientationState);
-                removeView(taskView);
-            } else if (taskView instanceof GroupedTaskView) {
+            // If we need to remove half of a pair of tasks, force a TaskView with Type.SINGLE
+            // to be a temporary container for the remaining task.
+            TaskView taskView = getTaskViewFromPool(
+                    isRemovalNeeded ? TaskView.Type.SINGLE : groupTask.taskViewType);
+            if (taskView instanceof GroupedTaskView) {
                 boolean firstTaskIsLeftTopTask =
                         groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id;
                 Task leftTopTask = firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2;
                 Task rightBottomTask = firstTaskIsLeftTopTask ? groupTask.task2 : groupTask.task1;
-
                 ((GroupedTaskView) taskView).bind(leftTopTask, rightBottomTask, mOrientationState,
-                        groupTask.mSplitBounds);
+                        mTaskOverlayFactory, groupTask.mSplitBounds);
             } else if (taskView instanceof DesktopTaskView) {
                 ((DesktopTaskView) taskView).bind(((DesktopTask) groupTask).tasks,
-                        mOrientationState);
+                        mOrientationState, mTaskOverlayFactory);
                 mDesktopTaskView = (DesktopTaskView) taskView;
             } else {
-                taskView.bind(groupTask.task1, mOrientationState);
+                Task task = groupTask.task1.key.id == stagedTaskIdToBeRemoved ? groupTask.task2
+                        : groupTask.task1;
+                taskView.bind(task, mOrientationState, mTaskOverlayFactory);
             }
+            addView(taskView);
 
             // enables instance filtering if the feature flag for it is on
             if (FeatureFlags.ENABLE_MULTI_INSTANCE.get()) {
@@ -2012,6 +2013,9 @@
 
     public void setFullscreenProgress(float fullscreenProgress) {
         mFullscreenProgress = fullscreenProgress;
+        if (enableRefactorTaskThumbnail()) {
+            mRecentsViewData.getFullscreenProgress().setValue(mFullscreenProgress);
+        }
         int taskCount = getTaskViewCount();
         for (int i = 0; i < taskCount; i++) {
             requireTaskViewAt(i).setFullscreenProgress(mFullscreenProgress);
@@ -2019,7 +2023,7 @@
         mClearAllButton.setFullscreenProgress(fullscreenProgress);
 
         // Fade out the actions view quickly (0.1 range)
-        mActionsView.getFullscreenAlpha().setValue(
+        mActionsView.getFullscreenAlpha().updateValue(
                 mapToRange(fullscreenProgress, 0, 0.1f, 1f, 0f, LINEAR));
     }
 
@@ -2075,12 +2079,17 @@
                 : View.LAYOUT_DIRECTION_RTL);
         mClearAllButton.setRotation(getPagedOrientationHandler().getDegreesRotated());
 
-        if (forceRecreateDragLayerControllers
-                || !getPagedOrientationHandler().equals(oldOrientationHandler)) {
+        boolean isOrientationHandlerChanged =
+                !getPagedOrientationHandler().equals(oldOrientationHandler);
+        if (forceRecreateDragLayerControllers || isOrientationHandlerChanged) {
             // Changed orientations, update controllers so they intercept accordingly.
             mContainer.getDragLayer().recreateControllers();
             onOrientationChanged();
             resetTaskVisuals();
+            // Log fake orientation changed.
+            if (isOrientationHandlerChanged) {
+                logOrientationChanged();
+            }
         }
 
         boolean isInLandscape = mOrientationState.getTouchRotation() != ROTATION_0
@@ -2165,7 +2174,8 @@
         }
         for (int i = 0; i < taskCount; i++) {
             TaskView taskView = requireTaskViewAt(i);
-            taskView.updateTaskSize();
+            taskView.updateTaskSize(mLastComputedTaskSize, mLastComputedGridTaskSize,
+                    mLastComputedCarouselTaskSize);
             taskView.setNonGridTranslationX(accumulatedTranslationX);
             taskView.setNonGridPivotTranslationX(translateXToMiddle);
             // Compensate space caused by TaskView scaling.
@@ -2270,8 +2280,8 @@
     }
 
     private void animateActionsViewAlpha(float alphaValue, long duration) {
-        mActionsViewAlphaAnimator = ObjectAnimator.ofFloat(
-                mActionsView.getVisibilityAlpha(), MULTI_PROPERTY_VALUE, alphaValue);
+        mActionsViewAlphaAnimator = ObjectAnimator.ofFloat(mActionsView.getVisibilityAlpha(),
+                AnimatedFloat.VALUE, alphaValue);
         mActionsViewAlphaAnimatorFinalValue = alphaValue;
         mActionsViewAlphaAnimator.setDuration(duration);
         // Set autocancel to prevent race-conditiony setting of alpha from other animations
@@ -2290,7 +2300,7 @@
         mClearAllButton.onRecentsViewScroll(scroll, mOverviewGridEnabled);
 
         // Clear all button alpha was set by the previous line.
-        mActionsView.getIndexScrollAlpha().setValue(1 - mClearAllButton.getScrollAlpha());
+        mActionsView.getIndexScrollAlpha().updateValue(1 - mClearAllButton.getScrollAlpha());
     }
 
     @Override
@@ -2354,8 +2364,8 @@
         // Update the task data for the in/visible children
         for (int i = 0; i < getTaskViewCount(); i++) {
             TaskView taskView = requireTaskViewAt(i);
-            TaskIdAttributeContainer[] containers = taskView.getTaskIdAttributeContainers();
-            if (containers.length == 0) {
+            List<TaskContainer> containers = taskView.getTaskContainers();
+            if (containers.isEmpty()) {
                 continue;
             }
             int index = indexOfChild(taskView);
@@ -2367,8 +2377,8 @@
             }
             if (visible) {
                 // Default update all non-null tasks, then remove running ones
-                List<Task> tasksToUpdate = Arrays.stream(containers).filter(Objects::nonNull)
-                        .map(TaskIdAttributeContainer::getTask)
+                List<Task> tasksToUpdate = containers.stream()
+                        .map(TaskContainer::getTask)
                         .collect(Collectors.toCollection(ArrayList::new));
                 if (mTmpRunningTasks != null) {
                     for (Task t : mTmpRunningTasks) {
@@ -2393,7 +2403,7 @@
                     mHasVisibleTaskData.put(task.key.id, visible);
                 }
             } else {
-                for (TaskIdAttributeContainer container : containers) {
+                for (TaskContainer container : containers) {
                     if (container == null) {
                         continue;
                     }
@@ -2784,26 +2794,24 @@
             if (needDesktopTask) {
                 taskView = getTaskViewFromPool(TaskView.Type.DESKTOP);
                 mTmpRunningTasks = Arrays.copyOf(runningTasks, runningTasks.length);
-                addView(taskView, 0);
                 ((DesktopTaskView) taskView).bind(Arrays.asList(mTmpRunningTasks),
-                        mOrientationState);
+                        mOrientationState, mTaskOverlayFactory);
             } else if (needGroupTaskView) {
                 taskView = getTaskViewFromPool(TaskView.Type.GROUPED);
                 mTmpRunningTasks = new Task[]{runningTasks[0], runningTasks[1]};
-                addView(taskView, 0);
                 // When we create a placeholder task view mSplitBoundsConfig will be null, but with
                 // the actual app running we won't need to show the thumbnail until all the tasks
                 // load later anyways
                 ((GroupedTaskView)taskView).bind(mTmpRunningTasks[0], mTmpRunningTasks[1],
-                        mOrientationState, mSplitBoundsConfig);
+                        mOrientationState, mTaskOverlayFactory, mSplitBoundsConfig);
             } else {
                 taskView = getTaskViewFromPool(TaskView.Type.SINGLE);
-                addView(taskView, 0);
                 // The temporary running task is only used for the duration between the start of the
                 // gesture and the task list is loaded and applied
                 mTmpRunningTasks = new Task[]{runningTasks[0]};
-                taskView.bind(mTmpRunningTasks[0], mOrientationState);
+                taskView.bind(mTmpRunningTasks[0], mOrientationState, mTaskOverlayFactory);
             }
+            addView(taskView, 0);
             runningTaskViewId = taskView.getTaskViewId();
             if (wasEmpty) {
                 addView(mClearAllButton);
@@ -2901,7 +2909,7 @@
         mRunningTaskShowScreenshot = showScreenshot;
         TaskView runningTaskView = getRunningTaskView();
         if (runningTaskView != null) {
-            runningTaskView.setShowScreenshot(mRunningTaskShowScreenshot);
+            runningTaskView.setShouldShowScreenshot(mRunningTaskShowScreenshot);
         }
     }
 
@@ -3745,7 +3753,7 @@
                 if (taskView == nextFocusedTaskView) {
                     // Enlarge the task to be focused next, and translate into focus position.
                     float scale = mTaskWidth / (float) mLastComputedGridTaskSize.width();
-                    anim.setFloat(taskView, TaskView.SNAPSHOT_SCALE, scale,
+                    anim.setFloat(taskView, TaskView.DISMISS_SCALE, scale,
                             clampToProgress(LINEAR, animationStartProgress,
                                     dismissTranslationInterpolationEnd));
                     anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(),
@@ -3816,19 +3824,17 @@
 
                 if (success) {
                     if (shouldRemoveTask) {
-                        if (dismissedTaskView.getTask() != null) {
-                            if (dismissedTaskView.isRunningTask()) {
-                                finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
-                                        () -> removeTaskInternal(dismissedTaskViewId));
-                            } else {
-                                removeTaskInternal(dismissedTaskViewId);
-                            }
-                            announceForAccessibility(
-                                    getResources().getString(R.string.task_view_closed));
-                            mContainer.getStatsLogManager().logger()
-                                    .withItemInfo(dismissedTaskView.getItemInfo())
-                                    .log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
+                        if (dismissedTaskView.isRunningTask()) {
+                            finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
+                                    () -> removeTaskInternal(dismissedTaskViewId));
+                        } else {
+                            removeTaskInternal(dismissedTaskViewId);
                         }
+                        announceForAccessibility(
+                                getResources().getString(R.string.task_view_closed));
+                        mContainer.getStatsLogManager().logger()
+                                .withItemInfo(dismissedTaskView.getFirstItemInfo())
+                                .log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
                     }
 
                     int pageToSnapTo = mCurrentPage;
@@ -3877,7 +3883,8 @@
                                         taskViewIdArray.removeValue(
                                                 finalNextFocusedTaskView.getTaskViewId());
                                     }
-                                    if (snappedIndex < taskViewIdArray.size()) {
+                                    if (snappedIndex >= 0
+                                            && snappedIndex < taskViewIdArray.size()) {
                                         taskViewIdToSnapTo = taskViewIdArray.get(snappedIndex);
                                     } else if (snappedIndex == taskViewIdArray.size()) {
                                         // If the snapped task is the last item from the
@@ -4011,7 +4018,9 @@
      * * Device is large screen
      */
     private void updateCurrentTaskActionsVisibility() {
-        boolean isCurrentSplit = getCurrentPageTaskView() instanceof GroupedTaskView;
+        TaskView taskView = getCurrentPageTaskView();
+        boolean isCurrentSplit = taskView instanceof GroupedTaskView;
+        GroupedTaskView groupedTaskView = isCurrentSplit ? (GroupedTaskView) taskView : null;
         // Update flags to see if entire actions bar should be hidden.
         if (!FeatureFlags.enableAppPairs()) {
             mActionsView.updateHiddenFlags(HIDDEN_SPLIT_SCREEN, isCurrentSplit);
@@ -4019,9 +4028,11 @@
         mActionsView.updateHiddenFlags(HIDDEN_SPLIT_SELECT_ACTIVE, isSplitSelectionActive());
         // Update flags to see if actions bar should show buttons for a single task or a pair of
         // tasks.
-        mActionsView.updateForGroupedTask(isCurrentSplit);
+        boolean canSaveAppPair = isCurrentSplit && supportsAppPairs() &&
+                getSplitSelectController().getAppPairsController().canSaveAppPair(groupedTaskView);
+        mActionsView.updateForGroupedTask(isCurrentSplit, canSaveAppPair);
 
-        boolean isCurrentDesktop = getCurrentPageTaskView() instanceof DesktopTaskView;
+        boolean isCurrentDesktop = taskView instanceof DesktopTaskView;
         mActionsView.updateHiddenFlags(HIDDEN_DESKTOP, isCurrentDesktop);
     }
 
@@ -4295,7 +4306,7 @@
         int alphaInt = Math.round(alpha * 255);
         mEmptyMessagePaint.setAlpha(alphaInt);
         mEmptyIcon.setAlpha(alphaInt);
-        mActionsView.getContentAlpha().setValue(mContentAlpha);
+        mActionsView.getContentAlpha().updateValue(mContentAlpha);
 
         if (alpha > 0) {
             setVisibility(VISIBLE);
@@ -4341,7 +4352,10 @@
     public void updateRecentsRotation() {
         final int rotation = TraceHelper.allowIpcs(
                 "RecentsView.updateRecentsRotation", () -> mContainer.getDisplay().getRotation());
-        mOrientationState.setRecentsRotation(rotation);
+        // Log real orientation change.
+        if (mOrientationState.setRecentsRotation(rotation)) {
+            logOrientationChanged();
+        }
     }
 
     public void setLayoutRotation(int touchRotation, int displayRotation) {
@@ -4730,7 +4744,8 @@
      */
     public void resetModalVisuals() {
         if (mSelectedTask != null) {
-            mSelectedTask.getThumbnail().getTaskOverlay().resetModalVisuals();
+            mSelectedTask.taskContainers.forEach(
+                    taskContainer -> taskContainer.getOverlay().resetModalVisuals());
         }
     }
 
@@ -4748,8 +4763,8 @@
     public void initiateSplitSelect(TaskView taskView, @StagePosition int stagePosition,
             StatsLogManager.EventEnum splitEvent) {
         mSplitHiddenTaskView = taskView;
-        mSplitSelectStateController.setInitialTaskSelect(null /*intent*/,
-                stagePosition, taskView.getItemInfo(), splitEvent, taskView.mTask.key.id);
+        mSplitSelectStateController.setInitialTaskSelect(null /*intent*/, stagePosition,
+                taskView.getFirstItemInfo(), splitEvent, taskView.getFirstTask().key.id);
         mSplitSelectStateController.setAnimateCurrentTaskDismissal(
                 true /*animateCurrentTaskDismissal*/);
         mSplitHiddenTaskViewIndex = indexOfChild(taskView);
@@ -4800,11 +4815,11 @@
             // Animate pair thumbnail into full thumbnail
             boolean primaryTaskSelected = mSplitHiddenTaskView.getTaskIds()[0]
                     == mSplitSelectStateController.getInitialTaskId();
-            TaskIdAttributeContainer taskIdAttributeContainer = mSplitHiddenTaskView
-                    .getTaskIdAttributeContainers()[primaryTaskSelected ? 1 : 0];
-            TaskThumbnailViewDeprecated thumbnail = taskIdAttributeContainer.getThumbnailView();
+            TaskContainer taskContainer = mSplitHiddenTaskView
+                    .getTaskContainers().get(primaryTaskSelected ? 1 : 0);
+            TaskThumbnailViewDeprecated thumbnail = taskContainer.getThumbnailViewDeprecated();
             mSplitSelectStateController.getSplitAnimationController()
-                    .addInitialSplitFromPair(taskIdAttributeContainer, builder,
+                    .addInitialSplitFromPair(taskContainer, builder,
                             mContainer.getDeviceProfile(),
                             mSplitHiddenTaskView.getWidth(), mSplitHiddenTaskView.getHeight(),
                             primaryTaskSelected);
@@ -5202,7 +5217,7 @@
         updateGridProperties();
         updateScrollSynchronously();
 
-        int targetSysUiFlags = tv.getThumbnail().getSysUiStatusNavFlags();
+        int targetSysUiFlags = tv.getFirstThumbnailViewDeprecated().getSysUiStatusNavFlags();
         final boolean[] passedOverviewThreshold = new boolean[] {false};
         ValueAnimator progressAnim = ValueAnimator.ofFloat(0, 1);
         progressAnim.addUpdateListener(animator -> {
@@ -5266,11 +5281,8 @@
                 } else {
                     tv.launchTask(this::onTaskLaunchAnimationEnd);
                 }
-                Task task = tv.getTask();
-                if (task != null) {
-                    mContainer.getStatsLogManager().logger().withItemInfo(tv.getItemInfo())
-                            .log(LAUNCHER_TASK_LAUNCH_SWIPE_DOWN);
-                }
+                mContainer.getStatsLogManager().logger().withItemInfo(tv.getFirstItemInfo())
+                        .log(LAUNCHER_TASK_LAUNCH_SWIPE_DOWN);
             } else {
                 onTaskLaunchAnimationEnd(false);
             }
@@ -5279,10 +5291,11 @@
         return mPendingAnimation;
     }
 
-    protected void onTaskLaunchAnimationEnd(boolean success) {
+    protected Unit onTaskLaunchAnimationEnd(boolean success) {
         if (success) {
             resetTaskVisuals();
         }
+        return Unit.INSTANCE;
     }
 
     @Override
@@ -5898,15 +5911,15 @@
             return;
         }
 
-        taskView.setShowScreenshot(true);
-        for (TaskIdAttributeContainer container : taskView.getTaskIdAttributeContainers()) {
+        taskView.setShouldShowScreenshot(true);
+        for (TaskContainer container : taskView.getTaskContainers()) {
             if (container == null) {
                 continue;
             }
 
             ThumbnailData td =
                     mRecentsAnimationController.screenshotTask(container.getTask().key.id);
-            TaskThumbnailViewDeprecated thumbnailView = container.getThumbnailView();
+            TaskThumbnailViewDeprecated thumbnailView = container.getThumbnailViewDeprecated();
             if (td != null) {
                 thumbnailView.setThumbnail(container.getTask(), td);
             } else {
@@ -5927,7 +5940,7 @@
             Runnable onFinishRunnable) {
         final TaskView taskView = getRunningTaskView();
         if (taskView != null) {
-            taskView.setShowScreenshot(true);
+            taskView.setShouldShowScreenshot(true);
             taskView.refreshThumbnails(thumbnailDatas);
             ViewUtils.postFrameDrawn(taskView, onFinishRunnable);
         } else {
@@ -6269,7 +6282,7 @@
     /**
      * Moves the provided task into desktop mode, and invoke {@code successCallback} if succeeded.
      */
-    public void moveTaskToDesktop(TaskIdAttributeContainer taskContainer,
+    public void moveTaskToDesktop(TaskContainer taskContainer,
             Runnable successCallback) {
         if (!DesktopModeStatus.canEnterDesktopMode(mContext)) {
             return;
@@ -6278,7 +6291,7 @@
                 () -> moveTaskToDesktopInternal(taskContainer, successCallback)));
     }
 
-    private void moveTaskToDesktopInternal(TaskIdAttributeContainer taskContainer,
+    private void moveTaskToDesktopInternal(TaskContainer taskContainer,
             Runnable successCallback) {
         if (mDesktopRecentsTransitionController == null) {
             return;
@@ -6287,6 +6300,24 @@
         successCallback.run();
     }
 
+    // Logs when the orientation of Overview changes. We log both real and fake orientation changes.
+    private void logOrientationChanged() {
+        // Only log when Overview is showing.
+        if (mOverviewStateEnabled) {
+            mContainer.getStatsLogManager()
+                    .logger()
+                    .withContainerInfo(
+                            LauncherAtom.ContainerInfo.newBuilder()
+                                    .setTaskSwitcherContainer(
+                                            LauncherAtom.TaskSwitcherContainer.newBuilder()
+                                                    .setOrientationHandler(
+                                                            getPagedOrientationHandler()
+                                                                    .getHandlerTypeForLogging()))
+                                    .build())
+                    .log(LAUNCHER_OVERVIEW_ORIENTATION_CHANGED);
+        }
+    }
+
     public interface TaskLaunchListener {
         void onTaskLaunched();
     }
diff --git a/src/com/android/quickstep/views/RecentsViewContainer.java b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
similarity index 80%
rename from src/com/android/quickstep/views/RecentsViewContainer.java
rename to quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
index 0c3f4f1..060c71e 100644
--- a/src/com/android/quickstep/views/RecentsViewContainer.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
@@ -27,6 +27,7 @@
 import android.view.Window;
 
 import com.android.launcher3.BaseActivity;
+import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.ScrimView;
@@ -165,4 +166,36 @@
      * Begins transition from overview back to homescreen
      */
     void returnToHomescreen();
+
+    /**
+     * True if the overview panel is visible.
+     * @return Boolean
+     */
+    boolean isRecentsViewVisible();
+
+    /**
+     * Overwrites any logged item in Launcher that doesn't have a container with the
+     * {@link com.android.launcher3.touch.PagedOrientationHandler} in use for Overview.
+     *
+     * @param itemInfoBuilder {@link LauncherAtom.ItemInfo.Builder}
+     */
+    default void applyOverwritesToLogItem(LauncherAtom.ItemInfo.Builder itemInfoBuilder) {
+        if (!itemInfoBuilder.getContainerInfo().hasTaskSwitcherContainer()) {
+            return;
+        }
+
+        if (!isRecentsViewVisible()) {
+            return;
+        }
+
+        RecentsView<?, ?> recentsView = getOverviewPanel();
+        var orientationForLogging =
+                recentsView.getPagedOrientationHandler().getHandlerTypeForLogging();
+        itemInfoBuilder.setContainerInfo(
+                LauncherAtom.ContainerInfo.newBuilder()
+                        .setTaskSwitcherContainer(
+                                LauncherAtom.TaskSwitcherContainer.newBuilder()
+                                        .setOrientationHandler(orientationForLogging))
+                        .build());
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index 443f83c..59ffc39 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -18,8 +18,6 @@
 
 import static com.android.app.animation.Interpolators.EMPHASIZED;
 import static com.android.launcher3.Flags.enableOverviewIconMenu;
-import static com.android.launcher3.testing.shared.TestProtocol.TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE;
-import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
 import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.quickstep.views.TaskThumbnailViewDeprecated.DIM_ALPHA;
@@ -56,7 +54,7 @@
 import com.android.quickstep.TaskUtils;
 import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
 import com.android.quickstep.util.TaskCornerRadius;
-import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
+import com.android.quickstep.views.TaskView.TaskContainer;
 
 /**
  * Contains options for a recent task when long-pressing its icon.
@@ -76,7 +74,7 @@
     private ValueAnimator mRevealAnimator;
     @Nullable private Runnable mOnClosingStartCallback;
     private TaskView mTaskView;
-    private TaskIdAttributeContainer mTaskContainer;
+    private TaskContainer mTaskContainer;
     private LinearLayout mOptionLayout;
     private float mMenuTranslationYBeforeOpen;
     private float mMenuTranslationXBeforeOpen;
@@ -163,7 +161,10 @@
         }
     }
 
-    public static boolean showForTask(TaskIdAttributeContainer taskContainer,
+    /**
+     * Show a task menu for the given taskContainer.
+     */
+    public static boolean showForTask(TaskContainer taskContainer,
             @Nullable Runnable onClosingStartCallback) {
         RecentsViewContainer container = RecentsViewContainer.containerFromContext(
                 taskContainer.getTaskView().getContext());
@@ -173,11 +174,14 @@
         return taskMenuView.populateAndShowForTask(taskContainer);
     }
 
-    public static boolean showForTask(TaskIdAttributeContainer taskContainer) {
+    /**
+     * Show a task menu for the given taskContainer.
+     */
+    public static boolean showForTask(TaskContainer taskContainer) {
         return showForTask(taskContainer, null);
     }
 
-    private boolean populateAndShowForTask(TaskIdAttributeContainer taskContainer) {
+    private boolean populateAndShowForTask(TaskContainer taskContainer) {
         if (isAttachedToWindow()) {
             return false;
         }
@@ -198,7 +202,7 @@
         return true;
     }
 
-    private void addMenuOptions(TaskIdAttributeContainer taskContainer) {
+    private void addMenuOptions(TaskContainer taskContainer) {
         if (enableOverviewIconMenu()) {
             removeView(mTaskName);
         } else {
@@ -226,7 +230,7 @@
         mOptionLayout.addView(menuOptionView);
     }
 
-    private void orientAroundTaskView(TaskIdAttributeContainer taskContainer) {
+    private void orientAroundTaskView(TaskContainer taskContainer) {
         RecentsView recentsView = mContainer.getOverviewPanel();
         RecentsPagedOrientationHandler orientationHandler =
                 recentsView.getPagedOrientationHandler();
@@ -237,12 +241,13 @@
         mContainer.getDragLayer().getDescendantRectRelativeToSelf(
                 enableOverviewIconMenu()
                         ? getIconView().findViewById(R.id.icon_view_menu_anchor)
-                        : taskContainer.getThumbnailView(),
+                        : taskContainer.getThumbnailViewDeprecated(),
                 sTempRect);
         Rect insets = mContainer.getDragLayer().getInsets();
         BaseDragLayer.LayoutParams params = (BaseDragLayer.LayoutParams) getLayoutParams();
-        params.width = orientationHandler.getTaskMenuWidth(taskContainer.getThumbnailView(),
-                deviceProfile, taskContainer.getStagePosition());
+        params.width = orientationHandler.getTaskMenuWidth(
+                taskContainer.getThumbnailViewDeprecated(), deviceProfile,
+                taskContainer.getStagePosition());
         // Gravity set to Left instead of Start as sTempRect.left measures Left distance not Start
         params.gravity = Gravity.LEFT;
         setLayoutParams(params);
@@ -275,10 +280,10 @@
             // Margin that insets the menuView inside the taskView
             float taskInsetMargin = getResources().getDimension(R.dimen.task_card_margin);
             setTranslationX(orientationHandler.getTaskMenuX(thumbnailAlignedX,
-                    mTaskContainer.getThumbnailView(), deviceProfile, taskInsetMargin,
+                    mTaskContainer.getThumbnailViewDeprecated(), deviceProfile, taskInsetMargin,
                     getIconView()));
             setTranslationY(orientationHandler.getTaskMenuY(
-                    thumbnailAlignedY, mTaskContainer.getThumbnailView(),
+                    thumbnailAlignedY, mTaskContainer.getThumbnailViewDeprecated(),
                     mTaskContainer.getStagePosition(), this, taskInsetMargin,
                     getIconView()));
         }
@@ -301,7 +306,6 @@
 
     private void animateOpenOrClosed(boolean closing) {
         if (mOpenCloseAnimator != null && mOpenCloseAnimator.isRunning()) {
-            testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE, "getting canceled");
             mOpenCloseAnimator.cancel();
         }
         mOpenCloseAnimator = new AnimatorSet();
@@ -358,24 +362,18 @@
                     iconAppChip.getMenuTranslationX(),
                     MULTI_PROPERTY_VALUE, closing ? 0 : -additionalTranslationX);
             menuTranslationXAnim.setInterpolator(EMPHASIZED);
-            testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE,
-                    "TaskMenuView.java.animateOpenOrClosed: running translation animations");
 
             mOpenCloseAnimator.playTogether(translationYAnim, translationXAnim,
                     menuTranslationXAnim, menuTranslationYAnim);
         }
-        testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE,
-                "TaskMenuView.java.animateOpenOrClosed: running animation 2");
         mOpenCloseAnimator.playTogether(mRevealAnimator,
                 ObjectAnimator.ofFloat(
-                        mTaskContainer.getThumbnailView(), DIM_ALPHA,
+                        mTaskContainer.getThumbnailViewDeprecated(), DIM_ALPHA,
                         closing ? 0 : TaskView.MAX_PAGE_SCRIM_ALPHA),
                 ObjectAnimator.ofFloat(this, ALPHA, closing ? 0 : 1));
         mOpenCloseAnimator.addListener(new AnimationSuccessListener() {
             @Override
             public void onAnimationStart(Animator animation) {
-                testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE,
-                        "TaskMenuView.java.animateOpenOrClosed: onAnimationStart");
                 setVisibility(VISIBLE);
                 if (closing && mOnClosingStartCallback != null) {
                     mOnClosingStartCallback.run();
@@ -383,16 +381,7 @@
             }
 
             @Override
-            public void onAnimationCancel(Animator animation) {
-                super.onAnimationCancel(animation);
-                testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE,
-                        "TaskMenuView.java.animateOpenOrClosed: onAnimationCancel");
-            }
-
-            @Override
             public void onAnimationSuccess(Animator animator) {
-                testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE,
-                        "TaskMenuView.java.animateOpenOrClosed: onAnimationSuccess");
                 if (closing) {
                     closeComplete();
                 }
@@ -403,7 +392,6 @@
     }
 
     private void closeComplete() {
-        testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE, "TaskMenuView.java.closeComplete");
         mIsOpen = false;
         mContainer.getDragLayer().removeView(this);
         mRevealAnimator = null;
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
index a138db0..659cc0c 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
@@ -37,16 +37,13 @@
 import com.android.launcher3.popup.SystemShortcut
 import com.android.launcher3.util.Themes
 import com.android.quickstep.TaskOverlayFactory
-import com.android.quickstep.views.TaskView.TaskIdAttributeContainer
+import com.android.quickstep.views.TaskView.TaskContainer
 
 class TaskMenuViewWithArrow<T> : ArrowPopup<T> where T : RecentsViewContainer, T : Context {
     companion object {
         const val TAG = "TaskMenuViewWithArrow"
 
-        fun <T> showForTask(
-            taskContainer: TaskIdAttributeContainer,
-            alignedOptionIndex: Int = 0
-        ): Boolean where T : RecentsViewContainer, T : Context {
+        fun showForTask(taskContainer: TaskContainer, alignedOptionIndex: Int = 0): Boolean {
             val container: RecentsViewContainer =
                 RecentsViewContainer.containerFromContext(taskContainer.taskView.context)
             val taskMenuViewWithArrow =
@@ -54,7 +51,7 @@
                     R.layout.task_menu_with_arrow,
                     container.dragLayer,
                     false
-                ) as TaskMenuViewWithArrow<T>
+                ) as TaskMenuViewWithArrow<*>
 
             return taskMenuViewWithArrow.populateAndShowForTask(taskContainer, alignedOptionIndex)
         }
@@ -87,7 +84,7 @@
 
     private lateinit var taskView: TaskView
     private lateinit var optionLayout: LinearLayout
-    private lateinit var taskContainer: TaskIdAttributeContainer
+    private lateinit var taskContainer: TaskContainer
 
     private var optionMeasuredHeight = 0
     private val arrowHorizontalPadding: Int
@@ -141,7 +138,7 @@
     }
 
     private fun populateAndShowForTask(
-        taskContainer: TaskIdAttributeContainer,
+        taskContainer: TaskContainer,
         alignedOptionIndex: Int
     ): Boolean {
         if (isAttachedToWindow) {
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailViewDeprecated.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailViewDeprecated.java
index 9802beb..447002f 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailViewDeprecated.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailViewDeprecated.java
@@ -53,6 +53,7 @@
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.SystemUiController.SystemUiControllerFlags;
+import com.android.launcher3.util.ViewPool;
 import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
 import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
 import com.android.quickstep.views.TaskView.FullscreenDrawParams;
@@ -66,7 +67,7 @@
  * @deprecated This class will be replaced by the new [TaskThumbnailView].
  */
 @Deprecated
-public class TaskThumbnailViewDeprecated extends View {
+public class TaskThumbnailViewDeprecated extends View implements ViewPool.Reusable {
     private static final MainThreadInitializedObject<FullscreenDrawParams> TEMP_PARAMS =
             new MainThreadInitializedObject<>(FullscreenDrawParams::new);
 
@@ -127,8 +128,7 @@
             };
 
     private final RecentsViewContainer mContainer;
-    @Nullable
-    private TaskOverlay mOverlay;
+    private TaskOverlay<?> mOverlay;
     private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     private final Paint mSplashBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -187,8 +187,9 @@
     /**
      * Updates the thumbnail to draw the provided task
      */
-    public void bind(Task task) {
-        getTaskOverlay().reset();
+    public void bind(Task task, TaskOverlay<?> overlay) {
+        mOverlay = overlay;
+        mOverlay.reset();
         mTask = task;
         int color = task == null ? Color.BLACK : task.colorBackground | 0xFF000000;
         mPaint.setColor(color);
@@ -198,6 +199,17 @@
     }
 
     /**
+     * Sets TaskOverlay without binding a task.
+     *
+     * @deprecated Should only be used when using new
+     * {@link com.android.quickstep.task.thumbnail.TaskThumbnailView}.
+     */
+    @Deprecated
+    public void setTaskOverlay(TaskOverlay<?> overlay) {
+        mOverlay = overlay;
+    }
+
+    /**
      * Updates the thumbnail.
      *
      * @param refreshNow whether the {@code thumbnailData} will be used to redraw immediately.
@@ -211,8 +223,8 @@
             boolean refreshNow) {
         mTask = task;
         boolean thumbnailWasNull = mThumbnailData == null;
-        mThumbnailData =
-                (thumbnailData != null && thumbnailData.thumbnail != null) ? thumbnailData : null;
+        mThumbnailData = (thumbnailData != null && thumbnailData.getThumbnail() != null)
+                ? thumbnailData : null;
         if (mTask != null) {
             updateSplashView(mTask.icon);
         }
@@ -237,8 +249,8 @@
      * @param shouldRefreshOverlay whether to re-initialize overlay
      */
     private void refresh(boolean shouldRefreshOverlay) {
-        if (mThumbnailData != null && mThumbnailData.thumbnail != null) {
-            Bitmap bm = mThumbnailData.thumbnail;
+        if (mThumbnailData != null && mThumbnailData.getThumbnail() != null) {
+            Bitmap bm = mThumbnailData.getThumbnail();
             bm.prepareToDraw();
             mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
             mPaint.setShader(mBitmapShader);
@@ -250,7 +262,7 @@
             mBitmapShader = null;
             mThumbnailData = null;
             mPaint.setShader(null);
-            getTaskOverlay().reset();
+            mOverlay.reset();
         }
         updateThumbnailPaintFilter();
     }
@@ -278,13 +290,6 @@
         invalidate();
     }
 
-    public TaskOverlay getTaskOverlay() {
-        if (mOverlay == null) {
-            mOverlay = getTaskView().getRecentsView().getTaskOverlayFactory().createOverlay(this);
-        }
-        return mOverlay;
-    }
-
     public float getDimAlpha() {
         return mDimAlpha;
     }
@@ -302,8 +307,10 @@
         }
 
         RectF bitmapRect = new RectF(
-                0, 0,
-                mThumbnailData.thumbnail.getWidth(), mThumbnailData.thumbnail.getHeight());
+                0,
+                0,
+                mThumbnailData.getThumbnail().getWidth(),
+                mThumbnailData.getThumbnail().getHeight());
         RectF viewRect = new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight());
 
         // The position helper matrix tells us how to transform the bitmap to fit the view, the
@@ -347,7 +354,7 @@
         canvas.save();
         // Draw the insets if we're being drawn fullscreen (we do this for quick switch).
         drawOnCanvas(canvas, 0, 0, getMeasuredWidth(), getMeasuredHeight(),
-                mFullscreenParams.mCurrentDrawnCornerRadius);
+                mFullscreenParams.getCurrentDrawnCornerRadius());
         canvas.restore();
     }
 
@@ -357,13 +364,13 @@
 
     public void setFullscreenParams(TaskView.FullscreenDrawParams fullscreenParams) {
         mFullscreenParams = fullscreenParams;
-        getTaskOverlay().setFullscreenParams(fullscreenParams);
         invalidate();
     }
 
     public void drawOnCanvas(Canvas canvas, float x, float y, float width, float height,
             float cornerRadius) {
-        if (mTask != null && getTaskView().isRunningTask() && !getTaskView().showScreenshot()) {
+        if (mTask != null && getTaskView().isRunningTask()
+                && !getTaskView().getShouldShowScreenshot()) {
             canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mClearPaint);
             canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius,
                     mDimmingPaintAfterClearing);
@@ -501,13 +508,13 @@
     }
 
     private boolean isThumbnailAspectRatioDifferentFromThumbnailData() {
-        if (mThumbnailData == null || mThumbnailData.thumbnail == null) {
+        if (mThumbnailData == null || mThumbnailData.getThumbnail() == null) {
             return false;
         }
 
         float thumbnailViewAspect = getWidth() / (float) getHeight();
-        float thumbnailDataAspect =
-                mThumbnailData.thumbnail.getWidth() / (float) mThumbnailData.thumbnail.getHeight();
+        float thumbnailDataAspect = mThumbnailData.getThumbnail().getWidth()
+                / (float) mThumbnailData.getThumbnail().getHeight();
 
         return isRelativePercentDifferenceGreaterThan(thumbnailViewAspect,
                 thumbnailDataAspect, MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT);
@@ -533,10 +540,10 @@
      */
     private void refreshOverlay() {
         if (mOverlayEnabled) {
-            getTaskOverlay().initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.getMatrix(),
+            mOverlay.initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.getMatrix(),
                     mPreviewPositionHelper.isOrientationChanged());
         } else {
-            getTaskOverlay().reset();
+            mOverlay.reset();
         }
     }
 
@@ -558,10 +565,9 @@
         DeviceProfile dp = mContainer.getDeviceProfile();
         mPreviewPositionHelper.setOrientationChanged(false);
         if (mBitmapShader != null && mThumbnailData != null) {
-            mPreviewRect.set(0, 0, mThumbnailData.thumbnail.getWidth(),
-                    mThumbnailData.thumbnail.getHeight());
-            int currentRotation = getTaskView().getRecentsView().getPagedViewOrientedState()
-                    .getRecentsActivityRotation();
+            mPreviewRect.set(0, 0, mThumbnailData.getThumbnail().getWidth(),
+                    mThumbnailData.getThumbnail().getHeight());
+            int currentRotation = getTaskView().getOrientedState().getRecentsActivityRotation();
             boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
             mPreviewPositionHelper.updateThumbnailMatrix(mPreviewRect, mThumbnailData,
                     getMeasuredWidth(), getMeasuredHeight(), dp.isTablet, currentRotation, isRtl);
@@ -593,7 +599,7 @@
         if (mThumbnailData == null) {
             return null;
         }
-        return mThumbnailData.thumbnail;
+        return mThumbnailData.getThumbnail();
     }
 
     /**
@@ -606,4 +612,9 @@
         }
         return mThumbnailData.isRealSnapshot && !mTask.isLocked;
     }
+
+    @Override
+    public void onRecycle() {
+        // Do nothing
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
deleted file mode 100644
index 93dd44f..0000000
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ /dev/null
@@ -1,2025 +0,0 @@
-/*
- * Copyright (C) 2017 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.views;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.widget.Toast.LENGTH_SHORT;
-
-import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.app.animation.Interpolators.LINEAR;
-import static com.android.launcher3.Flags.enableCursorHoverStates;
-import static com.android.launcher3.Flags.enableGridOnlyOverview;
-import static com.android.launcher3.Flags.enableOverviewIconMenu;
-import static com.android.launcher3.Flags.enableRefactorTaskThumbnail;
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
-import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_NOT_PINNABLE;
-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.STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
-import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
-import static com.android.quickstep.TaskOverlayFactory.getEnabledShortcuts;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.EXPECTING_TASK_APPEARED;
-import static com.android.quickstep.util.BorderAnimator.DEFAULT_BORDER_COLOR;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.annotation.IdRes;
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.util.AttributeSet;
-import android.util.FloatProperty;
-import android.util.Log;
-import android.view.Display;
-import android.view.MotionEvent;
-import android.view.RemoteAnimationTarget;
-import android.view.TouchDelegate;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewStub;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.FrameLayout;
-import android.widget.Toast;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.app.animation.Interpolators;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Flags;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.popup.SystemShortcut;
-import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.util.ActivityOptionsWrapper;
-import com.android.launcher3.util.CancellableTask;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.RunnableList;
-import com.android.launcher3.util.SafeCloseable;
-import com.android.launcher3.util.SplitConfigurationOptions;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
-import com.android.launcher3.util.TraceHelper;
-import com.android.launcher3.util.TransformingTouchDelegate;
-import com.android.launcher3.util.ViewPool.Reusable;
-import com.android.quickstep.RecentsModel;
-import com.android.quickstep.RemoteAnimationTargets;
-import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
-import com.android.quickstep.TaskAnimationManager;
-import com.android.quickstep.TaskIconCache;
-import com.android.quickstep.TaskThumbnailCache;
-import com.android.quickstep.TaskUtils;
-import com.android.quickstep.TaskViewUtils;
-import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
-import com.android.quickstep.task.thumbnail.TaskThumbnail;
-import com.android.quickstep.task.thumbnail.TaskThumbnailView;
-import com.android.quickstep.util.ActiveGestureLog;
-import com.android.quickstep.util.BorderAnimator;
-import com.android.quickstep.util.RecentsOrientedState;
-import com.android.quickstep.util.SplitSelectStateController;
-import com.android.quickstep.util.TaskCornerRadius;
-import com.android.quickstep.util.TaskRemovedDuringLaunchListener;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.QuickStepContract;
-
-import kotlin.Unit;
-
-import java.lang.annotation.Retention;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Optional;
-import java.util.function.Consumer;
-import java.util.stream.Stream;
-
-
-/**
- * A task in the Recents view.
- */
-public class TaskView extends FrameLayout implements Reusable {
-
-    private static final String TAG = TaskView.class.getSimpleName();
-    public static final int FLAG_UPDATE_ICON = 1;
-    public static final int FLAG_UPDATE_THUMBNAIL = FLAG_UPDATE_ICON << 1;
-    public static final int FLAG_UPDATE_CORNER_RADIUS = FLAG_UPDATE_THUMBNAIL << 1;
-
-    public static final int FLAG_UPDATE_ALL = FLAG_UPDATE_ICON | FLAG_UPDATE_THUMBNAIL
-            | FLAG_UPDATE_CORNER_RADIUS;
-
-    /**
-     * Used in conjunction with {@link #onTaskListVisibilityChanged(boolean, int)}, providing more
-     * granularity on which components of this task require an update
-     */
-    @Retention(SOURCE)
-    @IntDef({FLAG_UPDATE_ALL, FLAG_UPDATE_ICON, FLAG_UPDATE_THUMBNAIL, FLAG_UPDATE_CORNER_RADIUS})
-    public @interface TaskDataChanges {
-    }
-
-    /**
-     * Type of task view
-     */
-    @Retention(SOURCE)
-    @IntDef({Type.SINGLE, Type.GROUPED, Type.DESKTOP})
-    public @interface Type {
-        int SINGLE = 1;
-        int GROUPED = 2;
-        int DESKTOP = 3;
-    }
-
-    /** The maximum amount that a task view can be scrimmed, dimmed or tinted. */
-    public static final float MAX_PAGE_SCRIM_ALPHA = 0.4f;
-
-    private static final float EDGE_SCALE_DOWN_FACTOR_CAROUSEL = 0.03f;
-    private static final float EDGE_SCALE_DOWN_FACTOR_GRID = 0.00f;
-
-    public static final long SCALE_ICON_DURATION = 120;
-    private static final long DIM_ANIM_DURATION = 700;
-
-    /**
-     * This technically can be a vanilla {@link TouchDelegate} class, however that class requires
-     * setting the touch bounds at construction, so we'd repeatedly be created many instances
-     * unnecessarily as scrolling occurs, whereas {@link TransformingTouchDelegate} allows touch
-     * delegated bounds only to be updated.
-     */
-    private TransformingTouchDelegate mIconTouchDelegate;
-
-    private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
-            Collections.singletonList(new Rect());
-
-    public static final FloatProperty<TaskView> FOCUS_TRANSITION =
-            new FloatProperty<>("focusTransition") {
-                @Override
-                public void setValue(TaskView taskView, float v) {
-                    taskView.setIconsAndBannersTransitionProgress(v, false /* invert */);
-                }
-
-                @Override
-                public Float get(TaskView taskView) {
-                    return taskView.mFocusTransitionProgress;
-                }
-            };
-
-    private static final FloatProperty<TaskView> SPLIT_SELECT_TRANSLATION_X =
-            new FloatProperty<>("splitSelectTranslationX") {
-                @Override
-                public void setValue(TaskView taskView, float v) {
-                    taskView.setSplitSelectTranslationX(v);
-                }
-
-                @Override
-                public Float get(TaskView taskView) {
-                    return taskView.mSplitSelectTranslationX;
-                }
-            };
-
-    private static final FloatProperty<TaskView> SPLIT_SELECT_TRANSLATION_Y =
-            new FloatProperty<>("splitSelectTranslationY") {
-                @Override
-                public void setValue(TaskView taskView, float v) {
-                    taskView.setSplitSelectTranslationY(v);
-                }
-
-                @Override
-                public Float get(TaskView taskView) {
-                    return taskView.mSplitSelectTranslationY;
-                }
-            };
-
-    private static final FloatProperty<TaskView> DISMISS_TRANSLATION_X =
-            new FloatProperty<>("dismissTranslationX") {
-                @Override
-                public void setValue(TaskView taskView, float v) {
-                    taskView.setDismissTranslationX(v);
-                }
-
-                @Override
-                public Float get(TaskView taskView) {
-                    return taskView.mDismissTranslationX;
-                }
-            };
-
-    private static final FloatProperty<TaskView> DISMISS_TRANSLATION_Y =
-            new FloatProperty<>("dismissTranslationY") {
-                @Override
-                public void setValue(TaskView taskView, float v) {
-                    taskView.setDismissTranslationY(v);
-                }
-
-                @Override
-                public Float get(TaskView taskView) {
-                    return taskView.mDismissTranslationY;
-                }
-            };
-
-    private static final FloatProperty<TaskView> TASK_OFFSET_TRANSLATION_X =
-            new FloatProperty<>("taskOffsetTranslationX") {
-                @Override
-                public void setValue(TaskView taskView, float v) {
-                    taskView.setTaskOffsetTranslationX(v);
-                }
-
-                @Override
-                public Float get(TaskView taskView) {
-                    return taskView.mTaskOffsetTranslationX;
-                }
-            };
-
-    private static final FloatProperty<TaskView> TASK_OFFSET_TRANSLATION_Y =
-            new FloatProperty<>("taskOffsetTranslationY") {
-                @Override
-                public void setValue(TaskView taskView, float v) {
-                    taskView.setTaskOffsetTranslationY(v);
-                }
-
-                @Override
-                public Float get(TaskView taskView) {
-                    return taskView.mTaskOffsetTranslationY;
-                }
-            };
-
-    private static final FloatProperty<TaskView> TASK_RESISTANCE_TRANSLATION_X =
-            new FloatProperty<>("taskResistanceTranslationX") {
-                @Override
-                public void setValue(TaskView taskView, float v) {
-                    taskView.setTaskResistanceTranslationX(v);
-                }
-
-                @Override
-                public Float get(TaskView taskView) {
-                    return taskView.mTaskResistanceTranslationX;
-                }
-            };
-
-    private static final FloatProperty<TaskView> TASK_RESISTANCE_TRANSLATION_Y =
-            new FloatProperty<>("taskResistanceTranslationY") {
-                @Override
-                public void setValue(TaskView taskView, float v) {
-                    taskView.setTaskResistanceTranslationY(v);
-                }
-
-                @Override
-                public Float get(TaskView taskView) {
-                    return taskView.mTaskResistanceTranslationY;
-                }
-            };
-
-    public static final FloatProperty<TaskView> GRID_END_TRANSLATION_X =
-            new FloatProperty<>("gridEndTranslationX") {
-                @Override
-                public void setValue(TaskView taskView, float v) {
-                    taskView.setGridEndTranslationX(v);
-                }
-
-                @Override
-                public Float get(TaskView taskView) {
-                    return taskView.mGridEndTranslationX;
-                }
-            };
-
-    public static final FloatProperty<TaskView> SNAPSHOT_SCALE =
-            new FloatProperty<>("snapshotScale") {
-                @Override
-                public void setValue(TaskView taskView, float v) {
-                    taskView.setSnapshotScale(v);
-                }
-
-                @Override
-                public Float get(TaskView taskView) {
-                    return taskView.mTaskThumbnailViewDeprecated.getScaleX();
-                }
-            };
-
-    @Nullable
-    protected Task mTask;
-    @Nullable // can be null when enableRefactorTaskThumbnail() == true
-    protected TaskThumbnailViewDeprecated mTaskThumbnailViewDeprecated;
-    protected TaskThumbnailView mTaskThumbnailView;
-    protected TaskViewIcon mIconView;
-    protected final DigitalWellBeingToast mDigitalWellBeingToast;
-    protected float mFullscreenProgress;
-    private float mGridProgress;
-    protected float mTaskThumbnailSplashAlpha;
-    private float mNonGridScale = 1;
-    private float mDismissScale = 1;
-    protected final FullscreenDrawParams mCurrentFullscreenParams;
-    protected final RecentsViewContainer mContainer;
-
-    // Various causes of changing primary translation, which we aggregate to setTranslationX/Y().
-    private float mDismissTranslationX;
-    private float mDismissTranslationY;
-    private float mTaskOffsetTranslationX;
-    private float mTaskOffsetTranslationY;
-    private float mTaskResistanceTranslationX;
-    private float mTaskResistanceTranslationY;
-    // The following translation variables should only be used in the same orientation as Launcher.
-    private float mBoxTranslationY;
-    // The following grid translations scales with mGridProgress.
-    private float mGridTranslationX;
-    private float mGridTranslationY;
-    // The following grid translation is used to animate closing the gap between grid and clear all.
-    private float mGridEndTranslationX;
-    // Applied as a complement to gridTranslation, for adjusting the carousel overview and quick
-    // switch.
-    private float mNonGridTranslationX;
-    private float mNonGridPivotTranslationX;
-    // Used when in SplitScreenSelectState
-    private float mSplitSelectTranslationY;
-    private float mSplitSelectTranslationX;
-
-    @Nullable
-    private ObjectAnimator mIconAndDimAnimator;
-    private float mIconScaleAnimStartProgress = 0;
-    private float mFocusTransitionProgress = 1;
-    private float mModalness = 0;
-    private float mStableAlpha = 1;
-
-    private int mTaskViewId = -1;
-    protected int[] mTaskIdContainer = new int[0];
-    protected TaskIdAttributeContainer[] mTaskIdAttributeContainer =
-            new TaskIdAttributeContainer[0];
-
-    private boolean mShowScreenshot;
-    private boolean mBorderEnabled;
-
-    // The current background requests to load the task thumbnail and icon
-    @Nullable
-    private CancellableTask mThumbnailLoadRequest;
-    @Nullable
-    private CancellableTask mIconLoadRequest;
-
-    private boolean mEndQuickswitchCuj;
-
-    private final float[] mIconCenterCoords = new float[2];
-
-    protected final PointF mLastTouchDownPosition = new PointF();
-
-    private boolean mIsClickableAsLiveTile = true;
-
-    @Nullable
-    private final BorderAnimator mFocusBorderAnimator;
-
-    @Nullable
-    private final BorderAnimator mHoverBorderAnimator;
-
-    public TaskView(Context context) {
-        this(context, null);
-    }
-
-    public TaskView(Context context, @Nullable AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public TaskView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public TaskView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        this(context, attrs, defStyleAttr, defStyleRes, null, null);
-    }
-
-    @VisibleForTesting
-    public TaskView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
-            int defStyleRes, BorderAnimator focusBorderAnimator,
-            BorderAnimator hoverBorderAnimator) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        mContainer = RecentsViewContainer.containerFromContext(context);
-        setOnClickListener(this::onClick);
-
-        mCurrentFullscreenParams = new FullscreenDrawParams(context);
-        mDigitalWellBeingToast = new DigitalWellBeingToast(mContainer, this);
-
-        boolean keyboardFocusHighlightEnabled = FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get()
-                || Flags.enableFocusOutline();
-        boolean cursorHoverStatesEnabled = enableCursorHoverStates();
-
-        setWillNotDraw(!keyboardFocusHighlightEnabled && !cursorHoverStatesEnabled);
-
-        TypedArray styledAttrs = context.obtainStyledAttributes(
-                attrs, R.styleable.TaskView, defStyleAttr, defStyleRes);
-
-        if (focusBorderAnimator != null) {
-            mFocusBorderAnimator = focusBorderAnimator;
-        } else {
-            mFocusBorderAnimator = keyboardFocusHighlightEnabled
-                    ? BorderAnimator.createSimpleBorderAnimator(
-                    /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius,
-                    /* borderWidthPx= */ context.getResources().getDimensionPixelSize(
-                            R.dimen.keyboard_quick_switch_border_width),
-                    /* boundsBuilder= */ this::getThumbnailBounds,
-                    /* targetView= */ this,
-                    /* borderColor= */ styledAttrs.getColor(
-                            R.styleable.TaskView_focusBorderColor, DEFAULT_BORDER_COLOR))
-                    : null;
-        }
-
-        if (hoverBorderAnimator != null) {
-            mHoverBorderAnimator = hoverBorderAnimator;
-        } else {
-            mHoverBorderAnimator = cursorHoverStatesEnabled
-                    ? BorderAnimator.createSimpleBorderAnimator(
-                    /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius,
-                    /* borderWidthPx= */ context.getResources().getDimensionPixelSize(
-                            R.dimen.task_hover_border_width),
-                    /* boundsBuilder= */ this::getThumbnailBounds,
-                    /* targetView= */ this,
-                    /* borderColor= */ styledAttrs.getColor(
-                            R.styleable.TaskView_hoverBorderColor, DEFAULT_BORDER_COLOR))
-                    : null;
-        }
-        styledAttrs.recycle();
-    }
-
-    /** Returns the thumbnail's bounds relative to this view. */
-    public Unit getThumbnailBounds(@NonNull Rect bounds) {
-        return getThumbnailBounds(bounds, false);
-    }
-
-    /** Returns the thumbnail's bounds, optionally relative to the screen. */
-    public Unit getThumbnailBounds(@NonNull Rect bounds, boolean relativeToDragLayer) {
-        View snapshotView = getSnapshotView();
-
-        if (relativeToDragLayer) {
-            mContainer.getDragLayer().getDescendantRectRelativeToSelf(snapshotView, bounds);
-        } else {
-            bounds.set(snapshotView.getLeft() + Math.round(snapshotView.getTranslationX()),
-                    snapshotView.getTop() + Math.round(snapshotView.getTranslationY()),
-                    snapshotView.getRight() + Math.round(snapshotView.getTranslationX()),
-                    snapshotView.getBottom() + Math.round(snapshotView.getTranslationY()));
-        }
-        return Unit.INSTANCE;
-    }
-
-    public void setTaskViewId(int id) {
-        this.mTaskViewId = id;
-    }
-
-    public int getTaskViewId() {
-        return mTaskViewId;
-    }
-
-    /**
-     * Updates [TaskThumbnailView] to reflect the latest [Task] state (i.e., task isRunning).
-     */
-    public void notifyIsRunningTaskUpdated() {
-        // TODO(b/335649589): TaskView's VM will already have access to TaskThumbnailView's VM
-        //  so there will be no need to access TaskThumbnailView's VM through the TaskThumbnailView
-        if (mTask != null) {
-            bindTaskThumbnailView();
-        }
-    }
-
-    /**
-     * Builds proto for logging
-     */
-    public WorkspaceItemInfo getItemInfo() {
-        return getItemInfo(mTask);
-    }
-
-    /**
-     * Builds proto for logging
-     */
-    @VisibleForTesting
-    public WorkspaceItemInfo getItemInfo(@Nullable Task task) {
-        WorkspaceItemInfo stubInfo = new WorkspaceItemInfo();
-        stubInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_TASK;
-        stubInfo.container = LauncherSettings.Favorites.CONTAINER_TASKSWITCHER;
-        if (task == null) {
-            return stubInfo;
-        }
-
-        ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(task.key);
-        stubInfo.user = componentKey.user;
-        stubInfo.intent = new Intent().setComponent(componentKey.componentName);
-        stubInfo.title = task.title;
-        if (getRecentsView() != null) {
-            stubInfo.screenId = getRecentsView().indexOfChild(this);
-        }
-        if (Flags.privateSpaceRestrictAccessibilityDrag()) {
-            if (UserCache.getInstance(getContext()).getUserInfo(componentKey.user).isPrivate()) {
-                stubInfo.runtimeStatusFlags |= FLAG_NOT_PINNABLE;
-            }
-        }
-        return stubInfo;
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mTaskThumbnailViewDeprecated = findViewById(R.id.snapshot);
-        if (enableRefactorTaskThumbnail()) {
-            mTaskThumbnailView = new TaskThumbnailView(mContext);
-            mTaskThumbnailView.setLayoutParams(mTaskThumbnailViewDeprecated.getLayoutParams());
-            int indexOfSnapshotView = indexOfChild(mTaskThumbnailViewDeprecated);
-            addView(mTaskThumbnailView, indexOfSnapshotView);
-            mTaskThumbnailViewDeprecated.setVisibility(View.GONE);
-        }
-        ViewStub iconViewStub = findViewById(R.id.icon);
-        if (enableOverviewIconMenu()) {
-            iconViewStub.setLayoutResource(R.layout.icon_app_chip_view);
-        } else {
-            iconViewStub.setLayoutResource(R.layout.icon_view);
-        }
-        mIconView = (TaskViewIcon) iconViewStub.inflate();
-        mIconTouchDelegate = new TransformingTouchDelegate(mIconView.asView());
-    }
-
-    @Override
-    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
-    public void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
-        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
-        if (mFocusBorderAnimator != null && mBorderEnabled) {
-            mFocusBorderAnimator.setBorderVisibility(gainFocus, /* animated= */ true);
-        }
-    }
-
-    @Override
-    public boolean onHoverEvent(MotionEvent event) {
-        if (mHoverBorderAnimator != null && mBorderEnabled) {
-            switch (event.getAction()) {
-                case MotionEvent.ACTION_HOVER_ENTER:
-                    mHoverBorderAnimator.setBorderVisibility(/* visible= */ true, /* animated= */
-                            true);
-                    break;
-                case MotionEvent.ACTION_HOVER_EXIT:
-                    mHoverBorderAnimator.setBorderVisibility(/* visible= */ false, /* animated= */
-                            true);
-                    break;
-                default:
-                    break;
-            }
-        }
-        return super.onHoverEvent(event);
-    }
-
-    /**
-     * Enable or disable showing border on hover and focus change
-     */
-    public void setBorderEnabled(boolean enabled) {
-        if (mBorderEnabled == enabled) {
-            return;
-        }
-
-        mBorderEnabled = enabled;
-        // Set the animation correctly in case it misses the hover/focus event during state
-        // transition
-        if (mHoverBorderAnimator != null) {
-            mHoverBorderAnimator.setBorderVisibility(/* visible= */
-                    enabled && isHovered(), /* animated= */ true);
-        }
-
-        if (mFocusBorderAnimator != null) {
-            mFocusBorderAnimator.setBorderVisibility(/* visible= */
-                    enabled && isFocused(), /* animated= */true);
-        }
-    }
-
-    @Override
-    public boolean onInterceptHoverEvent(MotionEvent event) {
-        if (enableCursorHoverStates()) {
-            // avoid triggering hover event on child elements which would cause HOVER_EXIT for this
-            // task view
-            return true;
-        } else {
-            return super.onInterceptHoverEvent(event);
-        }
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        // Draw border first so any child views outside of the thumbnail bounds are drawn above it.
-        if (mFocusBorderAnimator != null) {
-            mFocusBorderAnimator.drawBorder(canvas);
-        }
-        if (mHoverBorderAnimator != null) {
-            mHoverBorderAnimator.drawBorder(canvas);
-        }
-        super.draw(canvas);
-    }
-
-    /**
-     * Whether the taskview should take the touch event from parent. Events passed to children
-     * that might require special handling.
-     */
-    public boolean offerTouchToChildren(MotionEvent event) {
-        if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            computeAndSetIconTouchDelegate(mIconView, mIconCenterCoords, mIconTouchDelegate);
-        }
-        return mIconTouchDelegate != null && mIconTouchDelegate.onTouchEvent(event);
-    }
-
-    protected void computeAndSetIconTouchDelegate(TaskViewIcon view, float[] tempCenterCoords,
-            TransformingTouchDelegate transformingTouchDelegate) {
-        if (view == null) {
-            return;
-        }
-        float viewHalfWidth = view.getWidth() / 2f;
-        float viewHalfHeight = view.getHeight() / 2f;
-        tempCenterCoords[0] = viewHalfWidth;
-        tempCenterCoords[1] = viewHalfHeight;
-        getDescendantCoordRelativeToAncestor(view.asView(), mContainer.getDragLayer(),
-                tempCenterCoords, false);
-        transformingTouchDelegate.setBounds(
-                (int) (tempCenterCoords[0] - viewHalfWidth),
-                (int) (tempCenterCoords[1] - viewHalfHeight),
-                (int) (tempCenterCoords[0] + viewHalfWidth),
-                (int) (tempCenterCoords[1] + viewHalfHeight));
-    }
-
-    /**
-     * The modalness of this view is how it should be displayed when it is shown on its own in the
-     * modal state of overview.
-     *
-     * @param modalness [0, 1] 0 being in context with other tasks, 1 being shown on its own.
-     */
-    public void setModalness(float modalness) {
-        if (mModalness == modalness) {
-            return;
-        }
-        mModalness = modalness;
-        mIconView.setModalAlpha(1 - modalness);
-        mDigitalWellBeingToast.updateBannerOffset(modalness);
-    }
-
-    public DigitalWellBeingToast getDigitalWellBeingToast() {
-        return mDigitalWellBeingToast;
-    }
-
-    /**
-     * Updates this task view to the given {@param task}.
-     */
-    public void bind(Task task, RecentsOrientedState orientedState) {
-        cancelPendingLoadTasks();
-        mTask = task;
-        mTaskIdContainer = new int[]{mTask.key.id};
-        mTaskIdAttributeContainer = new TaskIdAttributeContainer[]{
-                new TaskIdAttributeContainer(task, mTaskThumbnailViewDeprecated, mIconView,
-                        STAGE_POSITION_UNDEFINED)};
-        if (enableRefactorTaskThumbnail()) {
-            bindTaskThumbnailView();
-        } else {
-            mTaskThumbnailViewDeprecated.bind(task);
-        }
-        setOrientationState(orientedState);
-    }
-
-    private void bindTaskThumbnailView() {
-        // TODO(b/335649589): TaskView's VM will already have access to TaskThumbnailView's VM
-        //  so there will be no need to access TaskThumbnailView's VM through the TaskThumbnailView
-        mTaskThumbnailView.getViewModel().bind(new TaskThumbnail(mTask, isRunningTask()));
-    }
-
-    /**
-     * Sets up an on-click listener and the visibility for show_windows icon on top of the task.
-     */
-    public void setUpShowAllInstancesListener() {
-        if (mTaskIdAttributeContainer.length == 0) {
-            return;
-        }
-        String taskPackageName = mTaskIdAttributeContainer[0].mTask.key.getPackageName();
-
-        // icon of the top/left task
-        View showWindowsView = findViewById(R.id.show_windows);
-        updateFilterCallback(showWindowsView, getFilterUpdateCallback(taskPackageName));
-    }
-
-    /**
-     * Returns a callback that updates the state of the filter and the recents overview
-     *
-     * @param taskPackageName package name of the task to filter by
-     */
-    @Nullable
-    protected View.OnClickListener getFilterUpdateCallback(String taskPackageName) {
-        View.OnClickListener cb = (view) -> {
-            // update and apply a new filter
-            getRecentsView().setAndApplyFilter(taskPackageName);
-        };
-
-        if (!getRecentsView().getFilterState().shouldShowFilterUI(taskPackageName)) {
-            cb = null;
-        }
-        return cb;
-    }
-
-    /**
-     * Sets the correct visibility and callback on the provided filterView based on whether
-     * the callback is null or not
-     */
-    protected void updateFilterCallback(@NonNull View filterView,
-            @Nullable View.OnClickListener callback) {
-        // Filtering changes alpha instead of the visibility since visibility
-        // can be altered separately through RecentsView#resetFromSplitSelectionState()
-        if (callback == null) {
-            filterView.setAlpha(0);
-        } else {
-            filterView.setAlpha(1);
-        }
-
-        filterView.setOnClickListener(callback);
-    }
-
-    public TaskIdAttributeContainer[] getTaskIdAttributeContainers() {
-        return mTaskIdAttributeContainer;
-    }
-
-    @Nullable
-    public Task getTask() {
-        return mTask;
-    }
-
-    /**
-     * Check if given {@code taskId} is tracked in this view
-     */
-    public boolean containsTaskId(int taskId) {
-        return Arrays.stream(mTaskIdContainer).anyMatch(myTaskId -> myTaskId == taskId);
-    }
-
-    /**
-     * Returns a copy of integer array containing taskIds of all tasks in the TaskView.
-     */
-    public int[] getTaskIds() {
-        return Arrays.copyOf(mTaskIdContainer, mTaskIdContainer.length);
-    }
-
-    public boolean containsMultipleTasks() {
-        return mTaskIdContainer.length > 1;
-    }
-
-    /**
-     * Returns the TaskIdAttributeContainer corresponding to a given taskId, or null if the TaskView
-     * does not contain a Task with that ID.
-     */
-    @Nullable
-    public TaskIdAttributeContainer getTaskAttributesById(int taskId) {
-        for (TaskIdAttributeContainer attributes : mTaskIdAttributeContainer) {
-            if (attributes.getTask().key.id == taskId) {
-                return attributes;
-            }
-        }
-
-        return null;
-    }
-
-    public TaskThumbnailViewDeprecated getThumbnail() {
-        return mTaskThumbnailViewDeprecated;
-    }
-
-    void refreshThumbnails(@Nullable HashMap<Integer, ThumbnailData> thumbnailDatas) {
-        if (enableRefactorTaskThumbnail()) {
-            // TODO(b/334825222) add thumbnail logic
-            return;
-        }
-        if (mTask != null && thumbnailDatas != null) {
-            final ThumbnailData thumbnailData = thumbnailDatas.get(mTask.key.id);
-            if (thumbnailData != null) {
-                mTaskThumbnailViewDeprecated.setThumbnail(mTask, thumbnailData);
-                return;
-            }
-        }
-
-        mTaskThumbnailViewDeprecated.refresh();
-    }
-
-    /** TODO(b/197033698) Remove all usages of above method and migrate to this one */
-    public TaskThumbnailViewDeprecated[] getThumbnails() {
-        return new TaskThumbnailViewDeprecated[]{mTaskThumbnailViewDeprecated};
-    }
-
-    public TaskViewIcon getIconView() {
-        return mIconView;
-    }
-
-    @Override
-    public boolean dispatchTouchEvent(MotionEvent ev) {
-        RecentsView recentsView = getRecentsView();
-        if (recentsView == null || getTask() == null) {
-            return false;
-        }
-        SplitSelectStateController splitSelectStateController =
-                recentsView.getSplitSelectController();
-        // Disable taps for split selection animation unless we have multiple tasks
-        boolean disableTapsForSplitSelect =
-                splitSelectStateController.isSplitSelectActive()
-                        && splitSelectStateController.getInitialTaskId() == getTask().key.id
-                        && !containsMultipleTasks();
-        if (disableTapsForSplitSelect) {
-            return false;
-        }
-
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            mLastTouchDownPosition.set(ev.getX(), ev.getY());
-        }
-        return super.dispatchTouchEvent(ev);
-    }
-
-    private void onClick(View view) {
-        if (getTask() == null) {
-            Log.d("b/310064698", "onClick - task is null");
-            return;
-        }
-        if (confirmSecondSplitSelectApp()) {
-            Log.d("b/310064698", mTask + " - onClick - split select is active");
-            return;
-        }
-        RunnableList callbackList = launchTasks();
-        Log.d("b/310064698", mTask + " - onClick - callbackList: " + callbackList);
-        if (callbackList != null) {
-            callbackList.add(() -> Log.d("b/310064698", Arrays.toString(
-                    getTaskIds()) + " - onClick - launchCompleted"));
-        }
-        mContainer.getStatsLogManager().logger().withItemInfo(getItemInfo())
-                .log(LAUNCHER_TASK_LAUNCH_TAP);
-    }
-
-    /**
-     * @return {@code true} if user is already in split select mode and this tap was to choose the
-     * second app. {@code false} otherwise
-     */
-    protected boolean confirmSecondSplitSelectApp() {
-        int index = getLastSelectedChildTaskIndex();
-        if (index >= mTaskIdAttributeContainer.length) {
-            return false;
-        }
-        TaskIdAttributeContainer container = mTaskIdAttributeContainer[index];
-        if (container != null) {
-            return getRecentsView().confirmSplitSelect(this, container.getTask(),
-                    container.getIconView().getDrawable(), container.getThumbnailView(),
-                    container.getThumbnailView().getThumbnail(), /* intent */ null,
-                    /* user */ null, container.getItemInfo());
-        }
-        return false;
-    }
-
-    /**
-     * Returns the task index of the last selected child task (0 or 1).
-     * If we contain multiple tasks and this TaskView is used as part of split selection, the
-     * selected child task index will be that of the remaining task.
-     */
-    protected int getLastSelectedChildTaskIndex() {
-        return 0;
-    }
-
-    /**
-     * Starts the task associated with this view and animates the startup.
-     *
-     * @return CompletionStage to indicate the animation completion or null if the launch failed.
-     */
-    @Nullable
-    public RunnableList launchTaskAnimated() {
-        if (mTask != null) {
-            TestLogging.recordEvent(
-                    TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
-            ActivityOptionsWrapper opts = mContainer.getActivityLaunchOptions(this, null);
-            opts.options.setLaunchDisplayId(
-                    getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
-            if (ActivityManagerWrapper.getInstance()
-                    .startActivityFromRecents(mTask.key, opts.options)) {
-                Log.d(TAG, "launchTaskAnimated - startActivityFromRecents: " + Arrays.toString(
-                        getTaskIds()));
-                ActiveGestureLog.INSTANCE.trackEvent(EXPECTING_TASK_APPEARED);
-                RecentsView recentsView = getRecentsView();
-                if (recentsView.getRunningTaskViewId() != -1) {
-                    recentsView.onTaskLaunchedInLiveTileMode();
-
-                    // Return a fresh callback in the live tile case, so that it's not accidentally
-                    // triggered by QuickstepTransitionManager.AppLaunchAnimationRunner.
-                    RunnableList callbackList = new RunnableList();
-                    recentsView.addSideTaskLaunchCallback(callbackList);
-                    return callbackList;
-                }
-                if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
-                    // If the recents transition is running (ie. in live tile mode), then the start
-                    // of a new task will merge into the existing transition and it currently will
-                    // not be run independently, so we need to rely on the onTaskAppeared() call
-                    // for the new task to trigger the side launch callback to flush this runnable
-                    // list (which is usually flushed when the app launch animation finishes)
-                    recentsView.addSideTaskLaunchCallback(opts.onEndCallback);
-                }
-                return opts.onEndCallback;
-            } else {
-                notifyTaskLaunchFailed(TAG);
-                return null;
-            }
-        } else {
-            Log.d(TAG, "launchTaskAnimated - mTask is null" + Arrays.toString(getTaskIds()));
-            return null;
-        }
-    }
-
-    /**
-     * Starts the task associated with this view without any animation
-     */
-    public void launchTask(@NonNull Consumer<Boolean> callback) {
-        launchTask(callback, false /* isQuickswitch */);
-    }
-
-    /**
-     * Starts the task associated with this view without any animation
-     */
-    public void launchTask(@NonNull Consumer<Boolean> callback, boolean isQuickswitch) {
-        if (mTask != null) {
-            TestLogging.recordEvent(
-                    TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
-
-            TaskRemovedDuringLaunchListener failureListener = new TaskRemovedDuringLaunchListener(
-                    getContext().getApplicationContext());
-            if (isQuickswitch) {
-                // We only listen for failures to launch in quickswitch because the during this
-                // gesture launcher is in the background state, vs other launches which are in
-                // the actual overview state
-                failureListener.register(mContainer, mTask.key.id, () -> {
-                    notifyTaskLaunchFailed(TAG);
-                    RecentsView rv = getRecentsView();
-                    if (rv != null) {
-                        // Disable animations for now, as it is an edge case and the app usually
-                        // covers launcher and also any state transition animation also gets
-                        // clobbered by QuickstepTransitionManager.createWallpaperOpenAnimations
-                        // when launcher shows again
-                        rv.startHome(false /* animated */);
-                        if (rv.mSizeStrategy.getTaskbarController() != null) {
-                            // LauncherTaskbarUIController depends on the launcher state when
-                            // checking whether to handle resume, but that can come in before
-                            // startHome() changes the state, so force-refresh here to ensure the
-                            // taskbar is updated
-                            rv.mSizeStrategy.getTaskbarController().refreshResumedState();
-                        }
-                    }
-                });
-            }
-            // Indicate success once the system has indicated that the transition has started
-            ActivityOptions opts = ActivityOptions.makeCustomTaskAnimation(getContext(), 0, 0,
-                    MAIN_EXECUTOR.getHandler(),
-                    elapsedRealTime -> callback.accept(true),
-                    elapsedRealTime -> failureListener.onTransitionFinished());
-            opts.setLaunchDisplayId(
-                    getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
-            if (isQuickswitch) {
-                opts.setFreezeRecentTasksReordering();
-            }
-            // TODO(b/334826842) add splash functionality to new TTV
-            if (!enableRefactorTaskThumbnail()) {
-                opts.setDisableStartingWindow(mTaskThumbnailViewDeprecated.shouldShowSplashView());
-            }
-            Task.TaskKey key = mTask.key;
-            UI_HELPER_EXECUTOR.execute(() -> {
-                if (!ActivityManagerWrapper.getInstance().startActivityFromRecents(key, opts)) {
-                    // If the call to start activity failed, then post the result immediately,
-                    // otherwise, wait for the animation start callback from the activity options
-                    // above
-                    MAIN_EXECUTOR.post(() -> {
-                        notifyTaskLaunchFailed(TAG);
-                        callback.accept(false);
-                    });
-                }
-                Log.d(TAG,
-                        "launchTask - startActivityFromRecents: " + Arrays.toString(getTaskIds()));
-            });
-        } else {
-            callback.accept(false);
-            Log.d(TAG, "launchTask - mTask is null" + Arrays.toString(getTaskIds()));
-        }
-    }
-
-    /**
-     * Launch of the current task (both live and inactive tasks) with an animation.
-     */
-    @Nullable
-    public RunnableList launchTasks() {
-        RecentsView recentsView = getRecentsView();
-        RemoteTargetHandle[] remoteTargetHandles = recentsView.mRemoteTargetHandles;
-        if (isRunningTask() && remoteTargetHandles != null) {
-            if (!mIsClickableAsLiveTile) {
-                Log.e(TAG, "TaskView is not clickable as a live tile; returning to home.");
-                return null;
-            }
-
-            mIsClickableAsLiveTile = false;
-            RemoteAnimationTargets targets;
-            if (remoteTargetHandles.length == 1) {
-                targets = remoteTargetHandles[0].getTransformParams().getTargetSet();
-            } else {
-                RemoteAnimationTarget[] apps = Arrays.stream(remoteTargetHandles)
-                        .flatMap(handle -> Stream.of(
-                                handle.getTransformParams().getTargetSet().apps))
-                        .toArray(RemoteAnimationTarget[]::new);
-                RemoteAnimationTarget[] wallpapers = Arrays.stream(remoteTargetHandles)
-                        .flatMap(handle -> Stream.of(
-                                handle.getTransformParams().getTargetSet().wallpapers))
-                        .toArray(RemoteAnimationTarget[]::new);
-                targets = new RemoteAnimationTargets(apps, wallpapers,
-                        remoteTargetHandles[0].getTransformParams().getTargetSet().nonApps,
-                        remoteTargetHandles[0].getTransformParams().getTargetSet().targetMode);
-            }
-            if (targets == null) {
-                // If the recents animation is cancelled somehow between the parent if block and
-                // here, try to launch the task as a non live tile task.
-                RunnableList runnableList = launchTaskAnimated();
-                if (runnableList == null) {
-                    Log.e(TAG, "Recents animation cancelled and cannot launch task as non-live tile"
-                            + "; returning to home");
-                }
-                mIsClickableAsLiveTile = true;
-                return runnableList;
-            }
-
-            RunnableList runnableList = new RunnableList();
-            AnimatorSet anim = new AnimatorSet();
-            TaskViewUtils.composeRecentsLaunchAnimator(
-                    anim, this, targets.apps,
-                    targets.wallpapers, targets.nonApps, true /* launcherClosing */,
-                    recentsView.getStateManager(), recentsView,
-                    recentsView.getDepthController());
-            anim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animator) {
-                    if (mTask != null && mTask.key.displayId != getRootViewDisplayId()) {
-                        launchTaskAnimated();
-                    }
-                    mIsClickableAsLiveTile = true;
-                    runEndCallback();
-                }
-
-                @Override
-                public void onAnimationCancel(Animator animation) {
-                    runEndCallback();
-                }
-
-                private void runEndCallback() {
-                    runnableList.executeAllAndDestroy();
-                }
-            });
-            anim.start();
-            Log.d(TAG, "launchTasks - composeRecentsLaunchAnimator: " + Arrays.toString(
-                    getTaskIds()));
-            recentsView.onTaskLaunchedInLiveTileMode();
-            return runnableList;
-        } else {
-            return launchTaskAnimated();
-        }
-    }
-
-    /**
-     * See {@link TaskDataChanges}
-     *
-     * @param visible If this task view will be visible to the user in overview or hidden
-     */
-    public void onTaskListVisibilityChanged(boolean visible) {
-        onTaskListVisibilityChanged(visible, FLAG_UPDATE_ALL);
-    }
-
-    /**
-     * See {@link TaskDataChanges}
-     *
-     * @param visible If this task view will be visible to the user in overview or hidden
-     */
-    public void onTaskListVisibilityChanged(boolean visible, @TaskDataChanges int changes) {
-        if (mTask == null) {
-            return;
-        }
-        cancelPendingLoadTasks();
-        if (visible) {
-            // These calls are no-ops if the data is already loaded, try and load the high
-            // resolution thumbnail if the state permits
-            RecentsModel model = RecentsModel.INSTANCE.get(getContext());
-            TaskThumbnailCache thumbnailCache = model.getThumbnailCache();
-            TaskIconCache iconCache = model.getIconCache();
-
-            if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
-                mThumbnailLoadRequest = thumbnailCache.updateThumbnailInBackground(
-                        mTask, thumbnail -> {
-                            if (!enableRefactorTaskThumbnail()) {
-                                // TODO(b/334825222) add thumbnail state
-                                mTaskThumbnailViewDeprecated.setThumbnail(mTask, thumbnail);
-                            }
-                        });
-            }
-            if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
-                mIconLoadRequest = iconCache.updateIconInBackground(mTask,
-                        (task) -> {
-                            setIcon(mIconView, task.icon);
-                            if (enableOverviewIconMenu()) {
-                                setText(mIconView, task.title);
-                            }
-                            mDigitalWellBeingToast.initialize(task);
-                        });
-            }
-            if (needsUpdate(changes, FLAG_UPDATE_CORNER_RADIUS)) {
-                mCurrentFullscreenParams.updateCornerRadius(getContext());
-            }
-        } else {
-            if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
-                if (!enableRefactorTaskThumbnail()) {
-                    // TODO(b/334825222) add thumbnail state
-                    mTaskThumbnailViewDeprecated.setThumbnail(null, null);
-                }
-                // Reset the task thumbnail reference as well (it will be fetched from the cache or
-                // reloaded next time we need it)
-                mTask.thumbnail = null;
-            }
-            if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
-                setIcon(mIconView, null);
-                if (enableOverviewIconMenu()) {
-                    setText(mIconView, null);
-                }
-            }
-        }
-    }
-
-    protected boolean needsUpdate(@TaskDataChanges int dataChange, @TaskDataChanges int flag) {
-        return (dataChange & flag) == flag;
-    }
-
-    protected void cancelPendingLoadTasks() {
-        if (mThumbnailLoadRequest != null) {
-            mThumbnailLoadRequest.cancel();
-            mThumbnailLoadRequest = null;
-        }
-        if (mIconLoadRequest != null) {
-            mIconLoadRequest.cancel();
-            mIconLoadRequest = null;
-        }
-    }
-
-    private boolean showTaskMenu(TaskViewIcon iconView) {
-        if (!getRecentsView().canLaunchFullscreenTask()) {
-            // Don't show menu when selecting second split screen app
-            return true;
-        }
-
-        if (!mContainer.getDeviceProfile().isTablet
-                && !getRecentsView().isClearAllHidden()) {
-            getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
-            return false;
-        } else {
-            mContainer.getStatsLogManager().logger().withItemInfo(getItemInfo())
-                    .log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS);
-            return showTaskMenuWithContainer(iconView);
-        }
-    }
-
-    protected boolean showTaskMenuWithContainer(TaskViewIcon iconView) {
-        Optional<TaskIdAttributeContainer> menuContainer = Arrays.stream(
-                mTaskIdAttributeContainer).filter(
-                        container -> container.getIconView() == iconView).findAny();
-        if (menuContainer.isEmpty()) {
-            return false;
-        }
-        DeviceProfile dp = mContainer.getDeviceProfile();
-        if (enableOverviewIconMenu() && iconView instanceof IconAppChipView) {
-            ((IconAppChipView) iconView).revealAnim(/* isRevealing= */ true);
-            return TaskMenuView.showForTask(menuContainer.get(),
-                    () -> ((IconAppChipView) iconView).revealAnim(/* isRevealing= */ false));
-        } else if (dp.isTablet) {
-            int alignedOptionIndex = 0;
-            if (getRecentsView().isOnGridBottomRow(menuContainer.get().getTaskView())
-                    && dp.isLandscape) {
-                if (Flags.enableGridOnlyOverview()) {
-                    // With no focused task, there is less available space below the tasks, so align
-                    // the arrow to the third option in the menu.
-                    alignedOptionIndex = 2;
-                } else {
-                    // Bottom row of landscape grid aligns arrow to second option to avoid clipping
-                    alignedOptionIndex = 1;
-                }
-            }
-            return TaskMenuViewWithArrow.Companion.showForTask(menuContainer.get(),
-                    alignedOptionIndex);
-        } else {
-            return TaskMenuView.showForTask(menuContainer.get());
-        }
-    }
-
-    protected void setIcon(TaskViewIcon iconView, @Nullable Drawable icon) {
-        if (icon != null) {
-            iconView.setDrawable(icon);
-            iconView.setOnClickListener(v -> {
-                if (confirmSecondSplitSelectApp()) {
-                    return;
-                }
-                showTaskMenu(iconView);
-            });
-            iconView.setOnLongClickListener(v -> {
-                requestDisallowInterceptTouchEvent(true);
-                return showTaskMenu(iconView);
-            });
-        } else {
-            iconView.setDrawable(null);
-            iconView.setOnClickListener(null);
-            iconView.setOnLongClickListener(null);
-        }
-    }
-
-    protected void setText(TaskViewIcon iconView, CharSequence text) {
-        iconView.setText(text);
-    }
-
-    public void setOrientationState(RecentsOrientedState orientationState) {
-        mIconView.setIconOrientation(orientationState, isGridTask());
-        setThumbnailOrientation(orientationState);
-    }
-
-    protected void setThumbnailOrientation(RecentsOrientedState orientationState) {
-        DeviceProfile deviceProfile = mContainer.getDeviceProfile();
-        int thumbnailTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
-
-        // TODO(b/271468547), we should default to setting trasnlations only on the snapshot instead
-        //  of a hybrid of both margins and translations
-        LayoutParams snapshotParams = (LayoutParams) getSnapshotView().getLayoutParams();
-        snapshotParams.topMargin = thumbnailTopMargin;
-        getSnapshotView().setLayoutParams(snapshotParams);
-
-        // TODO(b/335606129) Investigate the usage of [TaskOverlay] in the new TaskThumbnailView.
-        //  and if it's still necessary we should support that in the new TTV class.
-        if (!enableRefactorTaskThumbnail()) {
-            mTaskThumbnailViewDeprecated.getTaskOverlay().updateOrientationState(orientationState);
-        }
-        mDigitalWellBeingToast.initialize(mTask);
-    }
-
-    /**
-     * Returns whether the task is part of overview grid and not being focused.
-     */
-    public boolean isGridTask() {
-        DeviceProfile deviceProfile = mContainer.getDeviceProfile();
-        return deviceProfile.isTablet && !isFocusedTask();
-    }
-
-    /**
-     * Called to animate a smooth transition when going directly from an app into Overview (and
-     * vice versa). Icons fade in, and DWB banners slide in with a "shift up" animation.
-     */
-    protected void setIconsAndBannersTransitionProgress(float progress, boolean invert) {
-        if (invert) {
-            progress = 1 - progress;
-        }
-        mFocusTransitionProgress = progress;
-        float iconScalePercentage = (float) SCALE_ICON_DURATION / DIM_ANIM_DURATION;
-        float lowerClamp = invert ? 1f - iconScalePercentage : 0;
-        float upperClamp = invert ? 1 : iconScalePercentage;
-        float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, lowerClamp, upperClamp)
-                .getInterpolation(progress);
-        mIconView.setContentAlpha(scale);
-        mDigitalWellBeingToast.updateBannerOffset(1f - scale);
-    }
-
-    public void setIconScaleAnimStartProgress(float startProgress) {
-        mIconScaleAnimStartProgress = startProgress;
-    }
-
-    public void animateIconScaleAndDimIntoView() {
-        if (mIconAndDimAnimator != null) {
-            mIconAndDimAnimator.cancel();
-        }
-        mIconAndDimAnimator = ObjectAnimator.ofFloat(this, FOCUS_TRANSITION, 1);
-        mIconAndDimAnimator.setCurrentFraction(mIconScaleAnimStartProgress);
-        mIconAndDimAnimator.setDuration(DIM_ANIM_DURATION).setInterpolator(LINEAR);
-        mIconAndDimAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mIconAndDimAnimator = null;
-            }
-        });
-        mIconAndDimAnimator.start();
-    }
-
-    protected void setIconScaleAndDim(float iconScale) {
-        setIconScaleAndDim(iconScale, false);
-    }
-
-    private void setIconScaleAndDim(float iconScale, boolean invert) {
-        if (mIconAndDimAnimator != null) {
-            mIconAndDimAnimator.cancel();
-        }
-        setIconsAndBannersTransitionProgress(iconScale, invert);
-    }
-
-    protected void resetPersistentViewTransforms() {
-        mNonGridTranslationX = mGridTranslationX =
-                mGridTranslationY = mBoxTranslationY = mNonGridPivotTranslationX = 0f;
-        resetViewTransforms();
-    }
-
-    protected void resetViewTransforms() {
-        // fullscreenTranslation and accumulatedTranslation should not be reset, as
-        // resetViewTransforms is called during Quickswitch scrolling.
-        mDismissTranslationX = mTaskOffsetTranslationX =
-                mTaskResistanceTranslationX = mSplitSelectTranslationX = mGridEndTranslationX = 0f;
-        mDismissTranslationY = mTaskOffsetTranslationY = mTaskResistanceTranslationY = 0f;
-        if (getRecentsView() == null || !getRecentsView().isSplitSelectionActive()) {
-            mSplitSelectTranslationY = 0f;
-        }
-
-        setSnapshotScale(1f);
-        applyTranslationX();
-        applyTranslationY();
-        setTranslationZ(0);
-        setAlpha(mStableAlpha);
-        setIconScaleAndDim(1);
-        setColorTint(0, 0);
-        if (!enableRefactorTaskThumbnail()) {
-            // TODO(b/335399428) add split select functionality to new TTV
-            mTaskThumbnailViewDeprecated.resetViewTransforms();
-        }
-    }
-
-    public void setStableAlpha(float parentAlpha) {
-        mStableAlpha = parentAlpha;
-        setAlpha(mStableAlpha);
-    }
-
-    @Override
-    public void onRecycle() {
-        resetPersistentViewTransforms();
-        // Clear any references to the thumbnail (it will be re-read either from the cache or the
-        // system on next bind)
-        // TODO(b/334825222): Implement thumbnail/snapshot for the new [TaskThumbnailView].
-        if (enableRefactorTaskThumbnail()) {
-            notifyIsRunningTaskUpdated();
-        } else {
-            mTaskThumbnailViewDeprecated.setThumbnail(mTask, null);
-        }
-        setOverlayEnabled(false);
-        onTaskListVisibilityChanged(false);
-        mBorderEnabled = false;
-    }
-
-    public float getTaskCornerRadius() {
-        return mCurrentFullscreenParams.mCornerRadius;
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        DeviceProfile deviceProfile = mContainer.getDeviceProfile();
-        int thumbnailTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
-        if (deviceProfile.isTablet) {
-            setPivotX(getLayoutDirection() == LAYOUT_DIRECTION_RTL ? 0 : right - left);
-            setPivotY(thumbnailTopMargin);
-        } else {
-            setPivotX((right - left) * 0.5f);
-            setPivotY(thumbnailTopMargin + (getHeight() - thumbnailTopMargin) * 0.5f);
-        }
-        SYSTEM_GESTURE_EXCLUSION_RECT.get(0).set(0, 0, getWidth(), getHeight());
-        setSystemGestureExclusionRects(SYSTEM_GESTURE_EXCLUSION_RECT);
-    }
-
-    /**
-     * How much to scale down pages near the edge of the screen.
-     */
-    public static float getEdgeScaleDownFactor(DeviceProfile deviceProfile) {
-        return deviceProfile.isTablet ? EDGE_SCALE_DOWN_FACTOR_GRID
-                : EDGE_SCALE_DOWN_FACTOR_CAROUSEL;
-    }
-
-    private void setNonGridScale(float nonGridScale) {
-        mNonGridScale = nonGridScale;
-        applyScale();
-    }
-
-    public float getNonGridScale() {
-        return mNonGridScale;
-    }
-
-    private void setSnapshotScale(float dismissScale) {
-        mDismissScale = dismissScale;
-        applyScale();
-    }
-
-    /**
-     * Moves TaskView between carousel and 2 row grid.
-     *
-     * @param gridProgress 0 = carousel; 1 = 2 row grid.
-     */
-    public void setGridProgress(float gridProgress) {
-        mGridProgress = gridProgress;
-        applyTranslationX();
-        applyTranslationY();
-        applyScale();
-    }
-
-    private void applyScale() {
-        float scale = 1;
-        scale *= getPersistentScale();
-        scale *= mDismissScale;
-        setScaleX(scale);
-        setScaleY(scale);
-        updateSnapshotRadius();
-    }
-
-    /**
-     * Returns multiplication of scale that is persistent (e.g. fullscreen and grid), and does not
-     * change according to a temporary state.
-     */
-    public float getPersistentScale() {
-        float scale = 1;
-        scale *= Utilities.mapRange(mGridProgress, mNonGridScale, 1f);
-        return scale;
-    }
-
-    /**
-     * Updates alpha of task thumbnail splash on swipe up/down.
-     */
-    public void setTaskThumbnailSplashAlpha(float taskThumbnailSplashAlpha) {
-        mTaskThumbnailSplashAlpha = taskThumbnailSplashAlpha;
-        applyThumbnailSplashAlpha();
-    }
-
-    protected void applyThumbnailSplashAlpha() {
-        if (!enableRefactorTaskThumbnail()) {
-            // TODO(b/334826842) add splash functionality to new TTV
-            mTaskThumbnailViewDeprecated.setSplashAlpha(mTaskThumbnailSplashAlpha);
-        }
-    }
-
-    protected void refreshTaskThumbnailSplash() {
-        if (!enableRefactorTaskThumbnail()) {
-            // TODO(b/334826842) add splash functionality to new TTV
-            mTaskThumbnailViewDeprecated.refreshSplashView();
-        }
-    }
-
-    private void setSplitSelectTranslationX(float x) {
-        mSplitSelectTranslationX = x;
-        applyTranslationX();
-    }
-
-    private void setSplitSelectTranslationY(float y) {
-        mSplitSelectTranslationY = y;
-        applyTranslationY();
-    }
-
-    private void setDismissTranslationX(float x) {
-        mDismissTranslationX = x;
-        applyTranslationX();
-    }
-
-    private void setDismissTranslationY(float y) {
-        mDismissTranslationY = y;
-        applyTranslationY();
-    }
-
-    private void setTaskOffsetTranslationX(float x) {
-        mTaskOffsetTranslationX = x;
-        applyTranslationX();
-    }
-
-    private void setTaskOffsetTranslationY(float y) {
-        mTaskOffsetTranslationY = y;
-        applyTranslationY();
-    }
-
-    private void setTaskResistanceTranslationX(float x) {
-        mTaskResistanceTranslationX = x;
-        applyTranslationX();
-    }
-
-    private void setTaskResistanceTranslationY(float y) {
-        mTaskResistanceTranslationY = y;
-        applyTranslationY();
-    }
-
-    public float getNonGridTranslationX() {
-        return mNonGridTranslationX;
-    }
-
-    /**
-     * Updates X coordinate of non-grid translation.
-     */
-    public void setNonGridTranslationX(float nonGridTranslationX) {
-        mNonGridTranslationX = nonGridTranslationX;
-        applyTranslationX();
-    }
-
-    public void setGridTranslationX(float gridTranslationX) {
-        mGridTranslationX = gridTranslationX;
-        applyTranslationX();
-    }
-
-    public float getGridTranslationX() {
-        return mGridTranslationX;
-    }
-
-    public void setGridTranslationY(float gridTranslationY) {
-        mGridTranslationY = gridTranslationY;
-        applyTranslationY();
-    }
-
-    public float getGridTranslationY() {
-        return mGridTranslationY;
-    }
-
-    private void setGridEndTranslationX(float gridEndTranslationX) {
-        mGridEndTranslationX = gridEndTranslationX;
-        applyTranslationX();
-    }
-
-    /**
-     * Set translation X for non-grid pivot
-     */
-    public void setNonGridPivotTranslationX(float nonGridPivotTranslationX) {
-        mNonGridPivotTranslationX = nonGridPivotTranslationX;
-        applyTranslationX();
-    }
-
-    public float getScrollAdjustment(boolean gridEnabled) {
-        float scrollAdjustment = 0;
-        if (gridEnabled) {
-            scrollAdjustment += mGridTranslationX;
-        } else {
-            scrollAdjustment += getNonGridTranslationX();
-        }
-        return scrollAdjustment;
-    }
-
-    public float getOffsetAdjustment(boolean gridEnabled) {
-        return getScrollAdjustment(gridEnabled);
-    }
-
-    public float getSizeAdjustment(boolean fullscreenEnabled) {
-        float sizeAdjustment = 1;
-        if (fullscreenEnabled) {
-            sizeAdjustment *= mNonGridScale;
-        }
-        return sizeAdjustment;
-    }
-
-    private void setBoxTranslationY(float boxTranslationY) {
-        mBoxTranslationY = boxTranslationY;
-        applyTranslationY();
-    }
-
-    private void applyTranslationX() {
-        setTranslationX(mDismissTranslationX + mTaskOffsetTranslationX + mTaskResistanceTranslationX
-                + mSplitSelectTranslationX + mGridEndTranslationX + getPersistentTranslationX());
-    }
-
-    private void applyTranslationY() {
-        setTranslationY(mDismissTranslationY + mTaskOffsetTranslationY + mTaskResistanceTranslationY
-                + mSplitSelectTranslationY + getPersistentTranslationY());
-    }
-
-    /**
-     * Returns addition of translationX that is persistent (e.g. fullscreen and grid), and does not
-     * change according to a temporary state (e.g. task offset).
-     */
-    public float getPersistentTranslationX() {
-        return getNonGridTrans(mNonGridTranslationX) + getGridTrans(mGridTranslationX)
-                + getNonGridTrans(mNonGridPivotTranslationX);
-    }
-
-    /**
-     * Returns addition of translationY that is persistent (e.g. fullscreen and grid), and does not
-     * change according to a temporary state (e.g. task offset).
-     */
-    public float getPersistentTranslationY() {
-        return mBoxTranslationY + getGridTrans(mGridTranslationY);
-    }
-
-    public FloatProperty<TaskView> getPrimarySplitTranslationProperty() {
-        return getPagedOrientationHandler().getPrimaryValue(
-                SPLIT_SELECT_TRANSLATION_X, SPLIT_SELECT_TRANSLATION_Y);
-    }
-
-    public FloatProperty<TaskView> getSecondarySplitTranslationProperty() {
-        return getPagedOrientationHandler().getSecondaryValue(
-                SPLIT_SELECT_TRANSLATION_X, SPLIT_SELECT_TRANSLATION_Y);
-    }
-
-    public FloatProperty<TaskView> getPrimaryDismissTranslationProperty() {
-        return getPagedOrientationHandler().getPrimaryValue(
-                DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
-    }
-
-    public FloatProperty<TaskView> getSecondaryDismissTranslationProperty() {
-        return getPagedOrientationHandler().getSecondaryValue(
-                DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
-    }
-
-    public FloatProperty<TaskView> getPrimaryTaskOffsetTranslationProperty() {
-        return getPagedOrientationHandler().getPrimaryValue(
-                TASK_OFFSET_TRANSLATION_X, TASK_OFFSET_TRANSLATION_Y);
-    }
-
-    public FloatProperty<TaskView> getSecondaryTaskOffsetTranslationProperty() {
-        return getPagedOrientationHandler().getSecondaryValue(
-                TASK_OFFSET_TRANSLATION_X, TASK_OFFSET_TRANSLATION_Y);
-    }
-
-    public FloatProperty<TaskView> getTaskResistanceTranslationProperty() {
-        return getPagedOrientationHandler().getSecondaryValue(
-                TASK_RESISTANCE_TRANSLATION_X, TASK_RESISTANCE_TRANSLATION_Y);
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        // TODO: Clip-out the icon region from the thumbnail, since they are overlapping.
-        return false;
-    }
-
-    public boolean isEndQuickswitchCuj() {
-        return mEndQuickswitchCuj;
-    }
-
-    public void setEndQuickswitchCuj(boolean endQuickswitchCuj) {
-        mEndQuickswitchCuj = endQuickswitchCuj;
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-
-        info.addAction(
-                new AccessibilityNodeInfo.AccessibilityAction(R.string.accessibility_close,
-                        getContext().getText(R.string.accessibility_close)));
-
-        final Context context = getContext();
-        for (TaskIdAttributeContainer taskContainer : mTaskIdAttributeContainer) {
-            for (SystemShortcut s : TraceHelper.allowIpcs(
-                    "TV.a11yInfo", () -> getEnabledShortcuts(this, taskContainer))) {
-                info.addAction(s.createAccessibilityAction(context));
-            }
-        }
-
-        if (mDigitalWellBeingToast.hasLimit()) {
-            info.addAction(
-                    new AccessibilityNodeInfo.AccessibilityAction(
-                            R.string.accessibility_app_usage_settings,
-                            getContext().getText(R.string.accessibility_app_usage_settings)));
-        }
-
-        final RecentsView recentsView = getRecentsView();
-        final AccessibilityNodeInfo.CollectionItemInfo itemInfo =
-                AccessibilityNodeInfo.CollectionItemInfo.obtain(
-                        0, 1, recentsView.getTaskViewCount() - recentsView.indexOfChild(this) - 1,
-                        1, false);
-        info.setCollectionItemInfo(itemInfo);
-    }
-
-    @Override
-    public boolean performAccessibilityAction(int action, Bundle arguments) {
-        if (action == R.string.accessibility_close) {
-            getRecentsView().dismissTask(this, true /*animateTaskView*/,
-                    true /*removeTask*/);
-            return true;
-        }
-
-        if (action == R.string.accessibility_app_usage_settings) {
-            mDigitalWellBeingToast.openAppUsageSettings(this);
-            return true;
-        }
-
-        for (TaskIdAttributeContainer taskContainer : mTaskIdAttributeContainer) {
-            for (SystemShortcut s : getEnabledShortcuts(this,
-                    taskContainer)) {
-                if (s.hasHandlerForAction(action)) {
-                    s.onClick(this);
-                    return true;
-                }
-            }
-        }
-
-        return super.performAccessibilityAction(action, arguments);
-    }
-
-    @Nullable
-    public RecentsView getRecentsView() {
-        return (RecentsView) getParent();
-    }
-
-    RecentsPagedOrientationHandler getPagedOrientationHandler() {
-        return getRecentsView().mOrientationState.getOrientationHandler();
-    }
-
-    private void notifyTaskLaunchFailed(String tag) {
-        String msg = "Failed to launch task";
-        if (mTask != null) {
-            msg += " (task=" + mTask.key.baseIntent + " userId=" + mTask.key.userId + ")";
-        }
-        Log.w(tag, msg);
-        Toast.makeText(getContext(), R.string.activity_not_available, LENGTH_SHORT).show();
-    }
-
-    /**
-     * Hides the icon and shows insets when this TaskView is about to be shown fullscreen.
-     *
-     * @param progress: 0 = show icon and no insets; 1 = don't show icon and show full insets.
-     */
-    public void setFullscreenProgress(float progress) {
-        progress = Utilities.boundToRange(progress, 0, 1);
-        mFullscreenProgress = progress;
-        mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
-        if (!enableRefactorTaskThumbnail()) {
-            // TODO(b/334826840) Add corner rounding to new TTV
-            mTaskThumbnailViewDeprecated.getTaskOverlay().setFullscreenProgress(progress);
-        }
-
-        RecentsView recentsView = mContainer.getOverviewPanel();
-        // Animate icons and DWB banners in/out, except in QuickSwitch state, when tiles are
-        // oversized and banner would look disproportionately large.
-        if (recentsView.getStateManager().getState() != BACKGROUND_APP) {
-            setIconsAndBannersTransitionProgress(progress, true);
-        }
-
-        updateSnapshotRadius();
-    }
-
-    protected void updateSnapshotRadius() {
-        updateCurrentFullscreenParams();
-        if (!enableRefactorTaskThumbnail()) {
-            // TODO(b/334826840) Add corner rounding to new TTV
-            mTaskThumbnailViewDeprecated.setFullscreenParams(mCurrentFullscreenParams);
-        }
-    }
-
-    void updateCurrentFullscreenParams() {
-        updateFullscreenParams(mCurrentFullscreenParams);
-    }
-
-    protected void updateFullscreenParams(TaskView.FullscreenDrawParams fullscreenParams) {
-        if (getRecentsView() == null) {
-            return;
-        }
-        fullscreenParams.setProgress(mFullscreenProgress, getRecentsView().getScaleX(),
-                getScaleX());
-    }
-
-    /**
-     * Updates TaskView scaling and translation required to support variable width if enabled, while
-     * ensuring TaskView fits into screen in fullscreen.
-     */
-    void updateTaskSize() {
-        ViewGroup.LayoutParams params = getLayoutParams();
-        float nonGridScale;
-        float boxTranslationY;
-        int expectedWidth;
-        int expectedHeight;
-        DeviceProfile deviceProfile = mContainer.getDeviceProfile();
-        final int thumbnailPadding = deviceProfile.overviewTaskThumbnailTopMarginPx;
-        final Rect lastComputedTaskSize = getRecentsView().getLastComputedTaskSize();
-        final int taskWidth = lastComputedTaskSize.width();
-        final int taskHeight = lastComputedTaskSize.height();
-        if (deviceProfile.isTablet) {
-            int boxWidth;
-            int boxHeight;
-            boolean isFocusedTask = isFocusedTask();
-            if (isFocusedTask) {
-                // Task will be focused and should use focused task size. Use focusTaskRatio
-                // that is associated with the original orientation of the focused task.
-                boxWidth = taskWidth;
-                boxHeight = taskHeight;
-            } else {
-                // Otherwise task is in grid, and should use lastComputedGridTaskSize.
-                Rect lastComputedGridTaskSize = getRecentsView().getLastComputedGridTaskSize();
-                boxWidth = lastComputedGridTaskSize.width();
-                boxHeight = lastComputedGridTaskSize.height();
-            }
-
-            // Bound width/height to the box size.
-            expectedWidth = boxWidth;
-            expectedHeight = boxHeight + thumbnailPadding;
-
-            // Scale to to fit task Rect.
-            if (enableGridOnlyOverview()) {
-                final Rect lastComputedCarouselTaskSize =
-                        getRecentsView().getLastComputedCarouselTaskSize();
-                nonGridScale = lastComputedCarouselTaskSize.width() / (float) taskWidth;
-            } else {
-                nonGridScale = taskWidth / (float) boxWidth;
-            }
-
-            // Align to top of task Rect.
-            boxTranslationY = (expectedHeight - thumbnailPadding - taskHeight) / 2.0f;
-        } else {
-            nonGridScale = 1f;
-            boxTranslationY = 0f;
-            expectedWidth = enableOverviewIconMenu() ? taskWidth : LayoutParams.MATCH_PARENT;
-            expectedHeight = enableOverviewIconMenu()
-                    ? taskHeight + thumbnailPadding
-                    : LayoutParams.MATCH_PARENT;
-        }
-
-        setNonGridScale(nonGridScale);
-        setBoxTranslationY(boxTranslationY);
-        if (params.width != expectedWidth || params.height != expectedHeight) {
-            params.width = expectedWidth;
-            params.height = expectedHeight;
-            setLayoutParams(params);
-        }
-    }
-
-    private float getGridTrans(float endTranslation) {
-        return Utilities.mapRange(mGridProgress, 0, endTranslation);
-    }
-
-    private float getNonGridTrans(float endTranslation) {
-        return endTranslation - getGridTrans(endTranslation);
-    }
-
-    public boolean isRunningTask() {
-        if (getRecentsView() == null) {
-            return false;
-        }
-        return this == getRecentsView().getRunningTaskView();
-    }
-
-    public boolean isFocusedTask() {
-        if (getRecentsView() == null) {
-            return false;
-        }
-        return this == getRecentsView().getFocusedTaskView();
-    }
-
-    public void setShowScreenshot(boolean showScreenshot) {
-        mShowScreenshot = showScreenshot;
-    }
-
-    public boolean showScreenshot() {
-        if (!isRunningTask()) {
-            return true;
-        }
-        return mShowScreenshot;
-    }
-
-    public void setOverlayEnabled(boolean overlayEnabled) {
-        // TODO(b/335606129) Investigate the usage of [TaskOverlay] in the new TaskThumbnailView.
-        //  and if it's still necessary we should support that in the new TTV class.
-        if (!enableRefactorTaskThumbnail()) {
-            mTaskThumbnailViewDeprecated.setOverlayEnabled(overlayEnabled);
-        }
-    }
-
-    public void initiateSplitSelect(SplitPositionOption splitPositionOption) {
-        getRecentsView().initiateSplitSelect(this, splitPositionOption.stagePosition,
-                getLogEventForPosition(splitPositionOption.stagePosition));
-    }
-
-    /**
-     * Set a color tint on the snapshot and supporting views.
-     */
-    public void setColorTint(float amount, int tintColor) {
-        if (!enableRefactorTaskThumbnail()) {
-            // TODO(b/334832108) Add scrim to new TTV
-            mTaskThumbnailViewDeprecated.setDimAlpha(amount);
-        }
-        mIconView.setIconColorTint(tintColor, amount);
-        mDigitalWellBeingToast.setBannerColorTint(tintColor, amount);
-    }
-
-
-    private int getRootViewDisplayId() {
-        Display display = getRootView().getDisplay();
-        return display != null ? display.getDisplayId() : DEFAULT_DISPLAY;
-    }
-
-    /**
-     * Sets visibility for the thumbnail and associated elements (DWB banners and action chips).
-     * IconView is unaffected.
-     *
-     * @param taskId is only used when setting visibility to a non-{@link View#VISIBLE} value
-     */
-    void setThumbnailVisibility(int visibility, int taskId) {
-        for (int i = 0; i < getChildCount(); i++) {
-            View child = getChildAt(i);
-            if (child != mIconView) {
-                child.setVisibility(visibility);
-            }
-        }
-    }
-
-    private View getSnapshotView() {
-        return enableRefactorTaskThumbnail() ? mTaskThumbnailView : mTaskThumbnailViewDeprecated;
-    }
-
-    /**
-     * We update and subsequently draw these in {@link #setFullscreenProgress(float)}.
-     */
-    public static class FullscreenDrawParams implements SafeCloseable {
-
-        private float mCornerRadius;
-        private float mWindowCornerRadius;
-
-        public float mCurrentDrawnCornerRadius;
-
-        public FullscreenDrawParams(Context context) {
-            updateCornerRadius(context);
-        }
-
-        /** Recomputes the start and end corner radius for the given Context. */
-        public void updateCornerRadius(Context context) {
-            mCornerRadius = computeTaskCornerRadius(context);
-            mWindowCornerRadius = computeWindowCornerRadius(context);
-        }
-
-        @VisibleForTesting
-        public float computeTaskCornerRadius(Context context) {
-            return TaskCornerRadius.get(context);
-        }
-
-        @VisibleForTesting
-        public float computeWindowCornerRadius(Context context) {
-            return QuickStepContract.getWindowCornerRadius(context);
-        }
-
-        /**
-         * Sets the progress in range [0, 1]
-         */
-        public void setProgress(float fullscreenProgress, float parentScale, float taskViewScale) {
-            mCurrentDrawnCornerRadius =
-                    Utilities.mapRange(fullscreenProgress, mCornerRadius, mWindowCornerRadius)
-                            / parentScale / taskViewScale;
-        }
-
-        @Override
-        public void close() {
-        }
-    }
-
-    public class TaskIdAttributeContainer {
-        private final TaskThumbnailViewDeprecated mThumbnailView;
-        private final Task mTask;
-        private final TaskViewIcon mIconView;
-        /** Defaults to STAGE_POSITION_UNDEFINED if in not a split screen task view */
-        private @SplitConfigurationOptions.StagePosition int mStagePosition;
-        @IdRes
-        private final int mA11yNodeId;
-
-        public TaskIdAttributeContainer(Task task, TaskThumbnailViewDeprecated thumbnailView,
-                TaskViewIcon iconView, int stagePosition) {
-            this.mTask = task;
-            this.mThumbnailView = thumbnailView;
-            this.mIconView = iconView;
-            this.mStagePosition = stagePosition;
-            this.mA11yNodeId = (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) ?
-                    R.id.split_bottomRight_appInfo : R.id.split_topLeft_appInfo;
-        }
-
-        public TaskThumbnailViewDeprecated getThumbnailView() {
-            return mThumbnailView;
-        }
-
-        public Task getTask() {
-            return mTask;
-        }
-
-        public WorkspaceItemInfo getItemInfo() {
-            return TaskView.this.getItemInfo(mTask);
-        }
-
-        public TaskView getTaskView() {
-            return TaskView.this;
-        }
-
-        public TaskViewIcon getIconView() {
-            return mIconView;
-        }
-
-        public int getStagePosition() {
-            return mStagePosition;
-        }
-
-        void setStagePosition(@SplitConfigurationOptions.StagePosition int stagePosition) {
-            this.mStagePosition = stagePosition;
-        }
-
-        public int getA11yNodeId() {
-            return mA11yNodeId;
-        }
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
new file mode 100644
index 0000000..1490fd0
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -0,0 +1,1701 @@
+/*
+ * Copyright (C) 2017 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.views
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
+import android.animation.ObjectAnimator
+import android.annotation.IdRes
+import android.app.ActivityOptions
+import android.content.Context
+import android.content.Intent
+import android.graphics.Canvas
+import android.graphics.PointF
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.os.Bundle
+import android.util.AttributeSet
+import android.util.FloatProperty
+import android.util.Log
+import android.view.Display
+import android.view.MotionEvent
+import android.view.View
+import android.view.View.OnClickListener
+import android.view.ViewGroup
+import android.view.ViewStub
+import android.view.accessibility.AccessibilityNodeInfo
+import android.widget.FrameLayout
+import android.widget.Toast
+import androidx.annotation.IntDef
+import androidx.annotation.VisibleForTesting
+import androidx.core.view.updateLayoutParams
+import com.android.app.animation.Interpolators
+import com.android.launcher3.Flags.enableCursorHoverStates
+import com.android.launcher3.Flags.enableFocusOutline
+import com.android.launcher3.Flags.enableGridOnlyOverview
+import com.android.launcher3.Flags.enableOverviewIconMenu
+import com.android.launcher3.Flags.enableRefactorTaskThumbnail
+import com.android.launcher3.Flags.privateSpaceRestrictAccessibilityDrag
+import com.android.launcher3.LauncherSettings
+import com.android.launcher3.LauncherState
+import com.android.launcher3.R
+import com.android.launcher3.Utilities
+import com.android.launcher3.config.FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.model.data.ItemInfoWithIcon
+import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.pm.UserCache
+import com.android.launcher3.testing.TestLogging
+import com.android.launcher3.testing.shared.TestProtocol
+import com.android.launcher3.util.CancellableTask
+import com.android.launcher3.util.Executors
+import com.android.launcher3.util.RunnableList
+import com.android.launcher3.util.SafeCloseable
+import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition
+import com.android.launcher3.util.TraceHelper
+import com.android.launcher3.util.TransformingTouchDelegate
+import com.android.launcher3.util.ViewPool
+import com.android.launcher3.util.rects.set
+import com.android.quickstep.RecentsModel
+import com.android.quickstep.RemoteAnimationTargets
+import com.android.quickstep.TaskAnimationManager
+import com.android.quickstep.TaskOverlayFactory
+import com.android.quickstep.TaskOverlayFactory.TaskOverlay
+import com.android.quickstep.TaskUtils
+import com.android.quickstep.TaskViewUtils
+import com.android.quickstep.orientation.RecentsPagedOrientationHandler
+import com.android.quickstep.task.thumbnail.TaskThumbnail
+import com.android.quickstep.task.thumbnail.TaskThumbnailView
+import com.android.quickstep.task.viewmodel.TaskViewData
+import com.android.quickstep.util.ActiveGestureErrorDetector
+import com.android.quickstep.util.ActiveGestureLog
+import com.android.quickstep.util.BorderAnimator
+import com.android.quickstep.util.BorderAnimator.Companion.createSimpleBorderAnimator
+import com.android.quickstep.util.RecentsOrientedState
+import com.android.quickstep.util.TaskCornerRadius
+import com.android.quickstep.util.TaskRemovedDuringLaunchListener
+import com.android.systemui.shared.recents.model.Task
+import com.android.systemui.shared.recents.model.ThumbnailData
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import com.android.systemui.shared.system.QuickStepContract
+
+/** A task in the Recents view. */
+open class TaskView
+@JvmOverloads
+constructor(
+    context: Context,
+    attrs: AttributeSet? = null,
+    defStyleAttr: Int = 0,
+    defStyleRes: Int = 0,
+    focusBorderAnimator: BorderAnimator? = null,
+    hoverBorderAnimator: BorderAnimator? = null
+) : FrameLayout(context, attrs), ViewPool.Reusable {
+    /**
+     * Used in conjunction with [onTaskListVisibilityChanged], providing more granularity on which
+     * components of this task require an update
+     */
+    @Retention(AnnotationRetention.SOURCE)
+    @IntDef(FLAG_UPDATE_ALL, FLAG_UPDATE_ICON, FLAG_UPDATE_THUMBNAIL, FLAG_UPDATE_CORNER_RADIUS)
+    annotation class TaskDataChanges
+
+    /** Type of task view */
+    @Retention(AnnotationRetention.SOURCE)
+    @IntDef(Type.SINGLE, Type.GROUPED, Type.DESKTOP)
+    annotation class Type {
+        companion object {
+            const val SINGLE = 1
+            const val GROUPED = 2
+            const val DESKTOP = 3
+        }
+    }
+
+    val taskViewData = TaskViewData()
+    val taskIds: IntArray
+        /** Returns a copy of integer array containing taskIds of all tasks in the TaskView. */
+        get() = taskContainers.map { it.task.key.id }.toIntArray()
+    val thumbnailViews: Array<TaskThumbnailViewDeprecated>
+        get() = taskContainers.map { it.thumbnailViewDeprecated }.toTypedArray()
+    val isGridTask: Boolean
+        /** Returns whether the task is part of overview grid and not being focused. */
+        get() = container.deviceProfile.isTablet && !isFocusedTask
+    val isRunningTask: Boolean
+        get() = this === recentsView?.runningTaskView
+    val isFocusedTask: Boolean
+        get() = this === recentsView?.focusedTaskView
+    val taskCornerRadius: Float
+        get() = currentFullscreenParams.cornerRadius
+    val recentsView: RecentsView<*, *>?
+        get() = parent as? RecentsView<*, *>
+    val pagedOrientationHandler: RecentsPagedOrientationHandler
+        get() = orientedState.orientationHandler
+
+    @get:Deprecated("Use [taskContainers] instead.")
+    val firstTask: Task
+        /** Returns the first task bound to this TaskView. */
+        get() = taskContainers[0].task
+    @get:Deprecated("Use [taskContainers] instead.")
+    val firstThumbnailViewDeprecated: TaskThumbnailViewDeprecated
+        /** Returns the first thumbnailView of the TaskView. */
+        get() = taskContainers[0].thumbnailViewDeprecated
+    @get:Deprecated("Use [taskContainers] instead.")
+    val firstItemInfo: ItemInfo
+        get() = taskContainers[0].itemInfo
+
+    private val currentFullscreenParams = FullscreenDrawParams(context)
+    protected val container: RecentsViewContainer =
+        RecentsViewContainer.containerFromContext(context)
+    protected val lastTouchDownPosition = PointF()
+
+    // Derived view properties
+    protected val persistentScale: Float
+        /**
+         * Returns multiplication of scale that is persistent (e.g. fullscreen and grid), and does
+         * not change according to a temporary state.
+         */
+        get() = Utilities.mapRange(gridProgress, nonGridScale, 1f)
+    protected val persistentTranslationX: Float
+        /**
+         * Returns addition of translationX that is persistent (e.g. fullscreen and grid), and does
+         * not change according to a temporary state (e.g. task offset).
+         */
+        get() =
+            (getNonGridTrans(nonGridTranslationX) +
+                getGridTrans(this.gridTranslationX) +
+                getNonGridTrans(nonGridPivotTranslationX))
+    protected val persistentTranslationY: Float
+        /**
+         * Returns addition of translationY that is persistent (e.g. fullscreen and grid), and does
+         * not change according to a temporary state (e.g. task offset).
+         */
+        get() = boxTranslationY + getGridTrans(gridTranslationY)
+    protected val primarySplitTranslationProperty: FloatProperty<TaskView>
+        get() =
+            pagedOrientationHandler.getPrimaryValue(
+                SPLIT_SELECT_TRANSLATION_X,
+                SPLIT_SELECT_TRANSLATION_Y
+            )
+    protected val secondarySplitTranslationProperty: FloatProperty<TaskView>
+        get() =
+            pagedOrientationHandler.getSecondaryValue(
+                SPLIT_SELECT_TRANSLATION_X,
+                SPLIT_SELECT_TRANSLATION_Y
+            )
+    protected val primaryDismissTranslationProperty: FloatProperty<TaskView>
+        get() =
+            pagedOrientationHandler.getPrimaryValue(DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y)
+    protected val secondaryDismissTranslationProperty: FloatProperty<TaskView>
+        get() =
+            pagedOrientationHandler.getSecondaryValue(DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y)
+    protected val primaryTaskOffsetTranslationProperty: FloatProperty<TaskView>
+        get() =
+            pagedOrientationHandler.getPrimaryValue(
+                TASK_OFFSET_TRANSLATION_X,
+                TASK_OFFSET_TRANSLATION_Y
+            )
+    protected val secondaryTaskOffsetTranslationProperty: FloatProperty<TaskView>
+        get() =
+            pagedOrientationHandler.getSecondaryValue(
+                TASK_OFFSET_TRANSLATION_X,
+                TASK_OFFSET_TRANSLATION_Y
+            )
+    protected val taskResistanceTranslationProperty: FloatProperty<TaskView>
+        get() =
+            pagedOrientationHandler.getSecondaryValue(
+                TASK_RESISTANCE_TRANSLATION_X,
+                TASK_RESISTANCE_TRANSLATION_Y
+            )
+
+    private val tempCoordinates = FloatArray(2)
+    private val focusBorderAnimator: BorderAnimator?
+    private val hoverBorderAnimator: BorderAnimator?
+    private val rootViewDisplayId: Int
+        get() = rootView.display?.displayId ?: Display.DEFAULT_DISPLAY
+
+    /** Returns a list of all TaskContainers in the TaskView. */
+    lateinit var taskContainers: List<TaskContainer>
+        protected set
+    lateinit var orientedState: RecentsOrientedState
+
+    var taskViewId = -1
+    var isEndQuickSwitchCuj = false
+
+    // Various animation progress variables.
+    // progress: 0 = show icon and no insets; 1 = don't show icon and show full insets.
+    protected var fullscreenProgress = 0f
+        set(value) {
+            field = Utilities.boundToRange(value, 0f, 1f)
+            onFullscreenProgressChanged(field)
+        }
+    // gridProgress 0 = carousel; 1 = 2 row grid.
+    protected var gridProgress = 0f
+        set(value) {
+            field = value
+            onGridProgressChanged()
+        }
+    /**
+     * The modalness of this view is how it should be displayed when it is shown on its own in the
+     * modal state of overview. 0 being in context with other tasks, 1 being shown on its own.
+     */
+    protected var modalness = 0f
+        set(value) {
+            field = value
+            onModalnessUpdated(field)
+        }
+    protected var taskThumbnailSplashAlpha = 0f
+        set(value) {
+            field = value
+            applyThumbnailSplashAlpha()
+        }
+    protected var nonGridScale = 1f
+        set(value) {
+            field = value
+            applyScale()
+        }
+    private var dismissScale = 1f
+        set(value) {
+            field = value
+            applyScale()
+        }
+    private var dismissTranslationX = 0f
+        set(value) {
+            field = value
+            applyTranslationX()
+        }
+    private var dismissTranslationY = 0f
+        set(value) {
+            field = value
+            applyTranslationY()
+        }
+    private var taskOffsetTranslationX = 0f
+        set(value) {
+            field = value
+            applyTranslationX()
+        }
+    private var taskOffsetTranslationY = 0f
+        set(value) {
+            field = value
+            applyTranslationY()
+        }
+    private var taskResistanceTranslationX = 0f
+        set(value) {
+            field = value
+            applyTranslationX()
+        }
+    private var taskResistanceTranslationY = 0f
+        set(value) {
+            field = value
+            applyTranslationY()
+        }
+    // The following translation variables should only be used in the same orientation as Launcher.
+    private var boxTranslationY = 0f
+        set(value) {
+            field = value
+            applyTranslationY()
+        }
+    // The following grid translations scales with mGridProgress.
+    protected var gridTranslationX = 0f
+        set(value) {
+            field = value
+            applyTranslationX()
+        }
+    var gridTranslationY = 0f
+        protected set(value) {
+            field = value
+            applyTranslationY()
+        }
+    // The following grid translation is used to animate closing the gap between grid and clear all.
+    private var gridEndTranslationX = 0f
+        set(value) {
+            field = value
+            applyTranslationX()
+        }
+    // Applied as a complement to gridTranslation, for adjusting the carousel overview and quick
+    // switch.
+    protected var nonGridTranslationX = 0f
+        set(value) {
+            field = value
+            applyTranslationX()
+        }
+    protected var nonGridPivotTranslationX = 0f
+        set(value) {
+            field = value
+            applyTranslationX()
+        }
+    // Used when in SplitScreenSelectState
+    private var splitSelectTranslationY = 0f
+        set(value) {
+            field = value
+            applyTranslationY()
+        }
+    private var splitSelectTranslationX = 0f
+        set(value) {
+            field = value
+            applyTranslationX()
+        }
+    protected var stableAlpha = 1f
+        set(value) {
+            field = value
+            alpha = stableAlpha
+        }
+    protected var shouldShowScreenshot = false
+        get() = !isRunningTask || field
+    /** Enable or disable showing border on hover and focus change */
+    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+    var borderEnabled = false
+        set(value) {
+            if (field == value) {
+                return
+            }
+            field = value
+            // Set the animation correctly in case it misses the hover/focus event during state
+            // transition
+            hoverBorderAnimator?.setBorderVisibility(visible = field && isHovered, animated = true)
+            focusBorderAnimator?.setBorderVisibility(visible = field && isFocused, animated = true)
+        }
+    protected var iconScaleAnimStartProgress = 0f
+    private var focusTransitionProgress = 1f
+
+    private var iconAndDimAnimator: ObjectAnimator? = null
+    // The current background requests to load the task thumbnail and icon
+    private val pendingThumbnailLoadRequests = mutableListOf<CancellableTask<*>>()
+    private val pendingIconLoadRequests = mutableListOf<CancellableTask<*>>()
+    private var isClickableAsLiveTile = true
+
+    init {
+        setOnClickListener { _ -> onClick() }
+        val keyboardFocusHighlightEnabled =
+            (ENABLE_KEYBOARD_QUICK_SWITCH.get() || enableFocusOutline())
+        val cursorHoverStatesEnabled = enableCursorHoverStates()
+        setWillNotDraw(!keyboardFocusHighlightEnabled && !cursorHoverStatesEnabled)
+        context.obtainStyledAttributes(attrs, R.styleable.TaskView, defStyleAttr, defStyleRes).use {
+            this.focusBorderAnimator =
+                focusBorderAnimator
+                    ?: if (keyboardFocusHighlightEnabled)
+                        createSimpleBorderAnimator(
+                            currentFullscreenParams.cornerRadius.toInt(),
+                            context.resources.getDimensionPixelSize(
+                                R.dimen.keyboard_quick_switch_border_width
+                            ),
+                            { bounds: Rect -> getThumbnailBounds(bounds) },
+                            this,
+                            it.getColor(
+                                R.styleable.TaskView_focusBorderColor,
+                                BorderAnimator.DEFAULT_BORDER_COLOR
+                            )
+                        )
+                    else null
+            this.hoverBorderAnimator =
+                hoverBorderAnimator
+                    ?: if (cursorHoverStatesEnabled)
+                        createSimpleBorderAnimator(
+                            currentFullscreenParams.cornerRadius.toInt(),
+                            context.resources.getDimensionPixelSize(
+                                R.dimen.task_hover_border_width
+                            ),
+                            { bounds: Rect -> getThumbnailBounds(bounds) },
+                            this,
+                            it.getColor(
+                                R.styleable.TaskView_hoverBorderColor,
+                                BorderAnimator.DEFAULT_BORDER_COLOR
+                            )
+                        )
+                    else null
+        }
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+    public override fun onFocusChanged(
+        gainFocus: Boolean,
+        direction: Int,
+        previouslyFocusedRect: Rect?
+    ) {
+        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect)
+        if (borderEnabled) {
+            focusBorderAnimator?.setBorderVisibility(gainFocus, /* animated= */ true)
+        }
+    }
+
+    override fun onHoverEvent(event: MotionEvent): Boolean {
+        if (borderEnabled) {
+            when (event.action) {
+                MotionEvent.ACTION_HOVER_ENTER ->
+                    hoverBorderAnimator?.setBorderVisibility(visible = true, animated = true)
+                MotionEvent.ACTION_HOVER_EXIT ->
+                    hoverBorderAnimator?.setBorderVisibility(visible = false, animated = true)
+                else -> {}
+            }
+        }
+        return super.onHoverEvent(event)
+    }
+
+    // avoid triggering hover event on child elements which would cause HOVER_EXIT for this
+    // task view
+    override fun onInterceptHoverEvent(event: MotionEvent) =
+        if (enableCursorHoverStates()) true else super.onInterceptHoverEvent(event)
+
+    override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
+        val recentsView = recentsView ?: return false
+        val splitSelectStateController = recentsView.splitSelectController
+        // Disable taps for split selection animation unless we have a task not being selected
+        if (
+            splitSelectStateController.isSplitSelectActive &&
+                taskContainers.none { it.task.key.id != splitSelectStateController.initialTaskId }
+        ) {
+            return false
+        }
+        if (ev.action == MotionEvent.ACTION_DOWN) {
+            with(lastTouchDownPosition) {
+                x = ev.x
+                y = ev.y
+            }
+        }
+        return super.dispatchTouchEvent(ev)
+    }
+
+    override fun draw(canvas: Canvas) {
+        // Draw border first so any child views outside of the thumbnail bounds are drawn above it.
+        focusBorderAnimator?.drawBorder(canvas)
+        hoverBorderAnimator?.drawBorder(canvas)
+        super.draw(canvas)
+    }
+
+    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
+        super.onLayout(changed, left, top, right, bottom)
+        val thumbnailTopMargin = container.deviceProfile.overviewTaskThumbnailTopMarginPx
+        if (container.deviceProfile.isTablet) {
+            pivotX = (if (layoutDirection == LAYOUT_DIRECTION_RTL) 0 else right - left).toFloat()
+            pivotY = thumbnailTopMargin.toFloat()
+        } else {
+            pivotX = (right - left) * 0.5f
+            pivotY = thumbnailTopMargin + (height - thumbnailTopMargin) * 0.5f
+        }
+        systemGestureExclusionRects =
+            SYSTEM_GESTURE_EXCLUSION_RECT.onEach {
+                it.right = width
+                it.bottom = height
+            }
+    }
+
+    override fun onRecycle() {
+        resetPersistentViewTransforms()
+        // Clear any references to the thumbnail (it will be re-read either from the cache or the
+        // system on next bind)
+        // TODO(b/334825222): Implement thumbnail/snapshot for the new [TaskThumbnailView].
+        if (enableRefactorTaskThumbnail()) {
+            notifyIsRunningTaskUpdated()
+        } else {
+            taskContainers.forEach { it.thumbnailViewDeprecated.setThumbnail(it.task, null) }
+        }
+        setOverlayEnabled(false)
+        onTaskListVisibilityChanged(false)
+        borderEnabled = false
+    }
+
+    // TODO: Clip-out the icon region from the thumbnail, since they are overlapping.
+    override fun hasOverlappingRendering() = false
+
+    override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo) {
+        super.onInitializeAccessibilityNodeInfo(info)
+        with(info) {
+            addAction(
+                AccessibilityNodeInfo.AccessibilityAction(
+                    R.string.accessibility_close,
+                    context.getText(R.string.accessibility_close)
+                )
+            )
+            taskContainers.forEach {
+                TraceHelper.allowIpcs("TV.a11yInfo") {
+                    TaskOverlayFactory.getEnabledShortcuts(this@TaskView, it).forEach { shortcut ->
+                        addAction(shortcut.createAccessibilityAction(context))
+                    }
+                }
+            }
+            // TODO(b/341672022): handle multiple digitalWellBeingToast accessibility actions
+            if (taskContainers[0].digitalWellBeingToast?.hasLimit() == true) {
+                addAction(
+                    AccessibilityNodeInfo.AccessibilityAction(
+                        R.string.accessibility_app_usage_settings,
+                        context.getText(R.string.accessibility_app_usage_settings)
+                    )
+                )
+            }
+            recentsView?.let {
+                collectionItemInfo =
+                    AccessibilityNodeInfo.CollectionItemInfo.obtain(
+                        0,
+                        1,
+                        it.taskViewCount - it.indexOfChild(this@TaskView) - 1,
+                        1,
+                        false
+                    )
+            }
+        }
+    }
+
+    override fun performAccessibilityAction(action: Int, arguments: Bundle?): Boolean {
+        if (action == R.string.accessibility_close) {
+            recentsView?.dismissTask(this, true /*animateTaskView*/, true /*removeTask*/)
+            return true
+        }
+        if (action == R.string.accessibility_app_usage_settings) {
+            // TODO(b/341672022): handle multiple digitalWellBeingToast accessibility actions
+            taskContainers[0].digitalWellBeingToast?.openAppUsageSettings(this)
+            return true
+        }
+        taskContainers.forEach {
+            TaskOverlayFactory.getEnabledShortcuts(this, it).forEach { shortcut ->
+                if (shortcut.hasHandlerForAction(action)) {
+                    shortcut.onClick(this)
+                    return true
+                }
+            }
+        }
+        return super.performAccessibilityAction(action, arguments)
+    }
+
+    /** Updates this task view to the given {@param task}. */
+    open fun bind(
+        task: Task,
+        orientedState: RecentsOrientedState,
+        taskOverlayFactory: TaskOverlayFactory
+    ) {
+        cancelPendingLoadTasks()
+        taskContainers =
+            listOf(
+                createTaskContainer(
+                    task,
+                    R.id.snapshot,
+                    R.id.icon,
+                    R.id.show_windows,
+                    STAGE_POSITION_UNDEFINED,
+                    taskOverlayFactory
+                )
+            )
+        setOrientationState(orientedState)
+    }
+
+    protected fun createTaskContainer(
+        task: Task,
+        @IdRes thumbnailViewId: Int,
+        @IdRes iconViewId: Int,
+        @IdRes showWindowViewId: Int,
+        @StagePosition stagePosition: Int,
+        taskOverlayFactory: TaskOverlayFactory
+    ): TaskContainer {
+        val thumbnailViewDeprecated: TaskThumbnailViewDeprecated = findViewById(thumbnailViewId)!!
+        val thumbnailView: TaskThumbnailView?
+        if (enableRefactorTaskThumbnail()) {
+            val indexOfSnapshotView = indexOfChild(thumbnailViewDeprecated)
+            thumbnailView =
+                TaskThumbnailView(context).apply {
+                    layoutParams = thumbnailViewDeprecated.layoutParams
+                    addView(this, indexOfSnapshotView)
+                }
+            thumbnailViewDeprecated.visibility = GONE
+        } else {
+            thumbnailView = null
+        }
+        val iconView = getOrInflateIconView(iconViewId)
+        return TaskContainer(
+                task,
+                thumbnailView,
+                thumbnailViewDeprecated,
+                iconView,
+                TransformingTouchDelegate(iconView.asView()),
+                stagePosition,
+                DigitalWellBeingToast(container, this),
+                findViewById(showWindowViewId)!!,
+                taskOverlayFactory
+            )
+            .apply {
+                if (enableRefactorTaskThumbnail()) {
+                    thumbnailViewDeprecated.setTaskOverlay(overlay)
+                    bindThumbnailView()
+                } else {
+                    thumbnailViewDeprecated.bind(task, overlay)
+                }
+            }
+    }
+
+    protected fun getOrInflateIconView(@IdRes iconViewId: Int): TaskViewIcon {
+        val iconView = findViewById<View>(iconViewId)!!
+        return iconView as? TaskViewIcon
+            ?: (iconView as ViewStub)
+                .apply {
+                    layoutResource =
+                        if (enableOverviewIconMenu()) R.layout.icon_app_chip_view
+                        else R.layout.icon_view
+                }
+                .inflate() as TaskViewIcon
+    }
+
+    protected fun isTaskContainersInitialized() = this::taskContainers.isInitialized
+
+    fun containsMultipleTasks() = taskContainers.size > 1
+
+    /**
+     * Returns the TaskContainer corresponding to a given taskId, or null if the TaskView does not
+     * contain a Task with that ID.
+     */
+    fun getTaskContainerById(taskId: Int) = taskContainers.firstOrNull { it.task.key.id == taskId }
+
+    /** Check if given `taskId` is tracked in this view */
+    fun containsTaskId(taskId: Int) = getTaskContainerById(taskId) != null
+
+    open fun setOrientationState(orientationState: RecentsOrientedState) {
+        this.orientedState = orientationState
+        taskContainers.forEach { it.iconView.setIconOrientation(orientationState, isGridTask) }
+        setThumbnailOrientation(orientationState)
+    }
+
+    protected open fun setThumbnailOrientation(orientationState: RecentsOrientedState) {
+        taskContainers.forEach {
+            it.overlay.updateOrientationState(orientationState)
+            it.digitalWellBeingToast?.initialize(it.task)
+        }
+    }
+
+    /**
+     * Updates TaskView scaling and translation required to support variable width if enabled, while
+     * ensuring TaskView fits into screen in fullscreen.
+     */
+    fun updateTaskSize(
+        lastComputedTaskSize: Rect,
+        lastComputedGridTaskSize: Rect,
+        lastComputedCarouselTaskSize: Rect
+    ) {
+        val thumbnailPadding = container.deviceProfile.overviewTaskThumbnailTopMarginPx
+        val taskWidth = lastComputedTaskSize.width()
+        val taskHeight = lastComputedTaskSize.height()
+        val nonGridScale: Float
+        val boxTranslationY: Float
+        val expectedWidth: Int
+        val expectedHeight: Int
+        if (container.deviceProfile.isTablet) {
+            val boxWidth: Int
+            val boxHeight: Int
+            if (isFocusedTask) {
+                // Task will be focused and should use focused task size. Use focusTaskRatio
+                // that is associated with the original orientation of the focused task.
+                boxWidth = taskWidth
+                boxHeight = taskHeight
+            } else {
+                // Otherwise task is in grid, and should use lastComputedGridTaskSize.
+                boxWidth = lastComputedGridTaskSize.width()
+                boxHeight = lastComputedGridTaskSize.height()
+            }
+
+            // Bound width/height to the box size.
+            expectedWidth = boxWidth
+            expectedHeight = boxHeight + thumbnailPadding
+
+            // Scale to to fit task Rect.
+            nonGridScale =
+                if (enableGridOnlyOverview()) {
+                    lastComputedCarouselTaskSize.width() / taskWidth.toFloat()
+                } else {
+                    taskWidth / boxWidth.toFloat()
+                }
+
+            // Align to top of task Rect.
+            boxTranslationY = (expectedHeight - thumbnailPadding - taskHeight) / 2.0f
+        } else {
+            nonGridScale = 1f
+            boxTranslationY = 0f
+            expectedWidth = if (enableOverviewIconMenu()) taskWidth else LayoutParams.MATCH_PARENT
+            expectedHeight =
+                if (enableOverviewIconMenu()) taskHeight + thumbnailPadding
+                else LayoutParams.MATCH_PARENT
+        }
+        this.nonGridScale = nonGridScale
+        this.boxTranslationY = boxTranslationY
+        updateLayoutParams<ViewGroup.LayoutParams> {
+            width = expectedWidth
+            height = expectedHeight
+        }
+        updateThumbnailSize()
+    }
+
+    protected open fun updateThumbnailSize() {
+        // TODO(b/271468547), we should default to setting translations only on the snapshot instead
+        //  of a hybrid of both margins and translations
+        taskContainers[0].snapshotView.updateLayoutParams<LayoutParams> {
+            topMargin = container.deviceProfile.overviewTaskThumbnailTopMarginPx
+        }
+    }
+
+    /** Returns the thumbnail's bounds, optionally relative to the screen. */
+    @JvmOverloads
+    open fun getThumbnailBounds(bounds: Rect, relativeToDragLayer: Boolean = false) {
+        bounds.setEmpty()
+        taskContainers.forEach {
+            val thumbnailBounds = Rect()
+            if (relativeToDragLayer) {
+                container.dragLayer.getDescendantRectRelativeToSelf(it.snapshotView, bounds)
+            } else {
+                thumbnailBounds.set(it.snapshotView)
+            }
+            bounds.union(thumbnailBounds)
+        }
+    }
+
+    /**
+     * See [TaskDataChanges]
+     *
+     * @param visible If this task view will be visible to the user in overview or hidden
+     */
+    fun onTaskListVisibilityChanged(visible: Boolean) {
+        onTaskListVisibilityChanged(visible, FLAG_UPDATE_ALL)
+    }
+
+    /**
+     * See [TaskDataChanges]
+     *
+     * @param visible If this task view will be visible to the user in overview or hidden
+     */
+    open fun onTaskListVisibilityChanged(visible: Boolean, @TaskDataChanges changes: Int) {
+        cancelPendingLoadTasks()
+        val recentsModel = RecentsModel.INSTANCE.get(context)
+        // These calls are no-ops if the data is already loaded, try and load the high
+        // resolution thumbnail if the state permits
+        if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
+            if (!enableRefactorTaskThumbnail()) {
+                // TODO(b/334825222) add thumbnail state
+                taskContainers.forEach {
+                    if (visible) {
+                        recentsModel.thumbnailCache
+                            .updateThumbnailInBackground(it.task) { thumbnailData ->
+                                it.thumbnailViewDeprecated.setThumbnail(it.task, thumbnailData)
+                            }
+                            ?.also { request -> pendingThumbnailLoadRequests.add(request) }
+                    } else {
+                        it.thumbnailViewDeprecated.setThumbnail(null, null)
+                        // Reset the task thumbnail reference as well (it will be fetched from the
+                        // cache or reloaded next time we need it)
+                        it.task.thumbnail = null
+                    }
+                }
+            }
+        }
+        if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
+            taskContainers.forEach {
+                if (visible) {
+                    recentsModel.iconCache
+                        .updateIconInBackground(it.task) { thumbnailData ->
+                            setIcon(it.iconView, thumbnailData.icon)
+                            if (enableOverviewIconMenu()) {
+                                setText(it.iconView, thumbnailData.title)
+                            }
+                            it.digitalWellBeingToast?.initialize(thumbnailData)
+                        }
+                        ?.also { request -> pendingIconLoadRequests.add(request) }
+                } else {
+                    setIcon(it.iconView, null)
+                    if (enableOverviewIconMenu()) {
+                        setText(it.iconView, null)
+                    }
+                }
+            }
+        }
+        if (needsUpdate(changes, FLAG_UPDATE_CORNER_RADIUS)) {
+            currentFullscreenParams.updateCornerRadius(context)
+        }
+    }
+
+    protected open fun needsUpdate(@TaskDataChanges dataChange: Int, @TaskDataChanges flag: Int) =
+        (dataChange and flag) == flag
+
+    protected open fun cancelPendingLoadTasks() {
+        pendingThumbnailLoadRequests.forEach { it.cancel() }
+        pendingThumbnailLoadRequests.clear()
+        pendingIconLoadRequests.forEach { it.cancel() }
+        pendingIconLoadRequests.clear()
+    }
+
+    protected fun setIcon(iconView: TaskViewIcon, icon: Drawable?) {
+        with(iconView) {
+            if (icon != null) {
+                setDrawable(icon)
+                setOnClickListener {
+                    if (!confirmSecondSplitSelectApp()) {
+                        showTaskMenu(this)
+                    }
+                }
+                setOnLongClickListener {
+                    requestDisallowInterceptTouchEvent(true)
+                    showTaskMenu(this)
+                }
+            } else {
+                setDrawable(null)
+                setOnClickListener(null)
+                setOnLongClickListener(null)
+            }
+        }
+    }
+
+    protected fun setText(iconView: TaskViewIcon, text: CharSequence?) {
+        iconView.setText(text)
+    }
+
+    open fun refreshThumbnails(thumbnailDatas: HashMap<Int, ThumbnailData?>?) {
+        if (enableRefactorTaskThumbnail()) {
+            // TODO(b/334825222) add thumbnail logic
+            return
+        }
+
+        taskContainers.forEach {
+            val thumbnailData = thumbnailDatas?.get(it.task.key.id)
+            if (thumbnailData != null) {
+                it.thumbnailViewDeprecated.setThumbnail(it.task, thumbnailData)
+            } else {
+                it.thumbnailViewDeprecated.refresh()
+            }
+        }
+    }
+
+    private fun onClick() {
+        if (confirmSecondSplitSelectApp()) {
+            Log.d("b/310064698", "${taskIds.contentToString()} - onClick - split select is active")
+            return
+        }
+        val callbackList =
+            launchTasks()?.apply {
+                add {
+                    Log.d("b/310064698", "${taskIds.contentToString()} - onClick - launchCompleted")
+                }
+            }
+        Log.d("b/310064698", "${taskIds.contentToString()} - onClick - callbackList: $callbackList")
+        container.statsLogManager
+            .logger()
+            .withItemInfo(firstItemInfo)
+            .log(LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP)
+    }
+
+    /**
+     * Starts the task associated with this view and animates the startup.
+     *
+     * @return CompletionStage to indicate the animation completion or null if the launch failed.
+     */
+    open fun launchTaskAnimated(): RunnableList? {
+        TestLogging.recordEvent(
+            TestProtocol.SEQUENCE_MAIN,
+            "startActivityFromRecentsAsync",
+            taskIds.contentToString()
+        )
+        val opts =
+            container.getActivityLaunchOptions(this, null).apply {
+                options.launchDisplayId = display?.displayId ?: Display.DEFAULT_DISPLAY
+            }
+        if (
+            ActivityManagerWrapper.getInstance()
+                .startActivityFromRecents(taskContainers[0].task.key, opts.options)
+        ) {
+            Log.d(
+                TAG,
+                "launchTaskAnimated - startActivityFromRecents: ${taskIds.contentToString()}"
+            )
+            ActiveGestureLog.INSTANCE.trackEvent(
+                ActiveGestureErrorDetector.GestureEvent.EXPECTING_TASK_APPEARED
+            )
+            val recentsView = recentsView ?: return null
+            if (recentsView.runningTaskViewId != -1) {
+                recentsView.onTaskLaunchedInLiveTileMode()
+
+                // Return a fresh callback in the live tile case, so that it's not accidentally
+                // triggered by QuickstepTransitionManager.AppLaunchAnimationRunner.
+                return RunnableList().also { recentsView.addSideTaskLaunchCallback(it) }
+            }
+            if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
+                // If the recents transition is running (ie. in live tile mode), then the start
+                // of a new task will merge into the existing transition and it currently will
+                // not be run independently, so we need to rely on the onTaskAppeared() call
+                // for the new task to trigger the side launch callback to flush this runnable
+                // list (which is usually flushed when the app launch animation finishes)
+                recentsView.addSideTaskLaunchCallback(opts.onEndCallback)
+            }
+            return opts.onEndCallback
+        } else {
+            notifyTaskLaunchFailed()
+            return null
+        }
+    }
+
+    /** Starts the task associated with this view without any animation */
+    fun launchTask(callback: (launched: Boolean) -> Unit) {
+        launchTask(callback, isQuickSwitch = false)
+    }
+
+    /** Starts the task associated with this view without any animation */
+    open fun launchTask(callback: (launched: Boolean) -> Unit, isQuickSwitch: Boolean) {
+        TestLogging.recordEvent(
+            TestProtocol.SEQUENCE_MAIN,
+            "startActivityFromRecentsAsync",
+            taskIds.contentToString()
+        )
+        val firstContainer = taskContainers[0]
+        val failureListener = TaskRemovedDuringLaunchListener(context.applicationContext)
+        if (isQuickSwitch) {
+            // We only listen for failures to launch in quickswitch because the during this
+            // gesture launcher is in the background state, vs other launches which are in
+            // the actual overview state
+            failureListener.register(container, firstContainer.task.key.id) {
+                notifyTaskLaunchFailed()
+                recentsView?.let {
+                    // Disable animations for now, as it is an edge case and the app usually
+                    // covers launcher and also any state transition animation also gets
+                    // clobbered by QuickstepTransitionManager.createWallpaperOpenAnimations
+                    // when launcher shows again
+                    it.startHome(false /* animated */)
+                    // LauncherTaskbarUIController depends on the launcher state when
+                    // checking whether to handle resume, but that can come in before
+                    // startHome() changes the state, so force-refresh here to ensure the
+                    // taskbar is updated
+                    it.mSizeStrategy.taskbarController?.refreshResumedState()
+                }
+            }
+        }
+        // Indicate success once the system has indicated that the transition has started
+        val opts =
+            ActivityOptions.makeCustomTaskAnimation(
+                    context,
+                    0,
+                    0,
+                    Executors.MAIN_EXECUTOR.handler,
+                    { callback(true) }
+                ) {
+                    failureListener.onTransitionFinished()
+                }
+                .apply {
+                    launchDisplayId = display?.displayId ?: Display.DEFAULT_DISPLAY
+                    if (isQuickSwitch) {
+                        setFreezeRecentTasksReordering()
+                    }
+                    // TODO(b/334826842) add splash functionality to new TTV
+                    if (!enableRefactorTaskThumbnail()) {
+                        disableStartingWindow =
+                            firstContainer.thumbnailViewDeprecated.shouldShowSplashView()
+                    }
+                }
+        Executors.UI_HELPER_EXECUTOR.execute {
+            if (
+                !ActivityManagerWrapper.getInstance()
+                    .startActivityFromRecents(firstContainer.task.key, opts)
+            ) {
+                // If the call to start activity failed, then post the result immediately,
+                // otherwise, wait for the animation start callback from the activity options
+                // above
+                Executors.MAIN_EXECUTOR.post {
+                    notifyTaskLaunchFailed()
+                    callback(false)
+                }
+            }
+            Log.d(TAG, "launchTask - startActivityFromRecents: ${taskIds.contentToString()}")
+        }
+    }
+
+    /** Launch of the current task (both live and inactive tasks) with an animation. */
+    fun launchTasks(): RunnableList? {
+        val recentsView = recentsView ?: return null
+        val remoteTargetHandles = recentsView.mRemoteTargetHandles
+        if (!isRunningTask || remoteTargetHandles == null) {
+            return launchTaskAnimated()
+        }
+        if (!isClickableAsLiveTile) {
+            Log.e(TAG, "TaskView is not clickable as a live tile; returning to home.")
+            return null
+        }
+        isClickableAsLiveTile = false
+        val targets =
+            if (remoteTargetHandles.size == 1) {
+                remoteTargetHandles[0].transformParams.targetSet
+            } else {
+                val apps =
+                    remoteTargetHandles.flatMap { it.transformParams.targetSet.apps.asIterable() }
+                val wallpapers =
+                    remoteTargetHandles.flatMap {
+                        it.transformParams.targetSet.wallpapers.asIterable()
+                    }
+                RemoteAnimationTargets(
+                    apps.toTypedArray(),
+                    wallpapers.toTypedArray(),
+                    remoteTargetHandles[0].transformParams.targetSet.nonApps,
+                    remoteTargetHandles[0].transformParams.targetSet.targetMode
+                )
+            }
+        if (targets == null) {
+            // If the recents animation is cancelled somehow between the parent if block and
+            // here, try to launch the task as a non live tile task.
+            val runnableList = launchTaskAnimated()
+            if (runnableList == null) {
+                Log.e(
+                    TAG,
+                    "Recents animation cancelled and cannot launch task as non-live tile" +
+                        "; returning to home"
+                )
+            }
+            isClickableAsLiveTile = true
+            return runnableList
+        }
+        val runnableList = RunnableList()
+        with(AnimatorSet()) {
+            TaskViewUtils.composeRecentsLaunchAnimator(
+                this,
+                this@TaskView,
+                targets.apps,
+                targets.wallpapers,
+                targets.nonApps,
+                true /* launcherClosing */,
+                recentsView.stateManager,
+                recentsView,
+                recentsView.depthController
+            )
+            addListener(
+                object : AnimatorListenerAdapter() {
+                    override fun onAnimationEnd(animator: Animator) {
+                        if (taskContainers.any { it.task.key.displayId != rootViewDisplayId }) {
+                            launchTaskAnimated()
+                        }
+                        isClickableAsLiveTile = true
+                        runEndCallback()
+                    }
+
+                    override fun onAnimationCancel(animation: Animator) {
+                        runEndCallback()
+                    }
+
+                    private fun runEndCallback() {
+                        runnableList.executeAllAndDestroy()
+                    }
+                }
+            )
+            start()
+        }
+        Log.d(TAG, "launchTasks - composeRecentsLaunchAnimator: ${taskIds.contentToString()}")
+        recentsView.onTaskLaunchedInLiveTileMode()
+        return runnableList
+    }
+
+    private fun notifyTaskLaunchFailed() {
+        val sb = StringBuilder("Failed to launch task \n")
+        taskContainers.forEach {
+            sb.append("(task=${it.task.key.baseIntent} userId=${it.task.key.userId})\n")
+        }
+        Log.w(TAG, sb.toString())
+        Toast.makeText(context, R.string.activity_not_available, Toast.LENGTH_SHORT).show()
+    }
+
+    fun initiateSplitSelect(splitPositionOption: SplitPositionOption) {
+        recentsView?.initiateSplitSelect(
+            this,
+            splitPositionOption.stagePosition,
+            SplitConfigurationOptions.getLogEventForPosition(splitPositionOption.stagePosition)
+        )
+    }
+
+    /**
+     * Returns `true` if user is already in split select mode and this tap was to choose the second
+     * app. `false` otherwise
+     */
+    protected open fun confirmSecondSplitSelectApp(): Boolean {
+        val index = getLastSelectedChildTaskIndex()
+        if (index >= taskContainers.size) {
+            return false
+        }
+        val container = taskContainers[index]
+        val recentsView = recentsView ?: return false
+        return recentsView.confirmSplitSelect(
+            this,
+            container.task,
+            container.iconView.drawable,
+            container.thumbnailViewDeprecated,
+            container.thumbnailViewDeprecated.thumbnail, /* intent */
+            null, /* user */
+            null,
+            container.itemInfo
+        )
+    }
+
+    /**
+     * Returns the task index of the last selected child task (0 or 1). If we contain multiple tasks
+     * and this TaskView is used as part of split selection, the selected child task index will be
+     * that of the remaining task.
+     */
+    protected open fun getLastSelectedChildTaskIndex() = 0
+
+    private fun showTaskMenu(iconView: TaskViewIcon): Boolean {
+        val recentsView = recentsView ?: return false
+        if (!recentsView.canLaunchFullscreenTask()) {
+            // Don't show menu when selecting second split screen app
+            return true
+        }
+        if (!container.deviceProfile.isTablet && !recentsView.isClearAllHidden) {
+            recentsView.snapToPage(recentsView.indexOfChild(this))
+            return false
+        }
+        val menuContainer = taskContainers.firstOrNull { it.iconView === iconView } ?: return false
+        container.statsLogManager
+            .logger()
+            .withItemInfo(menuContainer.itemInfo)
+            .log(LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS)
+        return showTaskMenuWithContainer(menuContainer)
+    }
+
+    private fun showTaskMenuWithContainer(menuContainer: TaskContainer): Boolean {
+        val recentsView = recentsView ?: return false
+        return if (enableOverviewIconMenu() && menuContainer.iconView is IconAppChipView) {
+            menuContainer.iconView.revealAnim(/* isRevealing= */ true)
+            TaskMenuView.showForTask(menuContainer) {
+                menuContainer.iconView.revealAnim(/* isRevealing= */ false)
+            }
+        } else if (container.deviceProfile.isTablet) {
+            val alignedOptionIndex =
+                if (
+                    recentsView.isOnGridBottomRow(menuContainer.taskView) &&
+                        container.deviceProfile.isLandscape
+                ) {
+                    if (enableGridOnlyOverview()) {
+                        // With no focused task, there is less available space below the tasks, so
+                        // align the arrow to the third option in the menu.
+                        2
+                    } else {
+                        // Bottom row of landscape grid aligns arrow to second option to avoid
+                        // clipping
+                        1
+                    }
+                } else {
+                    0
+                }
+            TaskMenuViewWithArrow.showForTask(menuContainer, alignedOptionIndex)
+        } else {
+            TaskMenuView.showForTask(menuContainer)
+        }
+    }
+
+    /**
+     * Whether the taskview should take the touch event from parent. Events passed to children that
+     * might require special handling.
+     */
+    open fun offerTouchToChildren(event: MotionEvent): Boolean {
+        taskContainers.forEach {
+            if (event.action == MotionEvent.ACTION_DOWN) {
+                computeAndSetIconTouchDelegate(it.iconView, tempCoordinates, it.iconTouchDelegate)
+                if (it.iconTouchDelegate.onTouchEvent(event)) {
+                    return true
+                }
+            }
+        }
+        return false
+    }
+
+    private fun computeAndSetIconTouchDelegate(
+        view: TaskViewIcon,
+        tempCenterCoordinates: FloatArray,
+        transformingTouchDelegate: TransformingTouchDelegate
+    ) {
+        val viewHalfWidth = view.width / 2f
+        val viewHalfHeight = view.height / 2f
+        Utilities.getDescendantCoordRelativeToAncestor(
+            view.asView(),
+            container.dragLayer,
+            tempCenterCoordinates.apply {
+                this[0] = viewHalfWidth
+                this[1] = viewHalfHeight
+            },
+            false
+        )
+        transformingTouchDelegate.setBounds(
+            (tempCenterCoordinates[0] - viewHalfWidth).toInt(),
+            (tempCenterCoordinates[1] - viewHalfHeight).toInt(),
+            (tempCenterCoordinates[0] + viewHalfWidth).toInt(),
+            (tempCenterCoordinates[1] + viewHalfHeight).toInt()
+        )
+    }
+
+    /** Sets up an on-click listener and the visibility for show_windows icon on top of the task. */
+    open fun setUpShowAllInstancesListener() {
+        taskContainers.forEach {
+            it.showWindowsView?.let { showWindowsView ->
+                updateFilterCallback(
+                    showWindowsView,
+                    getFilterUpdateCallback(it.task.key.packageName)
+                )
+            }
+        }
+    }
+
+    /**
+     * Returns a callback that updates the state of the filter and the recents overview
+     *
+     * @param taskPackageName package name of the task to filter by
+     */
+    private fun getFilterUpdateCallback(taskPackageName: String?) =
+        if (recentsView?.filterState?.shouldShowFilterUI(taskPackageName) == true)
+            OnClickListener { recentsView?.setAndApplyFilter(taskPackageName) }
+        else null
+
+    /**
+     * Sets the correct visibility and callback on the provided filterView based on whether the
+     * callback is null or not
+     */
+    private fun updateFilterCallback(filterView: View, callback: OnClickListener?) {
+        // Filtering changes alpha instead of the visibility since visibility
+        // can be altered separately through RecentsView#resetFromSplitSelectionState()
+        with(filterView) {
+            alpha = if (callback == null) 0f else 1f
+            setOnClickListener(callback)
+        }
+    }
+
+    protected open fun setIconsAndBannersFullscreenProgress(progress: Float) {
+        // Animate icons and DWB banners in/out, except in QuickSwitch state, when tiles are
+        // oversized and banner would look disproportionately large.
+        if (recentsView?.stateManager?.state == LauncherState.BACKGROUND_APP) {
+            return
+        }
+        setIconsAndBannersTransitionProgress(progress, invert = true)
+    }
+
+    /**
+     * Called to animate a smooth transition when going directly from an app into Overview (and vice
+     * versa). Icons fade in, and DWB banners slide in with a "shift up" animation.
+     */
+    protected open fun setIconsAndBannersTransitionProgress(progress: Float, invert: Boolean) {
+        focusTransitionProgress = if (invert) 1 - progress else progress
+        getIconContentScale(invert).let { iconContentScale ->
+            taskContainers.forEach {
+                it.iconView.setContentAlpha(iconContentScale)
+                it.digitalWellBeingToast?.updateBannerOffset(1f - iconContentScale)
+            }
+        }
+    }
+
+    private fun getIconContentScale(invert: Boolean): Float {
+        val iconScalePercentage = SCALE_ICON_DURATION.toFloat() / DIM_ANIM_DURATION
+        val lowerClamp = if (invert) 1f - iconScalePercentage else 0f
+        val upperClamp = if (invert) 1f else iconScalePercentage
+        return Interpolators.clampToProgress(Interpolators.FAST_OUT_SLOW_IN, lowerClamp, upperClamp)
+            .getInterpolation(focusTransitionProgress)
+    }
+
+    fun animateIconScaleAndDimIntoView() {
+        iconAndDimAnimator?.cancel()
+        iconAndDimAnimator =
+            ObjectAnimator.ofFloat(this, FOCUS_TRANSITION, 1f).apply {
+                setCurrentFraction(iconScaleAnimStartProgress)
+                setDuration(DIM_ANIM_DURATION).interpolator = Interpolators.LINEAR
+                addListener(
+                    object : AnimatorListenerAdapter() {
+                        override fun onAnimationEnd(animation: Animator) {
+                            iconAndDimAnimator = null
+                        }
+                    }
+                )
+                start()
+            }
+    }
+
+    fun setIconScaleAndDim(iconScale: Float) {
+        iconAndDimAnimator?.cancel()
+        setIconsAndBannersTransitionProgress(iconScale, invert = false)
+    }
+
+    /** Set a color tint on the snapshot and supporting views. */
+    open fun setColorTint(amount: Float, tintColor: Int) {
+        taskContainers.forEach {
+            if (!enableRefactorTaskThumbnail()) {
+                // TODO(b/334832108) Add scrim to new TTV
+                it.thumbnailViewDeprecated.dimAlpha = amount
+            }
+            it.iconView.setIconColorTint(tintColor, amount)
+            it.digitalWellBeingToast?.setBannerColorTint(tintColor, amount)
+        }
+    }
+
+    /**
+     * Sets visibility for the thumbnail and associated elements (DWB banners and action chips).
+     * IconView is unaffected.
+     *
+     * @param taskId is only used when setting visibility to a non-[View.VISIBLE] value
+     */
+    open fun setThumbnailVisibility(visibility: Int, taskId: Int) {
+        taskContainers.forEach {
+            if (visibility == VISIBLE || it.task.key.id == taskId) {
+                it.snapshotView.visibility = visibility
+                it.digitalWellBeingToast?.setBannerVisibility(visibility)
+                it.showWindowsView?.visibility = visibility
+                it.overlay.setVisibility(visibility)
+            }
+        }
+    }
+
+    open fun setOverlayEnabled(overlayEnabled: Boolean) {
+        // TODO(b/335606129) Investigate the usage of [TaskOverlay] in the new TaskThumbnailView.
+        //  and if it's still necessary we should support that in the new TTV class.
+        if (!enableRefactorTaskThumbnail()) {
+            taskContainers.forEach { it.thumbnailViewDeprecated.setOverlayEnabled(overlayEnabled) }
+        }
+    }
+
+    protected open fun refreshTaskThumbnailSplash() {
+        if (!enableRefactorTaskThumbnail()) {
+            // TODO(b/334826842) add splash functionality to new TTV
+            taskContainers.forEach { it.thumbnailViewDeprecated.refreshSplashView() }
+        }
+    }
+
+    protected fun getScrollAdjustment(gridEnabled: Boolean) =
+        if (gridEnabled) gridTranslationX else nonGridTranslationX
+
+    protected fun getOffsetAdjustment(gridEnabled: Boolean) = getScrollAdjustment(gridEnabled)
+
+    fun getSizeAdjustment(fullscreenEnabled: Boolean) = if (fullscreenEnabled) nonGridScale else 1f
+
+    private fun applyScale() {
+        val scale = persistentScale * dismissScale
+        scaleX = scale
+        scaleY = scale
+        if (enableRefactorTaskThumbnail()) {
+            taskViewData.scale.value = scale
+        }
+        updateSnapshotRadius()
+    }
+
+    protected open fun applyThumbnailSplashAlpha() {
+        if (!enableRefactorTaskThumbnail()) {
+            // TODO(b/334826842) add splash functionality to new TTV
+            taskContainers.forEach {
+                it.thumbnailViewDeprecated.setSplashAlpha(taskThumbnailSplashAlpha)
+            }
+        }
+    }
+
+    private fun applyTranslationX() {
+        translationX =
+            dismissTranslationX +
+                taskOffsetTranslationX +
+                taskResistanceTranslationX +
+                splitSelectTranslationX +
+                gridEndTranslationX +
+                persistentTranslationX
+    }
+
+    private fun applyTranslationY() {
+        translationY =
+            dismissTranslationY +
+                taskOffsetTranslationY +
+                taskResistanceTranslationY +
+                splitSelectTranslationY +
+                persistentTranslationY
+    }
+
+    private fun onGridProgressChanged() {
+        applyTranslationX()
+        applyTranslationY()
+        applyScale()
+    }
+
+    protected open fun onFullscreenProgressChanged(fullscreenProgress: Float) {
+        taskContainers.forEach {
+            it.iconView.setVisibility(if (fullscreenProgress < 1) VISIBLE else INVISIBLE)
+            it.overlay.setFullscreenProgress(fullscreenProgress)
+        }
+        setIconsAndBannersFullscreenProgress(fullscreenProgress)
+        updateSnapshotRadius()
+    }
+
+    protected open fun updateSnapshotRadius() {
+        updateCurrentFullscreenParams()
+        taskContainers.forEach {
+            it.thumbnailViewDeprecated.setFullscreenParams(getThumbnailFullscreenParams())
+            it.overlay.setFullscreenParams(getThumbnailFullscreenParams())
+        }
+    }
+
+    protected open fun updateCurrentFullscreenParams() {
+        updateFullscreenParams(currentFullscreenParams)
+    }
+
+    protected fun updateFullscreenParams(fullscreenParams: FullscreenDrawParams) {
+        recentsView?.let { fullscreenParams.setProgress(fullscreenProgress, it.scaleX, scaleX) }
+    }
+
+    protected open fun getThumbnailFullscreenParams(): FullscreenDrawParams =
+        currentFullscreenParams
+
+    private fun onModalnessUpdated(modalness: Float) {
+        taskContainers.forEach {
+            it.iconView.setModalAlpha(1 - modalness)
+            it.digitalWellBeingToast?.updateBannerOffset(modalness)
+        }
+    }
+
+    /** Updates [TaskThumbnailView] to reflect the latest [Task] state (i.e., task isRunning). */
+    fun notifyIsRunningTaskUpdated() {
+        // TODO(b/335649589): TaskView's VM will already have access to TaskThumbnailView's VM
+        //  so there will be no need to access TaskThumbnailView's VM through the TaskThumbnailView
+        taskContainers.forEach { it.bindThumbnailView() }
+    }
+
+    fun resetPersistentViewTransforms() {
+        nonGridTranslationX = 0f
+        gridTranslationX = 0f
+        gridTranslationY = 0f
+        boxTranslationY = 0f
+        nonGridPivotTranslationX = 0f
+        resetViewTransforms()
+    }
+
+    open fun resetViewTransforms() {
+        // fullscreenTranslation and accumulatedTranslation should not be reset, as
+        // resetViewTransforms is called during QuickSwitch scrolling.
+        dismissTranslationX = 0f
+        taskOffsetTranslationX = 0f
+        taskResistanceTranslationX = 0f
+        splitSelectTranslationX = 0f
+        gridEndTranslationX = 0f
+        dismissTranslationY = 0f
+        taskOffsetTranslationY = 0f
+        taskResistanceTranslationY = 0f
+        if (recentsView?.isSplitSelectionActive != true) {
+            splitSelectTranslationY = 0f
+        }
+        dismissScale = 1f
+        translationZ = 0f
+        alpha = stableAlpha
+        setIconScaleAndDim(1f)
+        setColorTint(0f, 0)
+        if (!enableRefactorTaskThumbnail()) {
+            // TODO(b/335399428) add split select functionality to new TTV
+            taskContainers.forEach { it.thumbnailViewDeprecated.resetViewTransforms() }
+        }
+    }
+
+    private fun getGridTrans(endTranslation: Float) =
+        Utilities.mapRange(gridProgress, 0f, endTranslation)
+
+    private fun getNonGridTrans(endTranslation: Float) =
+        endTranslation - getGridTrans(endTranslation)
+
+    /** We update and subsequently draw these in [fullscreenProgress]. */
+    open class FullscreenDrawParams(context: Context) : SafeCloseable {
+        var cornerRadius = 0f
+        private var windowCornerRadius = 0f
+        var currentDrawnCornerRadius = 0f
+
+        init {
+            updateCornerRadius(context)
+        }
+
+        /** Recomputes the start and end corner radius for the given Context. */
+        fun updateCornerRadius(context: Context) {
+            cornerRadius = computeTaskCornerRadius(context)
+            windowCornerRadius = computeWindowCornerRadius(context)
+        }
+
+        @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+        open fun computeTaskCornerRadius(context: Context): Float {
+            return TaskCornerRadius.get(context)
+        }
+
+        @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+        open fun computeWindowCornerRadius(context: Context): Float {
+            return QuickStepContract.getWindowCornerRadius(context)
+        }
+
+        /** Sets the progress in range [0, 1] */
+        fun setProgress(fullscreenProgress: Float, parentScale: Float, taskViewScale: Float) {
+            currentDrawnCornerRadius =
+                Utilities.mapRange(fullscreenProgress, cornerRadius, windowCornerRadius) /
+                    parentScale /
+                    taskViewScale
+        }
+
+        override fun close() {}
+    }
+
+    /** Holder for all Task dependent information. */
+    inner class TaskContainer(
+        val task: Task,
+        val thumbnailView: TaskThumbnailView?,
+        val thumbnailViewDeprecated: TaskThumbnailViewDeprecated,
+        val iconView: TaskViewIcon,
+        /**
+         * This technically can be a vanilla [android.view.TouchDelegate] class, however that class
+         * requires setting the touch bounds at construction, so we'd repeatedly be created many
+         * instances unnecessarily as scrolling occurs, whereas [TransformingTouchDelegate] allows
+         * touch delegated bounds only to be updated.
+         */
+        val iconTouchDelegate: TransformingTouchDelegate,
+        /** Defaults to STAGE_POSITION_UNDEFINED if in not a split screen task view */
+        @StagePosition val stagePosition: Int,
+        val digitalWellBeingToast: DigitalWellBeingToast?,
+        val showWindowsView: View?,
+        taskOverlayFactory: TaskOverlayFactory
+    ) {
+        val overlay: TaskOverlay<*> = taskOverlayFactory.createOverlay(this)
+
+        @IdRes
+        val a11yNodeId: Int =
+            if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) R.id.split_bottomRight_appInfo
+            else R.id.split_topLeft_appInfo
+
+        val snapshotView: View
+            get() = thumbnailView ?: thumbnailViewDeprecated
+
+        /** Builds proto for logging */
+        val itemInfo: WorkspaceItemInfo
+            get() =
+                WorkspaceItemInfo().apply {
+                    itemType = LauncherSettings.Favorites.ITEM_TYPE_TASK
+                    container = LauncherSettings.Favorites.CONTAINER_TASKSWITCHER
+                    val componentKey = TaskUtils.getLaunchComponentKeyForTask(task.key)
+                    user = componentKey.user
+                    intent = Intent().setComponent(componentKey.componentName)
+                    title = task.title
+                    recentsView?.let { screenId = it.indexOfChild(this@TaskView) }
+                    if (privateSpaceRestrictAccessibilityDrag()) {
+                        if (
+                            UserCache.getInstance(context).getUserInfo(componentKey.user).isPrivate
+                        ) {
+                            runtimeStatusFlags =
+                                runtimeStatusFlags or ItemInfoWithIcon.FLAG_NOT_PINNABLE
+                        }
+                    }
+                }
+
+        val taskView: TaskView
+            get() = this@TaskView
+
+        // TODO(b/335649589): TaskView's VM will already have access to TaskThumbnailView's VM
+        //  so there will be no need to access TaskThumbnailView's VM through the TaskThumbnailView
+        fun bindThumbnailView() {
+            thumbnailView?.viewModel?.bind(TaskThumbnail(task, isRunningTask))
+        }
+    }
+
+    companion object {
+        private const val TAG = "TaskView"
+        const val FLAG_UPDATE_ICON = 1
+        const val FLAG_UPDATE_THUMBNAIL = FLAG_UPDATE_ICON shl 1
+        const val FLAG_UPDATE_CORNER_RADIUS = FLAG_UPDATE_THUMBNAIL shl 1
+        const val FLAG_UPDATE_ALL =
+            (FLAG_UPDATE_ICON or FLAG_UPDATE_THUMBNAIL or FLAG_UPDATE_CORNER_RADIUS)
+
+        /** The maximum amount that a task view can be scrimmed, dimmed or tinted. */
+        const val MAX_PAGE_SCRIM_ALPHA = 0.4f
+        const val SCALE_ICON_DURATION: Long = 120
+        private const val DIM_ANIM_DURATION: Long = 700
+        private val SYSTEM_GESTURE_EXCLUSION_RECT = listOf(Rect())
+
+        @JvmField
+        val FOCUS_TRANSITION: FloatProperty<TaskView> =
+            object : FloatProperty<TaskView>("focusTransition") {
+                override fun setValue(taskView: TaskView, v: Float) {
+                    taskView.setIconsAndBannersTransitionProgress(v, false /* invert */)
+                }
+
+                override fun get(taskView: TaskView) = taskView.focusTransitionProgress
+            }
+        private val SPLIT_SELECT_TRANSLATION_X: FloatProperty<TaskView> =
+            object : FloatProperty<TaskView>("splitSelectTranslationX") {
+                override fun setValue(taskView: TaskView, v: Float) {
+                    taskView.splitSelectTranslationX = v
+                }
+
+                override fun get(taskView: TaskView) = taskView.splitSelectTranslationX
+            }
+        private val SPLIT_SELECT_TRANSLATION_Y: FloatProperty<TaskView> =
+            object : FloatProperty<TaskView>("splitSelectTranslationY") {
+                override fun setValue(taskView: TaskView, v: Float) {
+                    taskView.splitSelectTranslationY = v
+                }
+
+                override fun get(taskView: TaskView) = taskView.splitSelectTranslationY
+            }
+        private val DISMISS_TRANSLATION_X: FloatProperty<TaskView> =
+            object : FloatProperty<TaskView>("dismissTranslationX") {
+                override fun setValue(taskView: TaskView, v: Float) {
+                    taskView.dismissTranslationX = v
+                }
+
+                override fun get(taskView: TaskView) = taskView.dismissTranslationX
+            }
+        private val DISMISS_TRANSLATION_Y: FloatProperty<TaskView> =
+            object : FloatProperty<TaskView>("dismissTranslationY") {
+                override fun setValue(taskView: TaskView, v: Float) {
+                    taskView.dismissTranslationY = v
+                }
+
+                override fun get(taskView: TaskView) = taskView.dismissTranslationY
+            }
+        private val TASK_OFFSET_TRANSLATION_X: FloatProperty<TaskView> =
+            object : FloatProperty<TaskView>("taskOffsetTranslationX") {
+                override fun setValue(taskView: TaskView, v: Float) {
+                    taskView.taskOffsetTranslationX = v
+                }
+
+                override fun get(taskView: TaskView) = taskView.taskOffsetTranslationX
+            }
+        private val TASK_OFFSET_TRANSLATION_Y: FloatProperty<TaskView> =
+            object : FloatProperty<TaskView>("taskOffsetTranslationY") {
+                override fun setValue(taskView: TaskView, v: Float) {
+                    taskView.taskOffsetTranslationY = v
+                }
+
+                override fun get(taskView: TaskView) = taskView.taskOffsetTranslationY
+            }
+        private val TASK_RESISTANCE_TRANSLATION_X: FloatProperty<TaskView> =
+            object : FloatProperty<TaskView>("taskResistanceTranslationX") {
+                override fun setValue(taskView: TaskView, v: Float) {
+                    taskView.taskResistanceTranslationX = v
+                }
+
+                override fun get(taskView: TaskView) = taskView.taskResistanceTranslationX
+            }
+        private val TASK_RESISTANCE_TRANSLATION_Y: FloatProperty<TaskView> =
+            object : FloatProperty<TaskView>("taskResistanceTranslationY") {
+                override fun setValue(taskView: TaskView, v: Float) {
+                    taskView.taskResistanceTranslationY = v
+                }
+
+                override fun get(taskView: TaskView) = taskView.taskResistanceTranslationY
+            }
+        @JvmField
+        val GRID_END_TRANSLATION_X: FloatProperty<TaskView> =
+            object : FloatProperty<TaskView>("gridEndTranslationX") {
+                override fun setValue(taskView: TaskView, v: Float) {
+                    taskView.gridEndTranslationX = v
+                }
+
+                override fun get(taskView: TaskView) = taskView.gridEndTranslationX
+            }
+        @JvmField
+        val DISMISS_SCALE: FloatProperty<TaskView> =
+            object : FloatProperty<TaskView>("dismissScale") {
+                override fun setValue(taskView: TaskView, v: Float) {
+                    taskView.dismissScale = v
+                }
+
+                override fun get(taskView: TaskView) = taskView.dismissScale
+            }
+    }
+}
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 7065075..c54755b 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
@@ -44,6 +44,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.any
+import org.mockito.kotlin.atLeastOnce
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
@@ -139,12 +140,12 @@
         assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_TASKBAR)
         assertThat(bubbleBarView.isAnimatingNewBubble).isTrue()
 
-        verify(bubbleStashController).updateTaskbarTouchRegion()
+        verify(bubbleStashController, atLeastOnce()).updateTaskbarTouchRegion()
 
         // verify the hide bubble animation is pending
         assertThat(animatorScheduler.delayedBlock).isNotNull()
 
-        animator.onBubbleClickedWhileAnimating()
+        animator.onBubbleBarTouchedWhileAnimating()
 
         assertThat(animatorScheduler.delayedBlock).isNull()
         assertThat(bubbleBarView.alpha).isEqualTo(1)
@@ -173,7 +174,7 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync {}
         PhysicsAnimatorTestUtils.blockUntilFirstAnimationFrameWhereTrue(handleAnimator) { true }
 
-        assertThat(handleAnimator.isRunning()).isTrue()
+        handleAnimator.assertIsRunning()
         assertThat(bubbleBarView.isAnimatingNewBubble).isTrue()
         // verify the hide bubble animation is pending
         assertThat(animatorScheduler.delayedBlock).isNotNull()
@@ -190,7 +191,7 @@
         // PhysicsAnimatorTestUtils posts the cancellation to the main thread so we need to wait
         // again
         InstrumentationRegistry.getInstrumentation().waitForIdleSync()
-        assertThat(handleAnimator.isRunning()).isFalse()
+        handleAnimator.assertIsNotRunning()
     }
 
     @Test
@@ -219,7 +220,7 @@
 
         // wait for the hide animation to start
         InstrumentationRegistry.getInstrumentation().runOnMainSync {}
-        assertThat(handleAnimator.isRunning()).isTrue()
+        handleAnimator.assertIsRunning()
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync {
             animator.onStashStateChangingWhileAnimating()
@@ -231,7 +232,7 @@
         // PhysicsAnimatorTestUtils posts the cancellation to the main thread so we need to wait
         // again
         InstrumentationRegistry.getInstrumentation().waitForIdleSync()
-        assertThat(handleAnimator.isRunning()).isFalse()
+        handleAnimator.assertIsNotRunning()
     }
 
     @Test
@@ -254,16 +255,130 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync {}
         PhysicsAnimatorTestUtils.blockUntilFirstAnimationFrameWhereTrue(handleAnimator) { true }
 
-        assertThat(handleAnimator.isRunning()).isTrue()
+        handleAnimator.assertIsRunning()
         assertThat(bubbleBarView.isAnimatingNewBubble).isTrue()
         assertThat(animatorScheduler.delayedBlock).isNotNull()
 
         handleAnimator.cancel()
-        assertThat(handleAnimator.isRunning()).isFalse()
+        handleAnimator.assertIsNotRunning()
         assertThat(bubbleBarView.isAnimatingNewBubble).isFalse()
         assertThat(animatorScheduler.delayedBlock).isNull()
     }
 
+    @Test
+    fun animateToInitialState_inApp() {
+        setUpBubbleBar()
+        setUpBubbleStashController()
+        whenever(bubbleStashController.bubbleBarTranslationY)
+            .thenReturn(BAR_TRANSLATION_Y_FOR_TASKBAR)
+
+        val handle = View(context)
+        val handleAnimator = PhysicsAnimator.getInstance(handle)
+        whenever(bubbleStashController.stashedHandlePhysicsAnimator).thenReturn(handleAnimator)
+
+        val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
+
+        val animator =
+            BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            animator.animateToInitialState(bubble, isInApp = true, isExpanding = false)
+        }
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {}
+        PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(DynamicAnimation.TRANSLATION_Y)
+
+        barAnimator.assertIsNotRunning()
+        assertThat(bubbleBarView.isAnimatingNewBubble).isTrue()
+        assertThat(bubbleBarView.alpha).isEqualTo(1)
+        assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_TASKBAR)
+
+        assertThat(animatorScheduler.delayedBlock).isNotNull()
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(animatorScheduler.delayedBlock!!)
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {}
+        PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(DynamicAnimation.TRANSLATION_Y)
+
+        assertThat(bubbleBarView.isAnimatingNewBubble).isFalse()
+        assertThat(bubbleBarView.alpha).isEqualTo(0)
+        assertThat(handle.translationY).isEqualTo(0)
+        assertThat(handle.alpha).isEqualTo(1)
+
+        verify(bubbleStashController).stashBubbleBarImmediate()
+    }
+
+    @Test
+    fun animateToInitialState_inApp_autoExpanding() {
+        setUpBubbleBar()
+        setUpBubbleStashController()
+        whenever(bubbleStashController.bubbleBarTranslationY)
+            .thenReturn(BAR_TRANSLATION_Y_FOR_TASKBAR)
+
+        val handle = View(context)
+        val handleAnimator = PhysicsAnimator.getInstance(handle)
+        whenever(bubbleStashController.stashedHandlePhysicsAnimator).thenReturn(handleAnimator)
+
+        val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
+
+        val animator =
+            BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            animator.animateToInitialState(bubble, isInApp = true, isExpanding = true)
+        }
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {}
+        PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(DynamicAnimation.TRANSLATION_Y)
+
+        barAnimator.assertIsNotRunning()
+        assertThat(bubbleBarView.isAnimatingNewBubble).isTrue()
+        assertThat(bubbleBarView.alpha).isEqualTo(1)
+        assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_TASKBAR)
+
+        assertThat(animatorScheduler.delayedBlock).isNotNull()
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(animatorScheduler.delayedBlock!!)
+
+        assertThat(bubbleBarView.isAnimatingNewBubble).isFalse()
+        assertThat(bubbleBarView.alpha).isEqualTo(1)
+        assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_TASKBAR)
+
+        verify(bubbleStashController).showBubbleBarImmediate()
+    }
+
+    @Test
+    fun animateToInitialState_inHome() {
+        setUpBubbleBar()
+        setUpBubbleStashController()
+        whenever(bubbleStashController.bubbleBarTranslationY)
+            .thenReturn(BAR_TRANSLATION_Y_FOR_HOTSEAT)
+
+        val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
+
+        val animator =
+            BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            animator.animateToInitialState(bubble, isInApp = false, isExpanding = false)
+        }
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {}
+        PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(DynamicAnimation.TRANSLATION_Y)
+
+        barAnimator.assertIsNotRunning()
+        assertThat(bubbleBarView.isAnimatingNewBubble).isTrue()
+        assertThat(bubbleBarView.alpha).isEqualTo(1)
+        assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_HOTSEAT)
+
+        assertThat(animatorScheduler.delayedBlock).isNotNull()
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(animatorScheduler.delayedBlock!!)
+
+        assertThat(bubbleBarView.isAnimatingNewBubble).isFalse()
+        assertThat(bubbleBarView.alpha).isEqualTo(1)
+        assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_HOTSEAT)
+
+        verify(bubbleStashController).showBubbleBarImmediate()
+    }
+
     private fun setUpBubbleBar() {
         bubbleBarView = BubbleBarView(context)
         InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -298,6 +413,18 @@
             .thenReturn(BAR_TRANSLATION_Y_FOR_TASKBAR)
     }
 
+    private fun <T> PhysicsAnimator<T>.assertIsRunning() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            assertThat(isRunning()).isTrue()
+        }
+    }
+
+    private fun <T> PhysicsAnimator<T>.assertIsNotRunning() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            assertThat(isRunning()).isFalse()
+        }
+    }
+
     private class TestBubbleBarViewAnimatorScheduler : BubbleBarViewAnimator.Scheduler {
 
         var delayedBlock: Runnable? = null
@@ -322,3 +449,4 @@
 private const val DIFF_BETWEEN_HANDLE_AND_BAR_CENTERS = -20f
 private const val HANDLE_TRANSLATION = -30f
 private const val BAR_TRANSLATION_Y_FOR_TASKBAR = -50f
+private const val BAR_TRANSLATION_Y_FOR_HOTSEAT = -40f
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelTest.kt
index e71192f..efd7bec 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelTest.kt
@@ -17,38 +17,63 @@
 package com.android.quickstep.task.thumbnail
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.quickstep.recents.viewmodel.RecentsViewData
+import com.android.quickstep.task.viewmodel.TaskViewData
 import com.android.systemui.shared.recents.model.Task
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
 class TaskThumbnailViewModelTest {
-    private val systemUnderTest = TaskThumbnailViewModel()
+    private val recentsViewData = RecentsViewData()
+    private val taskViewData = TaskViewData()
+    private val systemUnderTest = TaskThumbnailViewModel(recentsViewData, taskViewData)
 
     @Test
-    fun initialStateIsUninitialized() {
-        assertThat(systemUnderTest.uiState.value).isEqualTo(TaskThumbnailUiState.Uninitialized)
+    fun initialStateIsUninitialized() = runTest {
+        assertThat(systemUnderTest.uiState.first()).isEqualTo(TaskThumbnailUiState.Uninitialized)
     }
 
     @Test
-    fun bindRunningTask_thenStateIs_LiveTile() {
+    fun bindRunningTask_thenStateIs_LiveTile() = runTest {
         val taskThumbnail = TaskThumbnail(Task(), isRunning = true)
         systemUnderTest.bind(taskThumbnail)
 
-        assertThat(systemUnderTest.uiState.value).isEqualTo(TaskThumbnailUiState.LiveTile)
+        assertThat(systemUnderTest.uiState.first()).isEqualTo(TaskThumbnailUiState.LiveTile)
     }
 
     @Test
-    fun bindRunningTaskThenStoppedTask_thenStateIs_Uninitialized() {
+    fun setRecentsFullscreenProgress_thenProgressIsPassedThrough() = runTest {
+        recentsViewData.fullscreenProgress.value = 0.5f
+
+        assertThat(systemUnderTest.recentsFullscreenProgress.first()).isEqualTo(0.5f)
+
+        recentsViewData.fullscreenProgress.value = 0.6f
+
+        assertThat(systemUnderTest.recentsFullscreenProgress.first()).isEqualTo(0.6f)
+    }
+
+    @Test
+    fun setAncestorScales_thenScaleIsCalculated() = runTest {
+        recentsViewData.scale.value = 0.5f
+        taskViewData.scale.value = 0.6f
+
+        assertThat(systemUnderTest.inheritedScale.first()).isEqualTo(0.3f)
+    }
+
+    @Test
+    fun bindRunningTaskThenStoppedTask_thenStateIs_Uninitialized() = runTest {
         // TODO(b/334825222): Change the expectation here when snapshot state is implemented
         val task = Task()
         val runningTask = TaskThumbnail(task, isRunning = true)
         val stoppedTask = TaskThumbnail(task, isRunning = false)
         systemUnderTest.bind(runningTask)
-        assertThat(systemUnderTest.uiState.value).isEqualTo(TaskThumbnailUiState.LiveTile)
+        assertThat(systemUnderTest.uiState.first()).isEqualTo(TaskThumbnailUiState.LiveTile)
 
         systemUnderTest.bind(stoppedTask)
-        assertThat(systemUnderTest.uiState.value).isEqualTo(TaskThumbnailUiState.Uninitialized)
+        assertThat(systemUnderTest.uiState.first()).isEqualTo(TaskThumbnailUiState.Uninitialized)
     }
 }
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt
index ed88c29..e619e7c 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt
@@ -105,7 +105,7 @@
         verify(navbarButtonsViewController, times(1)).setBackForBouncer(false)
     }
 
-    private fun setFlags(flags: Int) {
+    private fun setFlags(flags: Long) {
         taskbarKeyguardController.updateStateForSysuiFlags(flags)
     }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
index 0f9d96c..36f2ccf 100644
--- a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
@@ -16,12 +16,12 @@
 
 package com.android.quickstep
 
-import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
-import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
-import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import android.content.ComponentName
 import android.content.Intent
 import android.platform.test.flag.junit.SetFlagsRule
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.android.launcher3.AbstractFloatingView
 import com.android.launcher3.AbstractFloatingViewHelper
 import com.android.launcher3.logging.StatsLogManager
@@ -29,22 +29,29 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo
 import com.android.launcher3.uioverrides.QuickstepLauncher
 import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.launcher3.util.TransformingTouchDelegate
+import com.android.quickstep.TaskOverlayFactory.TaskOverlay
 import com.android.quickstep.views.LauncherRecentsView
+import com.android.quickstep.views.TaskThumbnailViewDeprecated
 import com.android.quickstep.views.TaskView
+import com.android.quickstep.views.TaskViewIcon
 import com.android.systemui.shared.recents.model.Task
 import com.android.systemui.shared.recents.model.Task.TaskKey
 import com.android.window.flags.Flags
+import com.android.wm.shell.shared.DesktopModeStatus
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
-import org.mockito.quality.Strictness
 import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
+import org.mockito.quality.Strictness
 
 /** Test for DesktopSystemShortcut */
 class DesktopSystemShortcutTest {
@@ -58,21 +65,30 @@
     private val taskView: TaskView = mock()
     private val workspaceItemInfo: WorkspaceItemInfo = mock()
     private val abstractFloatingViewHelper: AbstractFloatingViewHelper = mock()
+    private val thumbnailViewDeprecated: TaskThumbnailViewDeprecated = mock()
+    private val iconView: TaskViewIcon = mock()
+    private val transformingTouchDelegate: TransformingTouchDelegate = mock()
     private val factory: TaskShortcutFactory =
         DesktopSystemShortcut.createFactory(abstractFloatingViewHelper)
+    private val overlayFactory: TaskOverlayFactory = mock()
+    private val overlay: TaskOverlay<*> = mock()
 
     private lateinit var mockitoSession: StaticMockitoSession
 
     @Before
-    fun setUp(){
-        mockitoSession = mockitoSession().strictness(Strictness.LENIENT)
-                .spyStatic(DesktopModeStatus::class.java).startMocking()
-        doReturn(true).`when` { DesktopModeStatus.enforceDeviceRestrictions() }
-        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+    fun setUp() {
+        mockitoSession =
+            mockitoSession()
+                .strictness(Strictness.LENIENT)
+                .spyStatic(DesktopModeStatus::class.java)
+                .startMocking()
+        ExtendedMockito.doReturn(true).`when` { DesktopModeStatus.enforceDeviceRestrictions() }
+        ExtendedMockito.doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        whenever(overlayFactory.createOverlay(any())).thenReturn(overlay)
     }
 
     @After
-    fun tearDown(){
+    fun tearDown() {
         mockitoSession.finishMocking()
     }
 
@@ -84,13 +100,7 @@
             Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
                 isDockable = true
             }
-        val taskContainer =
-            taskView.TaskIdAttributeContainer(
-                task,
-                null,
-                null,
-                SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
-            )
+        val taskContainer = createTaskContainer(task)
 
         val shortcuts = factory.getShortcuts(launcher, taskContainer)
         assertThat(shortcuts).isNull()
@@ -99,19 +109,9 @@
     @Test
     fun createDesktopTaskShortcutFactory_desktopModeEnabled_DeviceNotSupported() {
         setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-        doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        ExtendedMockito.doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
 
-        val task =
-            Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
-                isDockable = true
-            }
-        val taskContainer =
-            taskView.TaskIdAttributeContainer(
-                task,
-                null,
-                null,
-                SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
-            )
+        val taskContainer = createTaskContainer(createTask())
 
         val shortcuts = factory.getShortcuts(launcher, taskContainer)
         assertThat(shortcuts).isNull()
@@ -120,20 +120,11 @@
     @Test
     fun createDesktopTaskShortcutFactory_desktopModeEnabled_DeviceNotSupported_OverrideEnabled() {
         setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-        doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
-        doReturn(false).`when` { DesktopModeStatus.enforceDeviceRestrictions() }
+        ExtendedMockito.doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        ExtendedMockito.doReturn(false).`when` { DesktopModeStatus.enforceDeviceRestrictions() }
 
-        val task =
-            Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
-                isDockable = true
-            }
-        val taskContainer =
-            taskView.TaskIdAttributeContainer(
-                task,
-                null,
-                null,
-                SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
-            )
+        val taskContainer = spy(createTaskContainer(createTask()))
+        doReturn(workspaceItemInfo).whenever(taskContainer).itemInfo
 
         val shortcuts = factory.getShortcuts(launcher, taskContainer)
         assertThat(shortcuts).isNotNull()
@@ -143,17 +134,8 @@
     fun createDesktopTaskShortcutFactory_undockable() {
         setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
 
-        val task =
-            Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
-                isDockable = false
-            }
-        val taskContainer =
-            taskView.TaskIdAttributeContainer(
-                task,
-                null,
-                null,
-                SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
-            )
+        val unDockableTask = createTask().apply { isDockable = false }
+        val taskContainer = createTaskContainer(unDockableTask)
 
         val shortcuts = factory.getShortcuts(launcher, taskContainer)
         assertThat(shortcuts).isNull()
@@ -163,27 +145,18 @@
     fun desktopSystemShortcutClicked() {
         setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
 
-        val task =
-            Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
-                isDockable = true
-            }
-        val taskContainer =
-            taskView.TaskIdAttributeContainer(
-                task,
-                null,
-                null,
-                SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
-            )
+        val task = createTask()
+        val taskContainer = spy(createTaskContainer(task))
 
         whenever(launcher.getOverviewPanel<LauncherRecentsView>()).thenReturn(recentsView)
         whenever(launcher.statsLogManager).thenReturn(statsLogManager)
         whenever(statsLogManager.logger()).thenReturn(statsLogger)
         whenever(statsLogger.withItemInfo(any())).thenReturn(statsLogger)
-        whenever(taskView.getItemInfo(task)).thenReturn(workspaceItemInfo)
         whenever(recentsView.moveTaskToDesktop(any(), any())).thenAnswer {
             val successCallback = it.getArgument<Runnable>(1)
             successCallback.run()
         }
+        doReturn(workspaceItemInfo).whenever(taskContainer).itemInfo
 
         val shortcuts = factory.getShortcuts(launcher, taskContainer)
         assertThat(shortcuts).hasSize(1)
@@ -200,4 +173,24 @@
         verify(statsLogger).withItemInfo(workspaceItemInfo)
         verify(statsLogger).log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_DESKTOP_TAP)
     }
+
+    private fun createTask(): Task {
+        return Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
+            isDockable = true
+        }
+    }
+
+    private fun createTaskContainer(task: Task): TaskView.TaskContainer {
+        return taskView.TaskContainer(
+            task,
+            thumbnailView = null,
+            thumbnailViewDeprecated,
+            iconView,
+            transformingTouchDelegate,
+            SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
+            digitalWellBeingToast = null,
+            showWindowsView = null,
+            overlayFactory
+        )
+    }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt
index db06b6b..5d62a4c 100644
--- a/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt
@@ -53,7 +53,7 @@
         )
 
         val expectedRadius = TaskCornerRadius.get(context)
-        assertThat(params.mCurrentDrawnCornerRadius).isEqualTo(expectedRadius)
+        assertThat(params.currentDrawnCornerRadius).isEqualTo(expectedRadius)
     }
 
     @Test
@@ -67,7 +67,7 @@
         )
 
         val expectedRadius = QuickStepContract.getWindowCornerRadius(context)
-        assertThat(params.mCurrentDrawnCornerRadius).isEqualTo(expectedRadius)
+        assertThat(params.currentDrawnCornerRadius).isEqualTo(expectedRadius)
     }
 
     @Test
@@ -81,7 +81,7 @@
         )
 
         val expectedRadius = TaskCornerRadius.get(context)
-        assertThat(params.mCurrentDrawnCornerRadius).isEqualTo(expectedRadius)
+        assertThat(params.currentDrawnCornerRadius).isEqualTo(expectedRadius)
     }
 
     @Test
@@ -95,7 +95,7 @@
         )
 
         val expectedRadius = QuickStepContract.getWindowCornerRadius(context)
-        assertThat(params.mCurrentDrawnCornerRadius).isEqualTo(expectedRadius)
+        assertThat(params.currentDrawnCornerRadius).isEqualTo(expectedRadius)
     }
 
     @Test
@@ -117,7 +117,7 @@
             /* parentScale= */ 1.0f,
             /* taskViewScale= */ 1.0f
         )
-        assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display1TaskRadius)
+        assertThat(spyParams.currentDrawnCornerRadius).isEqualTo(display1TaskRadius)
 
         spyParams.updateCornerRadius(display2Context)
         spyParams.setProgress(
@@ -125,7 +125,7 @@
             /* parentScale= */ 1.0f,
             /* taskViewScale= */ 1.0f
         )
-        assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display2TaskRadius)
+        assertThat(spyParams.currentDrawnCornerRadius).isEqualTo(display2TaskRadius)
     }
 
     @Test
@@ -147,7 +147,7 @@
             /* parentScale= */ 1.0f,
             /* taskViewScale= */ 1.0f
         )
-        assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display1WindowRadius)
+        assertThat(spyParams.currentDrawnCornerRadius).isEqualTo(display1WindowRadius)
 
         spyParams.updateCornerRadius(display2Context)
         spyParams.setProgress(
@@ -155,6 +155,6 @@
             /* parentScale= */ 1.0f,
             /* taskViewScale= */ 1.0f,
         )
-        assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display2WindowRadius)
+        assertThat(spyParams.currentDrawnCornerRadius).isEqualTo(display2WindowRadius)
     }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java
index 4aa7cb0..07d8f61 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java
@@ -86,9 +86,10 @@
         final TaskView task = getOnceNotNull("No latest task", launcher -> getLatestTask(launcher));
 
         return getFromLauncher(launcher -> {
-            assertTrue("Latest task is not Calculator",
-                    CALCULATOR_PACKAGE.equals(task.getTask().getTopComponent().getPackageName()));
-            return task.getDigitalWellBeingToast();
+            TaskView.TaskContainer taskContainer = task.getTaskContainers().get(0);
+            assertTrue("Latest task is not Calculator", CALCULATOR_PACKAGE.equals(
+                    taskContainer.getTask().getTopComponent().getPackageName()));
+            return taskContainer.getDigitalWellBeingToast();
         });
     }
 
diff --git a/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java b/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
index c64ac23..69a7664 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
@@ -35,6 +35,7 @@
 import com.android.launcher3.util.rule.ScreenRecordRule;
 
 import org.junit.After;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -98,7 +99,6 @@
     }
 
     @Test
-    @ScreenRecordRule.ScreenRecord // b/334946529
     public void testPrivateSpaceContainerIsPresent() {
         // Scroll to the bottom of All Apps
         executeOnLauncher(launcher -> launcher.getAppsView().resetAndScrollToPrivateSpaceHeader());
@@ -167,6 +167,7 @@
 
     @Test
     @ScreenRecordRule.ScreenRecord // b/334946529
+    @Ignore("b/339179262")
     public void testPrivateSpaceLockingBehaviour() throws IOException {
         // Scroll to the bottom of All Apps
         executeOnLauncher(launcher -> launcher.getAppsView().resetAndScrollToPrivateSpaceHeader());
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 81a2d54..7877e8a 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -181,12 +181,6 @@
     public void testOverviewActions() throws Exception {
         assumeFalse("Skipping Overview Actions tests for grid only overview",
                 mLauncher.isTablet() && mLauncher.isGridOnlyOverviewEnabled());
-        // Experimenting for b/165029151:
-        final Overview overview = mLauncher.goHome().switchToOverview();
-        if (overview.hasTasks()) overview.dismissAllTasks();
-        mLauncher.goHome();
-        //
-
         startTestAppsWithCheck();
         OverviewActions actionsView =
                 mLauncher.goHome().switchToOverview().getOverviewActions();
@@ -434,9 +428,7 @@
     @Test
     @PortraitLandscape
     @TaskbarModeSwitch()
-    @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/309820115
     @Ignore("b/315376057")
-    @ScreenRecord // b/309820115
     public void testOverviewForTablet() throws Exception {
         assumeTrue(mLauncher.isTablet());
 
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
index 2a54057..bfd7bdb 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
@@ -130,7 +130,7 @@
                             .tapMenu()
                             .hasMenuItem("Save app pair"));
         } else {
-            overview.getOverviewActions().assertHasAction("Save app pair");
+            overview.getOverviewGroupActions().assertHasAction("Save app pair");
         }
     }
 
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
index de98703..250dc7b 100644
--- a/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
@@ -35,7 +35,7 @@
 import com.android.quickstep.views.IconView
 import com.android.quickstep.views.TaskThumbnailViewDeprecated
 import com.android.quickstep.views.TaskView
-import com.android.quickstep.views.TaskView.TaskIdAttributeContainer
+import com.android.quickstep.views.TaskView.TaskContainer
 import com.android.systemui.shared.recents.model.Task
 import org.junit.Assert.assertEquals
 import org.junit.Before
@@ -67,7 +67,7 @@
     private val mockGroupedTaskView: GroupedTaskView = mock()
     private val mockTask: Task = mock()
     private val mockTaskKey: Task.TaskKey = mock()
-    private val mockTaskIdAttributeContainer: TaskIdAttributeContainer = mock()
+    private val mockTaskContainer: TaskContainer = mock()
     // AppPairIcon
     private val mockAppPairIcon: AppPairIcon = mock()
     private val mockContextThemeWrapper: ContextThemeWrapper = mock()
@@ -83,14 +83,15 @@
     private val transitionInfo: TransitionInfo = mock()
     private val transaction: Transaction = mock()
 
-    lateinit var splitAnimationController: SplitAnimationController
+    private lateinit var splitAnimationController: SplitAnimationController
 
     @Before
     fun setup() {
-        whenever(mockTaskView.thumbnail).thenReturn(mockThumbnailView)
+        whenever(mockTaskContainer.thumbnailViewDeprecated).thenReturn(mockThumbnailView)
         whenever(mockThumbnailView.thumbnail).thenReturn(mockBitmap)
-        whenever(mockTaskView.iconView).thenReturn(mockIconView)
+        whenever(mockTaskContainer.iconView).thenReturn(mockIconView)
         whenever(mockIconView.drawable).thenReturn(mockTaskViewDrawable)
+        whenever(mockTaskView.taskContainers).thenReturn(List(1) { mockTaskContainer })
 
         whenever(splitSelectSource.drawable).thenReturn(mockSplitSourceDrawable)
         whenever(splitSelectSource.view).thenReturn(mockSplitSourceView)
@@ -177,14 +178,13 @@
         // Remove icon view from GroupedTaskView
         whenever(mockIconView.drawable).thenReturn(null)
 
-        whenever(mockTaskIdAttributeContainer.task).thenReturn(mockTask)
-        whenever(mockTaskIdAttributeContainer.iconView).thenReturn(mockIconView)
-        whenever(mockTaskIdAttributeContainer.thumbnailView).thenReturn(mockThumbnailView)
+        whenever(mockTaskContainer.task).thenReturn(mockTask)
+        whenever(mockTaskContainer.iconView).thenReturn(mockIconView)
+        whenever(mockTaskContainer.thumbnailViewDeprecated).thenReturn(mockThumbnailView)
         whenever(mockTask.getKey()).thenReturn(mockTaskKey)
         whenever(mockTaskKey.getId()).thenReturn(taskId)
         whenever(mockSplitSelectStateController.initialTaskId).thenReturn(taskId)
-        whenever(mockGroupedTaskView.taskIdAttributeContainers)
-            .thenReturn(Array(1) { mockTaskIdAttributeContainer })
+        whenever(mockGroupedTaskView.taskContainers).thenReturn(List(1) { mockTaskContainer })
         val splitAnimInitProps: SplitAnimationController.Companion.SplitAnimInitProps =
             splitAnimationController.getFirstAnimInitViews(
                 { mockGroupedTaskView },
@@ -277,9 +277,7 @@
         doNothing()
             .whenever(spySplitAnimationController)
             .composeIconSplitLaunchAnimator(any(), any(), any(), any())
-        doReturn(-1)
-                .whenever(spySplitAnimationController)
-                .hasChangesForBothAppPairs(any(), any())
+        doReturn(-1).whenever(spySplitAnimationController).hasChangesForBothAppPairs(any(), any())
 
         spySplitAnimationController.playSplitLaunchAnimation(
             null /* launchingTaskView */,
@@ -305,41 +303,10 @@
         val spySplitAnimationController = spy(splitAnimationController)
         whenever(mockAppPairIcon.context).thenReturn(mockContextThemeWrapper)
         doNothing()
-                .whenever(spySplitAnimationController)
-                .composeFullscreenIconSplitLaunchAnimator(any(), any(), any(), any(), any())
-        doReturn(0)
-                .whenever(spySplitAnimationController)
-                .hasChangesForBothAppPairs(any(), any())
-
-        spySplitAnimationController.playSplitLaunchAnimation(
-                null /* launchingTaskView */,
-                mockAppPairIcon,
-                taskId,
-                taskId2,
-                null /* apps */,
-                null /* wallpapers */,
-                null /* nonApps */,
-                stateManager,
-                depthController,
-                transitionInfo,
-                transaction,
-                {} /* finishCallback */
-        )
-
-        verify(spySplitAnimationController)
-                .composeFullscreenIconSplitLaunchAnimator(any(), any(), any(), any(), eq(0))
-    }
-
-    @Test
-    fun playsAppropriateSplitLaunchAnimation_playsIconLaunchFromTaskbarCMultiWindow() {
-        val spySplitAnimationController = spy(splitAnimationController)
-        whenever(mockAppPairIcon.context).thenReturn(mockTaskbarActivityContext)
-        doNothing()
             .whenever(spySplitAnimationController)
-            .composeScaleUpLaunchAnimation(any(), any(), any(), any())
-        doReturn(-1)
-                .whenever(spySplitAnimationController)
-                .hasChangesForBothAppPairs(any(), any())
+            .composeFullscreenIconSplitLaunchAnimator(any(), any(), any(), any(), any())
+        doReturn(0).whenever(spySplitAnimationController).hasChangesForBothAppPairs(any(), any())
+
         spySplitAnimationController.playSplitLaunchAnimation(
             null /* launchingTaskView */,
             mockAppPairIcon,
@@ -355,8 +322,35 @@
             {} /* finishCallback */
         )
 
-        verify(spySplitAnimationController).composeScaleUpLaunchAnimation(any(), any(), any(),
-                eq(WINDOWING_MODE_MULTI_WINDOW))
+        verify(spySplitAnimationController)
+            .composeFullscreenIconSplitLaunchAnimator(any(), any(), any(), any(), eq(0))
+    }
+
+    @Test
+    fun playsAppropriateSplitLaunchAnimation_playsIconLaunchFromTaskbarCMultiWindow() {
+        val spySplitAnimationController = spy(splitAnimationController)
+        whenever(mockAppPairIcon.context).thenReturn(mockTaskbarActivityContext)
+        doNothing()
+            .whenever(spySplitAnimationController)
+            .composeScaleUpLaunchAnimation(any(), any(), any(), any())
+        doReturn(-1).whenever(spySplitAnimationController).hasChangesForBothAppPairs(any(), any())
+        spySplitAnimationController.playSplitLaunchAnimation(
+            null /* launchingTaskView */,
+            mockAppPairIcon,
+            taskId,
+            taskId2,
+            null /* apps */,
+            null /* wallpapers */,
+            null /* nonApps */,
+            stateManager,
+            depthController,
+            transitionInfo,
+            transaction,
+            {} /* finishCallback */
+        )
+
+        verify(spySplitAnimationController)
+            .composeScaleUpLaunchAnimation(any(), any(), any(), eq(WINDOWING_MODE_MULTI_WINDOW))
     }
 
     @Test
@@ -364,28 +358,26 @@
         val spySplitAnimationController = spy(splitAnimationController)
         whenever(mockAppPairIcon.context).thenReturn(mockTaskbarActivityContext)
         doNothing()
-                .whenever(spySplitAnimationController)
-                .composeScaleUpLaunchAnimation(any(), any(), any(), any())
-        doReturn(0)
-                .whenever(spySplitAnimationController)
-                .hasChangesForBothAppPairs(any(), any())
+            .whenever(spySplitAnimationController)
+            .composeScaleUpLaunchAnimation(any(), any(), any(), any())
+        doReturn(0).whenever(spySplitAnimationController).hasChangesForBothAppPairs(any(), any())
         spySplitAnimationController.playSplitLaunchAnimation(
-                null /* launchingTaskView */,
-                mockAppPairIcon,
-                taskId,
-                taskId2,
-                null /* apps */,
-                null /* wallpapers */,
-                null /* nonApps */,
-                stateManager,
-                depthController,
-                transitionInfo,
-                transaction,
-                {} /* finishCallback */
+            null /* launchingTaskView */,
+            mockAppPairIcon,
+            taskId,
+            taskId2,
+            null /* apps */,
+            null /* wallpapers */,
+            null /* nonApps */,
+            stateManager,
+            depthController,
+            transitionInfo,
+            transaction,
+            {} /* finishCallback */
         )
 
-        verify(spySplitAnimationController).composeScaleUpLaunchAnimation(any(), any(), any(),
-                eq(WINDOWING_MODE_FULLSCREEN))
+        verify(spySplitAnimationController)
+            .composeScaleUpLaunchAnimation(any(), any(), any(), eq(WINDOWING_MODE_FULLSCREEN))
     }
 
     @Test
diff --git a/res/drawable/bg_ps_mask_left_corner.xml b/res/drawable/bg_ps_mask_left_corner.xml
new file mode 100644
index 0000000..43eeedb
--- /dev/null
+++ b/res/drawable/bg_ps_mask_left_corner.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <vector
+            android:viewportWidth="28"
+            android:viewportHeight="28"
+            android:width="@dimen/ps_floating_mask_corner_radius"
+            android:height="@dimen/ps_floating_mask_corner_radius">
+            <path
+                android:pathData="M0 28H28C24.3228 28 20.6821 27.2759 17.2847 25.8687C13.8877 24.4614 10.8013 22.3989 8.20117 19.7988C5.60107 17.1987 3.53857 14.1123 2.13135 10.7153C0.724121 7.31787 0 3.67725 0 0V28Z"
+                android:fillType="evenOdd"
+                android:fillColor="?attr/allAppsScrimColor" />
+        </vector>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/bg_ps_mask_right_corner.xml b/res/drawable/bg_ps_mask_right_corner.xml
new file mode 100644
index 0000000..d63b866
--- /dev/null
+++ b/res/drawable/bg_ps_mask_right_corner.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ 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.
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <vector
+            android:viewportWidth="28"
+            android:viewportHeight="28"
+            android:width="@dimen/ps_floating_mask_corner_radius"
+            android:height="@dimen/ps_floating_mask_corner_radius">
+            <path
+                android:pathData="M28 28V0C28 3.67725 27.2759 7.31787 25.8687 10.7153C24.4614 14.1123 22.3989 17.1987 19.7988 19.7988C17.1987 22.3989 14.1123 24.4614 10.7153 25.8687C7.31787 27.2759 3.67725 28 0 28H28Z"
+                android:fillType="evenOdd"
+                android:fillColor="?attr/allAppsScrimColor" />
+        </vector>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/layout/private_space_mask_view.xml b/res/layout/private_space_mask_view.xml
new file mode 100644
index 0000000..44e2797
--- /dev/null
+++ b/res/layout/private_space_mask_view.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ 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.
+  -->
+
+<com.android.launcher3.allapps.FloatingMaskView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_alignParentBottom="true"
+    android:layout_marginLeft="@dimen/ps_floating_mask_end_padding"
+    android:layout_marginRight="@dimen/ps_floating_mask_end_padding"
+    android:importantForAccessibility="noHideDescendants"
+    android:orientation="horizontal">
+
+    <ImageView
+        android:id="@+id/left_corner"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:layout_constraintStart_toStartOf="parent"
+        android:importantForAccessibility="no"
+        android:background="@drawable/bg_ps_mask_left_corner"/>
+
+    <ImageView
+        android:id="@+id/right_corner"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:scaleType="centerCrop"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:importantForAccessibility="no"
+        android:background="@drawable/bg_ps_mask_right_corner"/>
+
+    <ImageView
+        android:id="@+id/bottom_box"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:layout_constraintStart_toStartOf="@id/left_corner"
+        app:layout_constraintEnd_toEndOf="@id/right_corner"
+        app:layout_constraintTop_toBottomOf="@id/left_corner"
+        android:importantForAccessibility="no"
+        android:background="?attr/allAppsScrimColor"/>
+
+</com.android.launcher3.allapps.FloatingMaskView>
\ No newline at end of file
diff --git a/res/layout/work_apps_edu.xml b/res/layout/work_apps_edu.xml
index f557fb6..99db8c6 100644
--- a/res/layout/work_apps_edu.xml
+++ b/res/layout/work_apps_edu.xml
@@ -50,9 +50,9 @@
                 android:id="@+id/action_btn"
                 android:layout_width="@dimen/x_icon_size"
                 android:layout_height="@dimen/x_icon_size"
+                android:scaleType="centerInside"
                 android:layout_gravity="center"
                 android:contentDescription="@string/accessibility_close"
-                android:padding="@dimen/x_icon_padding"
                 android:background="@android:color/transparent"
                 android:src="@drawable/ic_remove_no_shadow" />
         </FrameLayout>
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index c363dc5..51a736c 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Voorstelle"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Noodsaaklikhede"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Nuus en tydskrifte"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Jou ontspansone"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Vermaak"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sosiaal"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Gesondheid en fiksheid"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Weer"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Voorgestel vir jou"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>-legstukke aan die regterkant, soektog en opsies aan die linkerkant"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# legstuk}other{# legstukke}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privaat, gesluit."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Sluit"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Privaat Ruimte-oorgang"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Installeer apps"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Installeer apps in privaat ruimte"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Oorvloei"</string>
 </resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index bc211ef..1de35f4 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"የአስተያየት ጥቆማዎች"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"ጠቃሚ ነገሮች"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"ዜና እና መጽሔቶች"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"የሚያርፉበት ቦታዎ"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"መዝናኛ"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"ማህበራዊ"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"ጤና እና የአካል ብቃት"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"የአየር ሁኔታ"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"ለእርስዎ የተጠቆሙ"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ምግብሮች በቀኝ በኩል፣ ፍለጋ እና አማራጮች በግራ በኩል"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ምግብር}one{# ምግብሮች}other{# ምግብሮች}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"የግል፣ የተቆለፈ።"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"ቆልፍ"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"የግል ቦታ ሽግግር"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"መተግበሪያዎችን ይጫኑ"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"መተግበሪያዎችን ወደ የግል ቦታ ይጫኑ"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ትርፍ ፍሰት"</string>
 </resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index becfa8d..4d6528a 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"اقتراحات"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"الأساسيات"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"الأخبار والمجلات"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"محتوى ترفيهي مقترَح"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"الترفيه"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"التواصل الاجتماعي"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"الصحة واللياقة البدنية"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"الطقس"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"اقتراحاتنا لك"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"تطبيقات \"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>\" المصغّرة على اليسار، والبحث والخيارات على اليمين"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{تطبيق مصغّر واحد}zero{# تطبيق مصغّر}two{تطبيقان مصغّران}few{# تطبيقات مصغّرة}many{# تطبيقًا مصغّرًا}other{# تطبيق مصغّر}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"المساحة الخاصة مُقفلة."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"قفل"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"النقل إلى المساحة الخاصة"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"تثبيت التطبيقات"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"تثبيت التطبيقات في المساحة الخاصّة"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"القائمة الكاملة"</string>
 </resources>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index d2487f8..9798dd2 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"পৰামৰ্শ"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"অত্যাৱশ্যকীয়সমূহ"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"বাতৰি আৰু আলোচনী"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"আপোনাৰ পচন্দৰ স্থান"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"মনোৰঞ্জন"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"সামাজিক"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"স্বাস্থ্য আৰু সুস্থতা"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"বতৰ"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"আপোনাৰ বাবে পৰামৰ্শ হিচাপে আগবঢ়োৱা"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ৱিজেট সোঁফালে, সন্ধান আৰু বিকল্পসমূহ বাওঁফালে"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# টা ৱিজেট}one{# টা ৱিজেট}other{# টা ৱিজেট}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"ব্যক্তিগত, লক কৰা আছে।"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"লক কৰক"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"ব্যক্তিগত স্পে’চৰ স্থানান্তৰণ"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"এপ্‌ ইনষ্টল কৰক"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"এপ্‌সমূহ প্ৰাইভেট স্পেচত ইনষ্টল কৰক"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"অ’ভাৰফ্ল’"</string>
 </resources>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 145ec69..a8f0a3f 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Təkliflər"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Əsaslar"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Xəbər və jurnallar"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"İstirahət zonası"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Əyləncə"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sosial"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Sağlamlıq və fitnes"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Hava"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Təklif edirik"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> vidcetləri sağda, axtarış və seçimlər solda"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# vidcet}other{# vidcet}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Şəxsi, kilidli."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Kilidləyin"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Şəxsi məkana keçid"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Tətbiqlər quraşdırın"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Tətbiqləri şəxsi sahədə quraşdırın"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Kənara çıxma"</string>
 </resources>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 28fb119..371d388 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -44,13 +44,10 @@
     <string name="add_to_home_screen" msgid="9168649446635919791">"Dodaj na početni ekran"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Dodali ste vidžet <xliff:g id="WIDGET_NAME">%1$s</xliff:g> na početni ekran"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Predlozi"</string>
-    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Neophodne aplikacije"</string>
+    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Osnovno"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Novosti i časopisi"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zona za opuštanje"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Zabava"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Društvene mreže"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Zdravlje i fitnes"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Vreme"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Predloženo za vas"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Vidžeti <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> sa desne strane, pretraga i opcije sa leve strane"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# vidžet}one{# vidžet}few{# vidžeta}other{# vidžeta}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privatno, zaključano."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Zaključavanje"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Prenos privatnog prostora"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Instalirajte aplikacije"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Instaliraj aplikacije u privatan prostor"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Preklopno"</string>
 </resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index c3b384c..7614fd8 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Прапановы"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Асноўнае"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Навіны і часопісы"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Зона адпачынку"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Забавы"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Сацыяльныя сеткі"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Здароўе і фітнес"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Надвор\'е"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Рэкамендавана для вас"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Віджэты праграмы \"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>\" справа, пошук і параметры злева"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# віджэт}one{# віджэт}few{# віджэты}many{# віджэтаў}other{# віджэта}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Прыватная прастора, заблакіравана."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Заблакіраваць"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Пераход у прыватную вобласць"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Усталяваць праграмы"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Усталяваць праграмы ў прыватнай прасторы"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Дадатковае меню"</string>
 </resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 7915de9..d1edca8 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Предложения"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Приспособления, които трябва да изпробвате"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Новини и списания"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Зоната ви за разпускане"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Развлечения"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Социални мрежи"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Здраве и фитнес"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Времето"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Предложено за вас"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Приспособленията за <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> са отдясно, търсенето и опциите – отляво"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# приспособление}other{# приспособления}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Частно, заключено."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Заключване"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Преминаване към личното пространство"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Инсталиране на приложения"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Инсталиране на приложения в частно пространство"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Препълване"</string>
 </resources>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index a19fbfc..0a791e2 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"সাজেশন"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"প্রয়োজনীয় জিনিস"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"খবর ও ম্যাগাজিন"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"আপনার চিল জোন"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"বিনোদন"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"সোশ্যাল"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"স্বাস্থ্য ও ফিটনেস"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"আবহাওয়া"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"আপনার জন্য সাজেস্ট করা হয়েছে"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> উইজেট ডানদিকে, সার্চ ও বিকল্প বাঁদিকে রয়েছে"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{#টি উইজেট}one{#টি উইজেট}other{#টি উইজেট}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"ব্যক্তিগত, লক করা আছে।"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"লক"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"ব্যক্তিগত স্পেস ট্রানজিট করা"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"অ্যাপ ইনস্টল করুন"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"প্রাইভেট স্পেসে অ্যাপ ইনস্টল করুন"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ওভারফ্লো"</string>
 </resources>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 74fe854..7cb54d0 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Prijedlozi"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Osnovne aplikacije"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Vijesti i časopisi"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Vaša zona opuštanja"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Zabava"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Društvene mreže"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Zdravlje i fitnes"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Vrijeme"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Predloženo za vas"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Vidžeti aplikacije <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> su na desnoj, a pretraživanje i opcije na lijevoj strani"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# vidžet}one{# vidžet}few{# vidžeta}other{# vidžeta}}"</string>
@@ -65,7 +62,7 @@
     <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Posao"</string>
     <string name="widget_category_conversations" msgid="8894438636213590446">"Razgovori"</string>
     <string name="widget_category_note_taking" msgid="3469689394504266039">"Pisanje bilješki"</string>
-    <string name="widget_add_button_label" msgid="2761267068711937179">"Dodaj"</string>
+    <string name="widget_add_button_label" msgid="2761267068711937179">"Dodajte"</string>
     <string name="widget_add_button_content_description" msgid="1810530016360039643">"Dodavanje vidžeta <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
     <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Dodirnite da promijenite postavke vidžeta"</string>
     <string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Promjena postavki vidžeta"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privatno, zaključano."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Zaključaj"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Prelazak u privatan prostor"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Instaliranje aplikacija"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Instaliranje aplikacija u privatni prostor"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Preklopni meni"</string>
 </resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 50bb41f..fc57a88 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggeriments"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essencials"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Notícies i revistes"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"La teva zona de relax"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entreteniment"</string>
-    <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Salut i fitnes"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Temps"</string>
+    <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Xarxes socials"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Suggeriments per a tu"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgets de <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> a la dreta, cerca i opcions a l\'esquerra"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privat, bloquejat."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Bloqueja"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Canvia a Espai privat"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Instal·la apps"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Instal·la les aplicacions a Espai privat"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Menú addicional"</string>
 </resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index b05002d..28fd138 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -44,13 +44,10 @@
     <string name="add_to_home_screen" msgid="9168649446635919791">"Přidat na plochu"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> byl přidán na plochu"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Návrhy"</string>
-    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Základní"</string>
+    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Nejdůležitější aplikace"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Zprávy a časopisy"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Vaše klidová zóna"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Zábava"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sociální sítě"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Zdraví a fitness"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Počasí"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Návrhy pro vás"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgety <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> vpravo, vyhledávání a možnosti vlevo"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{ # widget}few{# widgety}many{# widgetu}other{# widgetů}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Soukromé, uzamčeno."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Zamknout"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Převádění soukromého prostoru"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Instalovat aplikace"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Instalovat aplikace do soukromého prostoru"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Rozbalovací nabídka"</string>
 </resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 7d4e804..f813ff0 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Forslag"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Vigtige ting"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Aviser og blade"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Dit afslapningshjørne"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Underholdning"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Socialt"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Sundhed og fitness"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Vejr"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Forslag til dig"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>-widgets til højre, søgning og valgmuligheder til venstre"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}other{# widgets}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privat, låst."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Lås"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Ændringer af tilstanden for det private område"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Installer apps"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Installer apps i privat område"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Overløb"</string>
 </resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 230c578..990d533 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Vorschläge"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Must-haves"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Nachrichten und Zeitschriften"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zum Entspannen"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Unterhaltung"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Soziale Netzwerke"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Gesundheit und Fitness"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Wetter"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Vorschläge für dich"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>-Widgets rechts, Suche und Optionen links"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# Widget}other{# Widgets}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privat, gesperrt."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Sperren"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Sperrzustand des privaten Bereichs wird gerade geändert"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Apps installieren"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Apps im privaten Bereich installieren"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Weitere Optionen"</string>
 </resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 03fce60..59ff1c9 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Προτάσεις"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Απαραίτητα"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Ειδήσεις και περιοδικά"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Ο δικός σας τρόπος χαλάρωσης"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Ψυχαγωγία"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Κοινωνικά δίκτυα"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Υγεία και φυσική κατάσταση"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Καιρός"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Προτεινόμενα για εσάς"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Γραφικά στοιχεία <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> στα δεξιά, αναζήτηση και επιλογές στα αριστερά"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# γραφικό στοιχείο}other{# γραφικά στοιχεία}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Ιδιωτικό, κλειδωμένο."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Κλείδωμα"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Μετάβαση στον Ιδιωτικό χώρο"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Εγκατάσταση εφαρμογών"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Εγκατάσταση εφαρμογών στον απόρρητο χώρο"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Υπερχείλιση"</string>
 </resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 37c74a5..000a021 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggestions"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essentials"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"News and magazines"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Your chill zone"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entertainment"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Health and fitness"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Weather"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Suggested for you"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> widgets on right, search and options on left"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Private, locked."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Lock"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Private Space transitioning"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Install apps"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Install apps to private space"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Overflow"</string>
 </resources>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index a36e96c..b96374d 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggestions"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essentials"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"News &amp; magazines"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Your Chill Zone"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entertainment"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Health &amp; fitness"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Weather"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Suggested for you"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> widgets on right, search and options on left"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
@@ -193,7 +190,7 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Private, locked."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Lock"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Private Space Transitioning"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Install apps"</string>
+    <string name="ps_add_button_label" msgid="8127988716897128773">"Install"</string>
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Install apps to Private Space"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Overflow"</string>
 </resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 37c74a5..000a021 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggestions"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essentials"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"News and magazines"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Your chill zone"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entertainment"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Health and fitness"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Weather"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Suggested for you"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> widgets on right, search and options on left"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Private, locked."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Lock"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Private Space transitioning"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Install apps"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Install apps to private space"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Overflow"</string>
 </resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 37c74a5..000a021 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggestions"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essentials"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"News and magazines"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Your chill zone"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entertainment"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Health and fitness"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Weather"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Suggested for you"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> widgets on right, search and options on left"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Private, locked."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Lock"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Private Space transitioning"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Install apps"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Install apps to private space"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Overflow"</string>
 </resources>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index 04c2dcf..f6e3152 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‏‏‎‎‎‎‏‎‏‎‎‏‎‎‏‏‎‏‏‏‎‎‎‏‏‏‎‏‏‎‎‎‎‎‎‏‎‏‏‏‎‏‎‏‎‏‏‎‎‎‏‏‎‎Suggestions‎‏‎‎‏‎"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‎‎‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‏‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‎‎‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‎Essentials‎‏‎‎‏‎"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‏‎‏‎‏‏‏‎‏‎‏‏‏‎‎‎‎‏‎‎‏‎‎‎‏‎‎‎‏‏‏‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎News &amp; magazines‎‏‎‎‏‎"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‎‏‎‎‏‏‏‎‎‏‎‎‎‏‏‏‏‎‎‏‎‏‎‏‎‏‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‏‎‏‎‏‎‏‏‏‏‏‎Your Chill Zone‎‏‎‎‏‎"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‏‎‎‎‏‏‎‏‎‏‎‎‎‎‎‎‎‎‎‎‏‎‏‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‎‏‎‏‏‎‏‏‎‎‏‎‎Entertainment‎‏‎‎‏‎"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‎‏‏‎‎‏‎‎‎‎‎‏‎‏‏‎‎‎‎‎‏‎‏‎‎‏‏‏‎‎‏‏‎‎‏‎‎‎‏‏‎‏‎‎‎‎‏‎‏‎‏‏‎‎‏‏‎‏‎Social‎‏‎‎‏‎"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎‎‎‏‎‏‎‏‏‎‏‏‎‎‎Health &amp; fitness‎‏‎‎‏‎"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‎‎‏‎‎‏‎‏‏‎‏‏‏‏‎‏‏‏‏‎‎‏‏‎‎‏‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎Weather‎‏‎‎‏‎"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‎‏‏‎‏‎‏‏‎‏‎‏‎‎‏‏‎‎‎‏‎‏‏‏‎‎‏‎‎‏‎‏‏‎‎‎‏‎‎‎‏‎‎‏‎‎‏‎‎‎‎‏‎‏‎Suggested for you‎‏‎‎‏‎"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‏‏‏‎‎‎‏‏‎‎‏‏‏‎‎‎‏‎‎‏‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‏‏‎‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>‎‏‎‎‏‏‏‎ widgets on right, search and options on left‎‏‎‎‏‎"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‏‎‏‎‎‏‎‎‏‏‏‎‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‎‎‎‎# widget‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‏‎‏‎‎‏‎‎‏‏‏‎‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‎‎‎‎# widgets‎‏‎‎‏‎}}"</string>
@@ -193,7 +190,7 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‏‏‏‎‏‎‏‎‎‎‎‎‏‎‏‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‎‏‎‏‏‏‎‏‎‎Private, locked.‎‏‎‎‏‎"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‎‎‏‎‎‎‎‎‏‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‏‎‎‏‏‎‎‎‏‎‏‏‏‎‎‏‏‎‎‎‎‎‎‎‏‎‏‎‎Lock‎‏‎‎‏‎"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‎‎‎‏‎‎‎‏‏‎‎‎‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‎‎‎‏‏‎‎‎‏‎‎‏‏‎‎‎Private Space Transitioning‎‏‎‎‏‎"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‎‎‏‎‎‏‏‏‎‏‏‎‏‎‏‎‎‏‏‎‎‏‎‏‏‏‎‎‎‏‏‎‎‎‎‏‏‏‏‎‏‎‎‎‎‎‏‏‏‏‎Install apps‎‏‎‎‏‎"</string>
+    <string name="ps_add_button_label" msgid="8127988716897128773">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‎‎‏‏‎‏‎‏‎‏‎‏‎‏‏‎‎‏‎‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‎‏‎Install‎‏‎‎‏‎"</string>
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‏‎‎‏‏‎‎‎‎‎‎‏‎‎‎‎‏‏‏‏‎‎‏‎‎‎‎‏‏‎‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‏‏‎‏‏‎‎‎Install apps to Private Space‎‏‎‎‏‎"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‏‏‎‎‏‎‎‏‎‎‏‎‏‎‎‏‎‏‎‏‏‏‏‎‏‎‎‎‏‎‎‏‎‏‏‎‎‏‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎‎Overflow‎‏‎‎‏‎"</string>
 </resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 4aaa44b..9b252b47 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Sugerencias"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Imprescindibles"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Noticias y revistas"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zona de descanso"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entretenimiento"</string>
-    <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Salud y bienestar"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Clima"</string>
+    <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Redes sociales"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sugerencias para ti"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgets de <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> a la derecha, búsqueda y opciones a la izquierda"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privado (bloqueado)"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Bloqueo"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Pasar a Espacio privado"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Instala apps"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Instala las apps en el espacio privado"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Ampliada"</string>
 </resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index c91e510..cd22d50 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Sugerencias"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Imprescindibles"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Noticias y revistas"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Tu zona de descanso"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entretenimiento"</string>
-    <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Salud y actividad física"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"El tiempo"</string>
+    <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Redes sociales"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sugerencias para ti"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgets de <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> a la derecha, búsqueda y opciones a la izquierda"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privado, bloqueado."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Bloquear"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Cambiar a espacio privado"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Descarg. apps"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Descargar aplicaciones en el espacio privado"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Desplegable"</string>
 </resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 226ccd1..83b5a65 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Soovitused"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Põhiasjad"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Uudised ja ajakirjad"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Teie lõõgastumiskoht"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Meelelahutus"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Suhtlus"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Tervis ja vormisolek"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Ilm"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Teile soovitatud"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Teenuse <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> vidinad paremal, otsing ja valikud vasakul"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# vidin}other{# vidinat}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privaatne, lukustatud."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Lukk"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Privaatse ruumi üleviimine"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Rakenduste installimine"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Rakenduste installimine privaatses ruumis"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Ületäide"</string>
 </resources>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index b564f57..c6e6ca7 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Iradokizunak"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Oinarrizkoak"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Albisteak eta aldizkariak"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Lasaitzeko gunea"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Aisia"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sare sozialak"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Osasuna eta ongizatea"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Eguraldia"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Zuri iradokiak"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> zerbitzuaren widgetak eskuinean, bilaketa eta aukerak ezkerrean"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widget}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Pribatua, blokeatuta."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Blokeatu"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Eremu pribaturako trantsizioa"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Instalatu aplikazioak"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Instalatu aplikazioak eremu pribatuan"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Luzapena"</string>
 </resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 2ef33d8..ac3d27b 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"پیشنهادها"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"بایدها"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"اخبار و مجله"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"منطقه آرامش شما"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"سرگرمی"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"اجتماعی"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"سلامتی و تناسب اندام"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"آب‌وهوا"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"پیشنهاداتی برای شما"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"ابزارک‌های <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> در سمت چپ، جستجو و گزینه‌ها در سمت راست"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{‏# ابزارک}one{‏# ابزارک}other{‏# ابزارک}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"خصوصی، قفل."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"قفل کردن"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"انتقال «فضای خصوصی»"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"نصب برنامه‌ها"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"نصب برنامه‌ها در «فضای خصوصی»"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"سرریز"</string>
 </resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index f6d6ab8..9b432de 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Ehdotukset"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Kaikki tarvittava"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Uutiset ja aikakauslehdet"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Ota rennosti"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Viihde"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sosiaalinen"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Terveys ja kuntoilu"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Sää"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sinulle ehdotetut"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> widgetit oikealla, haku ja vaihtoehdot vasemmalla"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgetiä}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Yksityinen, lukittu."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Lukko"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Yksityisen tilan siirtäminen"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Asenna sovelluksia"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Asenna sovelluksia yksityiseen tilaan"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Ylivuoto"</string>
 </resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index c83f57e..e650524 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggestions"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essentiels"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Actualités et magazines"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zone de divertissement"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Divertissement"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Médias sociaux"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Santé et mise en forme"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Météo"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Suggestions personnalisées"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgets <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> à droite, recherche et options à gauche"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}other{# widgets}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privé, verrouillé."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Verrouiller"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Transition vers l\'Espace privé"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Installer des applications"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Installer des applications dans l\'Espace privé"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Menu à développer"</string>
 </resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 4f6f121..93d94fb 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -44,13 +44,10 @@
     <string name="add_to_home_screen" msgid="9168649446635919791">"Ajouter à l\'écran d\'accueil"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ajouté à l\'écran d\'accueil"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggestions"</string>
-    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Les bases"</string>
+    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Indispensables"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Actualités et magazines"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Votre espace détente"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Divertissement"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Réseaux sociaux"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Santé et bien-être"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Météo"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Recommandations"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgets <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> à droite, recherche et options à gauche"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}other{# widgets}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privé, verrouillé"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Verrouiller"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Transition vers Espace privé"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Installer applis"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Installer des applis dans l\'espace privé"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Dépassement"</string>
 </resources>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 3137d94..fa01b31 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suxestións"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Esenciais"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Noticias e revistas"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Reláxate"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entretemento"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Redes sociais"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Saúde e forma física"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"O tempo"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Suxestións personalizadas"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgets de <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> á dereita, busca e opcións á esquerda"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privado, bloqueado."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Bloquear"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Transición ao espazo privado"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Instalar apps"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Instalar as aplicacións no espazo privado"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Menú adicional"</string>
 </resources>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 8ef75fe..794d2fc 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"સૂચનો"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"આવશ્યક"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"ન્યૂઝ અને સામાયિકો"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"તમારો આરામદાયક ઝોન"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"મનોરંજન"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"સામાજિક"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"આરોગ્ય અને ફિટનેસ"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"હવામાન"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"તમારા માટે સૂચવેલી સેવાઓ"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>ની વિજેટ જમણે, શોધ અને વિકલ્પો ડાબે"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# વિજેટ}one{# વિજેટ}other{# વિજેટ}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"ખાનગી સ્પેસ, લૉક કરેલી છે."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"લૉક"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"ખાનગી સ્પેસ પર સ્થાનાંતરણ"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"ઍપ ઇન્સ્ટૉલ કરો"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"ખાનગી સ્પેસમાં ઍપ ઇન્સ્ટૉલ કરો"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ઓવરફ્લો"</string>
 </resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 5f489ae..0b505a3 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"सुझाव"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"ज़रूरी ऐप्लिकेशन"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"खबरों और पत्रिकाओं वाले ऐप्लिकेशन"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"आपके मनोरंजन के लिए"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"मनोरंजन से जुड़े ऐप्लिकेशन"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"सोशल मीडिया ऐप्लिकेशन"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"हेल्थ और फ़िटनेस वाले ऐप्लिकेशन"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"मौसम"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"आपके लिए सुझाए गए ऐप्लिकेशन"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> के विजेट दाईं ओर, खोज का विजेट और अन्य विकल्प बाईं ओर"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# विजेट}one{# विजेट}other{# विजेट}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"प्राइवेट स्पेस को लॉक किया गया."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"लॉक"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"प्राइवेट स्पेस की सेटिंग में बदलाव किया जा रहा है"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"ऐप्लिकेशन इंस्टॉल करें"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"प्राइवेट स्पेस में ऐप्लिकेशन इंस्टॉल करें"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ओवरफ़्लो"</string>
 </resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index db947fe..64d5d10 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Prijedlozi"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Osnovno"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Vijesti i časopisi"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Vaša zona za opuštanje"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Zabava"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Društvene mreže"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Zdravlje i fitness"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Vrijeme"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Prijedlozi za vas"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> –widgeti zdesna, pretraživanje i opcije slijeva"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}few{# widgeta}other{# widgeta}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privatno, zaključano."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Zaključavanje"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Prelazak na privatni prostor"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Instalirajte aplikacije"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Instaliranje aplikacija u privatni prostor"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Dodatni izbornik"</string>
 </resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index bdf5aa6..40b1072 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Javaslatok"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Legfontosabbak"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Újságok és magazinok"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Az Ön relaxáló zónája"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Szórakozás"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Közösségi"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Egészség és fitnesz"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Időjárás"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Neked javasolt"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"A <xliff:g id="SELECTED_HEADER">%1$s</xliff:g>-modulok a jobb, a kereső és a beállítások pedig a bal oldalon találhatók"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# modul}other{# modul}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privát, zárolt."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Zárolás"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Átállás privát területre…"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"App telepítése"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Alkalmazások telepítése magánterületre"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Túlcsordulás"</string>
 </resources>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 4a427f4..afd0a99 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Առաջարկներ"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Հիմնական"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Նորություններ և ամսագրեր"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Ձեր հանգստի գոտին"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Զվարճանք"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Սոցցանցեր"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Առողջություն և ֆիթնես"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Եղանակ"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Առաջարկում ենք"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"«<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>» հավելվածի վիջեթներն աջ կողմում են, իսկ որոնման դաշտը և կարգավորումները՝ ձախ կողմում"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# վիջեթ}one{# վիջեթ}other{# վիջեթ}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Անձնական, կողպված է։"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Կողպում"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Անցում մասնավոր տարածք"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Հավելվածների տեղադրում"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Հավելվածների տեղադրում անձնական տարածքում"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Լրացուցիչ ընտրացանկ"</string>
 </resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 141a4d2..e9f3f73 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Saran"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Penting"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Berita &amp; majalah"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zona Nyaman Anda"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Hiburan"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sosial"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Kesehatan &amp; kebugaran"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Cuaca"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Disarankan untuk Anda"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widget <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> di bagian kanan, penelusuran dan opsi di bagian kiri"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widget}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Pribadi, dikunci."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Kunci"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Ruang Pribadi Bertransisi"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Instal aplikasi"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Instal aplikasi ke Ruang Pribadi"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Menu tambahan"</string>
 </resources>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 5c4b3e9..93b8fe0 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Tillögur"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Það nauðsynlegasta"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Fréttir og tímarit"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Slakaðu á"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Afþreying"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Samfélag"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Heilsa og líkamsrækt"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Veður"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Tillögur fyrir þig"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>-græjur til hægri, leit og valkostir til vinstri"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# græja}one{# græja}other{# græjur}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Lokað, læst."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Læsa"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Einkarými að breytast"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Setja upp forrit"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Setja upp forrit í leynirými"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Yfirflæði"</string>
 </resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index bb178f5..4257b5f 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggerimenti"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essenziali"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Notizie e riviste"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Il tuo angolo di tranquillità"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Intrattenimento"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Salute e fitness"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Meteo"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Consigliati per te"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widget di <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> a destra, ricerca e opzioni a sinistra"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widget}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privato, bloccato."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Blocca"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Transizione dello Spazio privato in corso…"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Installa app"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Installa le app su spazi privati"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Extra"</string>
 </resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 4140088..2ecb026 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -44,13 +44,10 @@
     <string name="add_to_home_screen" msgid="9168649446635919791">"הוספה למסך הבית"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"הווידג\'ט <xliff:g id="WIDGET_NAME">%1$s</xliff:g> נוסף למסך הבית"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"הצעות"</string>
-    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"האפליקציות שחייבים להכיר"</string>
+    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"הכי חשוב"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"חדשות וכתבי עת"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"המקום שלך לרגיעה"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"בידור"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"רשתות חברתיות"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"בריאות וכושר"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"מזג אוויר"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"הצעות בשבילך"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"‫<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ווידג\'טים מימין, חיפוש ואפשרויות משמאל"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{ווידג\'ט אחד}one{# ווידג\'טים}two{# ווידג\'טים}other{# ווידג\'טים}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"פרטי, נעול."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"נעילה"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"מעבר למרחב הפרטי"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"התקנת אפליקציות"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"התקנת אפליקציות במרחב הפרטי"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"אפשרויות נוספות"</string>
 </resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 6276a20..f4f3852 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"候補"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"基本"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"ニュース&雑誌"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"休憩エリア"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"エンタメ"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"ソーシャル"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"健康&フィットネス"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"天気"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"おすすめ"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> のウィジェットは右側に、検索とオプションは左側にあります"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# 件のウィジェット}other{# 件のウィジェット}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"非公開で、ロックされています。"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"ロック"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"プライベート スペース移行中"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"アプリをインストールする"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"プライベート スペースにアプリをインストールします"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"オーバーフロー"</string>
 </resources>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index d6c44ea..cd5598b 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"შეთავაზებები"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"უმნიშვნელოვანესები"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"ახალი ამბები და ჟურნალები"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"განტვირთვის ადგილი"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"გართობა"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"სოციალური"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"ჯანმრთელობა და ფიტნესი"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"ამინდი"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"თქვენთვის შემოთავაზებული"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ვიჯეტები მდებარეობს მარჯვნივ, ძებნა და პარამეტრები — მარცხნივ"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ვიჯეტი}other{# ვიჯეტი}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"პირადი (ჩაკეტილი)."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"ჩაკეტვა"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"პირად სივრცეზე გადასვლა"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"აპების ინსტალაცია"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"კერძო სივრცეში აპების ინსტალაცია"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"გადავსება"</string>
 </resources>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 6fd40e5..f364ed5 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Ұсыныстар"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Ең қажетті"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Жаңалықтар мен журналдар"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Жанға жайлы жер"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Ойын-сауық"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Қоғам"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Денсаулық және фитнес"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Ауа райы"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Сізге ұсынылғандар"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> виджеттері оң жақта, іздеу мен опциялар сол жақта"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# виджет}other{# виджет}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Құпия (құлыптаулы)."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Құлыптау"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Жеке бөлмеге өту"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Қолданбалар орнату"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Қолданбаларды \"Құпия кеңістікке\" орнатыңыз."</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Қосымша мәзір"</string>
 </resources>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 7abe7fd..8f24162 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"ការណែនាំ"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"សំខាន់"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"ព័ត៌មាន និង​ទស្សនាវដ្ដី"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"តំបន់បន្ធូរ​អារម្មណ៍របស់អ្នក"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"កម្សាន្ត"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"សង្គម"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"សុខភាព និង​សម្បទា"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"អាកាសធាតុ"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"ណែនាំជូនអ្នក"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"ធាតុក្រាហ្វិក <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> នៅខាងស្ដាំ ការស្វែងរក និងជម្រើសនៅខាងឆ្វេង"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{ធាតុ​ក្រាហ្វិក #}other{ធាតុ​ក្រាហ្វិក #}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"ឯកជន ជាប់សោ។"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"ចាក់សោ"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"ការផ្លាស់ប្ដូរ Private Space"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"ដំឡើង​កម្មវិធី"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"ដំឡើងកម្មវិធីទៅលំហឯកជន"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ម៉ឺនុយបន្ថែម"</string>
 </resources>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 01800a8..aa50b3b 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"ಸಲಹೆಗಳು"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"ಅಗತ್ಯತೆಗಳು"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"ಸುದ್ದಿ ಮತ್ತು ನಿಯತಕಾಲಿಕೆಗಳು"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"ನೀವು ವಿಶ್ರಾಂತಿ ಪಡೆಯುವ ಸ್ಥಳ"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"ಮನರಂಜನೆ"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"ಸಾಮಾಜಿಕ"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"ಆರೋಗ್ಯ ಮತ್ತು ಫಿಟ್‌ನೆಸ್"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"ಹವಾಮಾನ"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"ನಿಮಗಾಗಿ ಸೂಚಿಸಲಾಗಿರುವುದು"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"ಬಲಭಾಗದಲ್ಲಿ <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ವಿಜೆಟ್‌ಗಳು, ಎಡಭಾಗದಲ್ಲಿ ಹುಡುಕಾಟ ಮತ್ತು ಆಯ್ಕೆಗಳು"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ವಿಜೆಟ್}one{# ವಿಜೆಟ್‌ಗಳು}other{# ವಿಜೆಟ್‌ಗಳು}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"ಖಾಸಗಿ, ಲಾಕ್ ಮಾಡಲಾಗಿದೆ."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"ಲಾಕ್ ಮಾಡಿ"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"ಖಾಸಗಿ ಸ್ಪೇಸ್ ಪರಿವರ್ತನೆಯಾಗುತ್ತಿದೆ"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"ಆ್ಯಪ್‌ಗಳನ್ನು ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿ"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"ಆ್ಯಪ್‌ಗಳನ್ನು ಪ್ರೈವೇಟ್ ಸ್ಪೇಸ್‌ನಲ್ಲಿ ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿ"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ಓವರ್‌ಫ್ಲೋ"</string>
 </resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 0b11f1a..5514416 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"추천"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"필수"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"뉴스 및 잡지"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"휴식 공간"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"엔터테인먼트"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"소셜"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"건강 및 피트니스"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"날씨"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"추천"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"오른쪽에 <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> 위젯, 왼쪽에 검색 및 옵션"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{위젯 #개}other{위젯 #개}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"비공개, 잠김."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"잠금"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"비공개 스페이스 전환"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"앱 설치"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"비공개 스페이스에 앱 설치"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"오버플로"</string>
 </resources>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 6561559..cd73445 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Сунуштар"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Эң зарыл параметрлер"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Жаңылыктар жана журналдар"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Чер жазуу"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Көңүл ачуу"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Коомдук тармактар"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Ден соолук жана дене-бойду чыңдоо"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Аба ырайы"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Сизге сунушталат"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> виджеттери оң, ал эми издөө жана параметрлер сол жакта"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# виджет}other{# виджет}}"</string>
@@ -186,14 +183,15 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Чыпкалоо"</string>
     <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="private_space_secondary_label" msgid="9203933341714508907">"Тууралоо же ачуу үчүн тийип коюңуз"</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>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Кулпулоо"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Жеке чөйрөгө өтүү"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Колдонмолорду орнотуу"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Колдонмолорду Жеке мейкиндикке орнотуe"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Кошумча меню"</string>
 </resources>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 89be107..7f5cd65 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"ການແນະນຳ"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"ສິ່ງຈຳເປັນ"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"ຂ່າວ ແລະ ວາລະສານ"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"ພື້ນທີ່ພັກຜ່ອນຂອງທ່ານ"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"ຄວາມບັນເທີງ"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"ສັງຄົມ"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"ສຸຂະພາບ ແລະ ການອອກກຳລັງກາຍ"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"ສະພາບອາກາດ"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"ແນະນຳສຳລັບທ່ານ"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"ວິດເຈັດ <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ຢູ່ທາງຂວາ, ການຊອກຫາ ແລະ ຕົວເລືອກຢູ່ທາງຊ້າຍ"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ວິດເຈັດ}other{# ວິດເຈັດ}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"ສ່ວນຕົວ, ລັອກແລ້ວ."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"ລັອກ"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"ການປ່ຽນແປງພື້ນທີ່ສ່ວນຕົວ"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"ຕິດຕັ້ງແອັບ"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"ຕິດຕັ້ງແອັບໄປໃສ່ພື້ນທີ່ສ່ວນບຸກຄົນ"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ການດຳເນີນການເພີ່ມເຕີມ"</string>
 </resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 9a19094..38d4597 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Pasiūlymai"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Būtiniausi"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Naujienos ir žurnalai"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Jūsų atsipalaidavimo zona"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Pramogos"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Socialiniai tinklai"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Sveikata ir kūno rengyba"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Orai"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Siūloma jums"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> valdikliai dešinėje, paieška ir parinktys kairėje"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# valdiklis}one{# valdiklis}few{# valdikliai}many{# valdiklio}other{# valdiklių}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privatus, užrakintas."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Užrakinti"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Privačios erdvės perkėlimas"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Programų diegimas"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Įdiegti programas privačioje erdvėje"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Perpildymas"</string>
 </resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 2c77a54..417464c 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Ieteikumi"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Produktivitātei"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Ziņas un žurnāli"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Jūsu atpūtas stūrītis"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Izklaide"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sociālie tīkli"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Veselība un fitness"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Laikapstākļi"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Ieteikumi jums"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Pa labi logrīki <xliff:g id="SELECTED_HEADER">%1$s</xliff:g>, pa kreisi meklēšana un iespējas"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# logrīks}zero{# logrīku}one{# logrīks}other{# logrīki}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privāta un bloķēta."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Bloķēšana"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Pāriet uz privāto mapi"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Lietotņu instalēšana"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Instalējiet lietotnes privātajā telpā."</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Pārpilde"</string>
 </resources>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 3618e22..828a65e 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Предлози"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Неопходни"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Вести и списанија"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Вашата зона за релаксација"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Забава"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Друштвени"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Здравје и фитнес"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Време"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Препорачано за вас"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> виџети оддесно, „Пребарување“ и „Опции“ одлево"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# виџет}one{# виџет}other{# виџети}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Приватно, заклучено."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Заклучи"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Префрлање на „Приватен простор“"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Инсталирајте апликации"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Инсталирање апликации во „Приватен простор“"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Проширено балонче"</string>
 </resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index dde91aa..290c7fe 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"നിർദ്ദേശങ്ങൾ"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"ആവശ്യമായവ"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"വാർത്തകളും മാസികകളും"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"നിങ്ങൾക്ക് സുഖപ്രദമായ സ്ഥലം"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"വിനോദം"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"സാമൂഹികം"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"ആരോഗ്യവും ശാരീരികക്ഷമതയും"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"കാലാവസ്ഥ"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"നിങ്ങൾക്കായി നിർദ്ദേശിച്ചവ"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"വലതുവശത്ത് <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> വിജറ്റുകളും ഇടതുവശത്ത് തിരയൽ, ഓപ്ഷനുകൾ എന്നിവയും"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# വിജറ്റ്}other{# വിജറ്റുകൾ}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"സ്വകാര്യം, ലോക്ക് ചെയ്തു."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"ലോക്ക് ചെയ്യുക"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"പ്രൈവറ്റ് സ്‌പേസ് ട്രാൻസിഷനിംഗ്"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യുക"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"സ്വകാര്യ സ്പേസിലേക്ക് ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യുക"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ഓവർഫ്ലോ"</string>
 </resources>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index ed17387..71f5b77 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Зөвлөмжүүд"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Зайлшгүй хэрэгтэй"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Мэдээ, сэтгүүлүүд"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Таны амралтын бүс"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Энтертэйнмент"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Сошиал"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Эрүүл мэнд, фитнес"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Цаг агаар"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Танд санал болгосон"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Баруун талд <xliff:g id="SELECTED_HEADER">%1$s</xliff:g>-н виджет, зүүн талд хайлт болон сонгуултууд байна"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# виджет}other{# виджет}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Хувийн, түгжээтэй."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Түгжээ"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Private Space-н шилжилт"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Аппуудыг суулгах"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Хувийн орон зайд аппууд суулгана уу"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Урт цэс"</string>
 </resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index e7287d8..a95eb6c 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"सूचना"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"आवश्यक गोष्टी"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"बातम्‍या आणि मासिके"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"तुमचा आरामदायक झोन"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"मनोरंजन"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"सोशल"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"आरोग्य आणि फिटनेस"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"हवामान"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"तुमच्यासाठी सुचवलेले"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"उजवीकडे <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> विजेट, डावीकडे शोध आणि पर्याय"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# विजेट}other{# विजेट}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"खाजगी, लॉक केलेली."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"लॉक"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"खाजगी स्पेस वर स्विच करणे"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"अ‍ॅप्स इंस्टॉल करा"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"अ‍ॅप्स खाजगी स्पेस मध्ये इंस्टॉल करा"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ओव्हरफ्लो"</string>
 </resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 099400f..3704ec9 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -44,13 +44,10 @@
     <string name="add_to_home_screen" msgid="9168649446635919791">"Tambahkan pada skrin utama"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ditambahkan pada skrin utama"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Cadangan"</string>
-    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Apl Asas"</string>
+    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Penting"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Berita &amp; majalah"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zon Santai Anda"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Hiburan"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sosial"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Kesihatan &amp; kecergasan"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Cuaca"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Dicadangkan untuk anda"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widget <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> pada sebelah kanan, carian dan pilihan pada sebelah kiri"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widget}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Peribadi, dikunci."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Kunci"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Peralihan Ruang Peribadi"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Pasang apl"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Pasang apl pada Ruang Peribadi"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Limpahan"</string>
 </resources>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 01d1a8f..a4ec89a 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"အကြံပြုချက်"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"မရှိမဖြစ်များ"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"သတင်းနှင့် မဂ္ဂဇင်း"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"သင်အနားယူသောနေရာ"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"ဖျော်ဖြေရေး"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"လူမှုရေး"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"ကျန်းမာကြံ့ခိုင်ရေး"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"မိုးလေဝသ"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"သင့်အတွက် အကြံပြုထားသည်များ"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ဝိဂျက်များသည် ညာဘက်တွင်ရှိပြီး ရှာဖွေမှုနှင့် ရွေးစရာများသည် ဘယ်ဘက်တွင်ရှိသည်"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{ဝိဂျက် # ခု}other{ဝိဂျက် # ခု}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"သီးသန့် လော့ခ်ချထားသည်။"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"လော့ခ်ချခြင်း"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"သီးသန့်ချတ်ခန်း အပြောင်းအလဲ"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"အက်ပ်ထည့်ခြင်း"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"‘သီးသန့်နေရာ’ တွင် အက်ပ်များ ထည့်သွင်းနိုင်သည်"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"မီနူးအပို"</string>
 </resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 56ef9e7..c2fdeaa 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Forslag"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Anbefalt"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Nyheter og tidsskrifter"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Avslappingssonen din"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Underholdning"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sosialt"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Kropp og helse"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Været"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Foreslått for deg"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> moduler til høyre, søk og alternativer til venstre"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# modul}other{# moduler}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privat (låst)."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Lås"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Private Space-overgang"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Installer apper"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Installer apper i privat område"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Overflyt"</string>
 </resources>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 4686ba0..2d407b2 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"सुझावहरू"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"अत्यावश्यक कुराहरू"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"समाचार तथा पत्रपत्रिकाहरू"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"तपाईंको Chill Zone"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"मनोरञ्जन"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"सोसल मिडिया"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"स्वास्थ्य तथा तन्दुरुस्ती"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"मौसम"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"तपाईंका लागि सिफारिस गरिएका"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"दायाँ भागमा <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> विजेटहरू, बायाँ भागमा खोज र विकल्पहरू"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# विजेट}other{# वटा विजेट}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"निजी, लक गरिएको।"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"लक गर्नुहोस्"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"निजी स्पेस ट्रान्जिसन गरिँदै छ"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"एपहरू इन्स्टल गर्नुहोस्"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"निजी स्पेसमा एपहरू इन्स्टल गर्नुहोस्"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ओभरफ्लो"</string>
 </resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index a61416e..a9e5359 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggesties"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essentials"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Nieuws en tijdschriften"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Je chillzone"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entertainment"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sociaal"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Gezondheid en fitness"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Weer"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Voorgesteld voor jou"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>-widgets aan de rechterkant, zoeken en opties aan de linkerkant"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privé, vergrendeld."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Vergrendelen"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Overschakelen naar privéruimte"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Apps installeren"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Apps installeren in privégedeelte"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Overloop"</string>
 </resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index da70678..291fd9b 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"ପରାମର୍ଶଗୁଡ଼ିକ"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"ଅତ୍ୟାବଶ୍ୟକୀୟ"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"ନ୍ୟୁଜ ଓ ମାଗାଜିନ"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"ଆପଣଙ୍କ ଚିଲ ଜୋନ"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"ମନୋରଞ୍ଜନ"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"ସୋସିଆଲ"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"ସ୍ୱାସ୍ଥ୍ୟ ଓ ଫିଟନେସ"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"ପାଣିପାଗ"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"ଆପଣଙ୍କ ପାଇଁ ପ୍ରସ୍ତାବିତ"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"ଡାହାଣରେ <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ୱିଜେଟଗୁଡ଼ିକ ଅଛି, ବାମରେ ସର୍ଚ୍ଚ ଓ ବିକଳ୍ପଗୁଡ଼ିକ ଅଛି"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ୱିଜେଟ}other{# ୱିଜେଟ}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"ପ୍ରାଇଭେଟ, ଲକ କରାଯାଇଛି।"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"ଲକ କରନ୍ତୁ"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"ପ୍ରାଇଭେଟ ସ୍ପେସ ଟ୍ରାଞ୍ଜିସନିଂ"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"ଆପ୍ ଇନଷ୍ଟଲ୍ କରନ୍ତୁ"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"ଆପ୍ସକୁ ପ୍ରାଇଭେଟ ସ୍ପେସରେ ଇନଷ୍ଟଲ କରନ୍ତୁ"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ଓଭରଫ୍ଲୋ"</string>
 </resources>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 3c1a37e..097d466 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"ਸੁਝਾਅ"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"ਲੋੜੀਂਦੀਆਂ"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"ਖਬਰਾਂ ਅਤੇ ਰਸਾਲੇ"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"ਤੁਹਾਡੇ ਲਈ ਸਕੂਨਮਈ ਖੇਤਰ"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"ਮਨੋਰੰਜਨ"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"ਸੋਸ਼ਲ"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"ਸਿਹਤ ਅਤੇ ਫਿੱਟਨੈੱਸ"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"ਮੌਸਮ"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"ਤੁਹਾਡੇ ਲਈ ਸੁਝਾਅ"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ਵਿਜੇਟ ਸੱਜੇ ਪਾਸੇ ਹਨ, ਖੋਜ ਵਿਜੇਟ ਅਤੇ ਹੋਰ ਵਿਕਲਪ ਖੱਬੇ ਪਾਸੇ ਹਨ"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ਵਿਜੇਟ}one{# ਵਿਜੇਟ}other{# ਵਿਜੇਟ}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"ਨਿੱਜੀ, ਲਾਕ ਕੀਤਾ ਗਿਆ।"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"ਲਾਕ ਕਰੋ"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"ਨਿੱਜੀ ਸਪੇਸ ਨੂੰ ਤਬਦੀਲ ਕਰਨਾ"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"ਐਪਾਂ ਸਥਾਪਤ ਕਰੋ"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ ਵਿੱਚ ਐਪਾਂ ਸਥਾਪਤ ਕਰੋ"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ਓਵਰਫ਼ਲੋ"</string>
 </resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index b6af6b3..1d51c8e 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -44,13 +44,10 @@
     <string name="add_to_home_screen" msgid="9168649446635919791">"Dodaj do ekranu głównego"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widżet <xliff:g id="WIDGET_NAME">%1$s</xliff:g> został dodany do ekranu głównego"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Sugestie"</string>
-    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Niezbędne"</string>
+    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Najbardziej przydatne"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Wiadomości i czasopisma"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Strefa relaksu"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Rozrywka"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Społecznościowe"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Zdrowie i fitness"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Pogoda"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Proponowane dla Ciebie"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widżety (<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>) po prawej, wyszukiwanie i opcje po lewej"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widżet}few{# widżety}many{# widżetów}other{# widżetu}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Prywatna, zablokowana."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Zablokuj"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Przenoszenie obszaru prywatnego"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Instaluj aplikacje"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Zainstaluj aplikacje w przestrzeni prywatnej"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Rozwiń menu"</string>
 </resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index e07d8cc..4ec41d9 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Sugestões"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essenciais"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Notícias e revistas"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"A sua zona de relaxamento"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entretenimento"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Redes sociais"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Saúde e fitness"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Meteorologia"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sugestões para si"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgets de <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> à direita, pesquisa e opções à esquerda"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privado, bloqueado."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Bloquear"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Transição do espaço privado"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Instalar apps"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Instale apps no espaço privado"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Menu adicional"</string>
 </resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 3b2aeec..386bcd0 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Sugestões"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essenciais"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Notícias e revistas"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Sua zona de relaxamento"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entretenimento"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Saúde e bem-estar"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Clima"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sugestões para você"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgets da <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> à direita, pesquisa e opções à esquerda"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}other{# widgets}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privada, bloqueado."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Bloquear"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Espaço particular em transição"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Instalar apps"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Instalar apps no espaço privado"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Balão flutuante"</string>
 </resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index d4c303e..86497ed 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Sugestii"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Esențiale"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Știri și reviste"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zona de relaxare"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Divertisment"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Rețele sociale"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Sănătate și fitness"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Meteo"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sugerate pentru tine"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgeturi pentru <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> în dreapta, căutare și opțiuni în stânga"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}few{# widgeturi}other{# de widgeturi}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privat, blocat."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Blochează"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Tranziție pentru spațiul privat"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Instalează aplicații"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Instalează aplicații în Spațiul privat"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Suplimentar"</string>
 </resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index a7e2666..48bdf7b 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Подсказки"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Основное"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Новости и журналы"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Развлечение и общение"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Развлечения"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Общение"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Здоровье и спорт"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Погода"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Рекомендации"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Виджеты приложения \"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>\" находятся справа, а панель поиска и настройки – слева"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# виджет}one{# виджет}few{# виджета}many{# виджетов}other{# виджета}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Личное, заблокировано."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Блокировка"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Переход к личному пространству"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Установить приложения"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Установить приложения в личном пространстве"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Дополнительное меню"</string>
 </resources>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index fce3499..6ae5a84 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"යෝජනා"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"අත්‍යවශ්‍යාංග"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"පුවත් සහ සඟරා"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"ඔබේ නිවුණු කලාපය"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"විනෝදාස්වාදය"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"සමාජයීය"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"සෞඛ්‍යය සහ යෝග්‍යතාව"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"කාලගුණ"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"ඔබ සඳහා යෝජිත"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"දකුණේ <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> විජට්, වමේ සෙවීම සහ විකල්ප"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{විජට් #}one{විජට් #}other{විජට් #}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"පුද්ගලික, අගුලු දමන ලදි."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"අගුළු දමන්න"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"පෞද්ගලික අවකාශ සංක්‍රමණය"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"යෙදුම් ස්ථාපනය කරන්න"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"පෞද්ගලික අවකාශයට යෙදුම් ස්ථාපනය කරන්න"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"පිටාර යාම"</string>
 </resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index a93c332..604b6a5 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Návrhy"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Všetko dôležité"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Noviny a časopisy"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Vaša komfortná zóna"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Zábava"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sociálne siete"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Zdravie a kondícia"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Počasie"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Navrhnuté pre vás"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Miniaplikácie <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> vpravo, vyhľadávanie a možnosti vľavo"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# miniaplikácia}few{# miniaplikácie}many{# widgets}other{# miniaplikácií}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Súkromné, uzamknuté."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Uzamknúť"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Prechod súkromného priestoru"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Inštalovať aplikácie"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Inštalácia aplikácií v súkromnom priestore"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Rozšírená ponuka"</string>
 </resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 26fed22..1f5a83f 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Predlogi"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Osnove"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Novice in revije"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Vaš kotiček za sprostitev"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Razvedrilo"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Družbeno"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Zdravje in fitnes"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Vreme"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Predlagano za vas"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Pripomočki <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> na desni, iskanje in možnosti na levi"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# pripomoček}one{# pripomoček}two{# pripomočka}few{# pripomočki}other{# pripomočkov}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Zasebno, zaklenjeno."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Zaklepanje"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Preklapljanje zasebnega prostora"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Nameščanje aplikacij"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Nameščanje aplikacij v zasebni prostor"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Oblaček z dodatnimi elementi"</string>
 </resources>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 95c0782..57c4b69 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Sugjerime"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Thelbësoret"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Lajme dhe revista"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zona jote e qetësisë"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Argëtim"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Rrjetet sociale"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Shëndet dhe fitnes"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Moti"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sugjeruar për ty"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Miniaplikacionet e <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> në të djathtë, kërkimi dhe opsionet në të majtë"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# miniaplikacion}other{# miniaplikacione}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Private, e kyçur."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Kyç"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Kalimi te \"Hapësira private\""</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Instalo aplikacionet"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Instalo aplikacionet në hapësirën private"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Tejkalimi"</string>
 </resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index bb82b67..10ea212 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -44,13 +44,10 @@
     <string name="add_to_home_screen" msgid="9168649446635919791">"Додај на почетни екран"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Додали сте виџет <xliff:g id="WIDGET_NAME">%1$s</xliff:g> на почетни екран"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Предлози"</string>
-    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Неопходне апликације"</string>
+    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Основно"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Новости и часописи"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Зона за опуштање"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Забава"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Друштвене мреже"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Здравље и фитнес"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Време"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Предложено за вас"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Виџети <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> са десне стране, претрага и опције са леве стране"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# виџет}one{# виџет}few{# виџета}other{# виџета}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Приватно, закључано."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Закључавање"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Пренос приватног простора"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Инсталирајте апликације"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Инсталирај апликације у приватан простор"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Преклопно"</string>
 </resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 5d13f13..555600d 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Förslag"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Viktigt"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Nyheter och tidskrifter"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Koppla av"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Underhållning"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Socialt"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Hälsa och träning"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Väder"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Våra förslag"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgetar för <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> till höger, sökning och alternativ till vänster"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgetar}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Privat, låst."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Lås"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Överföring av privat rum"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Installera appar"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Installera appar i privat rum"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Fler alternativ"</string>
 </resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 830b56d..1f9690b 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -44,13 +44,10 @@
     <string name="add_to_home_screen" msgid="9168649446635919791">"Weka kwenye skrini ya kwanza"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Umeongeza wijeti ya <xliff:g id="WIDGET_NAME">%1$s</xliff:g> kwenye skrini ya kwanza"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Mapendekezo"</string>
-    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essentials"</string>
+    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Vya msingi"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Habari na magazeti"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Mahali Pako pa Kupumzika"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Burudani"</string>
-    <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Mitandao jamii"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Afya na siha"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Hali ya Hewa"</string>
+    <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Mitandao ya kijamii"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Unayopendekezewa"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Wijeti za <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ziko upande wa kulia, utafutaji na chaguo ziko upande wa kushoto"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{Wijeti #}other{Wijeti #}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Ya faragha, imefungwa."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Funga"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Mabadiliko ya Nafasi ya Faragha"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Sakinisha programu"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Sakinisha programu kwenye Sehemu ya Faragha"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Menyu ya vipengee vya ziada"</string>
 </resources>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 061cfb3..88aab6e 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"பரிந்துரைகள்"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"அத்தியாவசியமானவை"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"செய்திகள் &amp; பத்திரிக்கைகள்"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"உங்கள் மனதுக்கு இதமானவை"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"பொழுதுபோக்கு"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"சமூகம்"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"ஆரோக்கியம் &amp; உடற்பயிற்சி"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"வானிலை"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"உங்களுக்கான பரிந்துரைகள்"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> விட்ஜெட்கள் வலதுபுறத்தில் உள்ளன, தேடல் மற்றும் விருப்பங்கள் இடதுபுறத்தில் உள்ளன"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# விட்ஜெட்}other{# விட்ஜெட்டுகள்}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"தனிப்பட்டது, லாக் செய்யப்பட்டுள்ளது."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"பூட்டு"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"தனிப்பட்ட சேமிப்பிடத்திற்கு மாற்றுகிறது"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"ஆப்ஸை நிறுவுதல்"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"தனிப்பட்ட சேமிப்பிடத்தில் ஆப்ஸை நிறுவும்"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"கூடுதல் விருப்பங்களைக் காட்டும்"</string>
 </resources>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index d3aea7b..29916c9 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"సూచనలు"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"నిత్యావసరాలు"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"వార్తలు &amp; మ్యాగజైన్లు"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"మీరు ప్రశాంతంగా ఉండే ప్రదేశం"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"వినోదం"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"సామాజికం"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"ఆరోగ్యం &amp; ఫిట్‌నెస్"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"వాతావరణం"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"మీ కోసం సూచించినవి"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"కుడి వైపున <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> విడ్జెట్‌లు, ఎడమ వైపున సెర్చ్, ఇతర ఆప్షన్‌లు"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# విడ్జెట్}other{# విడ్జెట్‌లు}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"ప్రైవేట్, లాక్ చేయబడింది."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"లాక్ చేయండి"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"ప్రైవేట్ స్పేస్ కేటాయించడం జరుగుతుంది"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"యాప్‌లను ఇన్‌స్టాల్ చేయండి"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"ప్రైవేట్ స్పేస్‌కు యాప్‌లను ఇన్‌స్టాల్ చేయండి"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ఓవర్‌ఫ్లో"</string>
 </resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 1a7c6d1..0ae1abe 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"คำแนะนำ"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"รายการที่ห้ามพลาด"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"ข่าวสารและนิตยสาร"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"พื้นที่สบายๆ ของคุณ"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"ความบันเทิง"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"โซเชียล"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"สุขภาพและการออกกำลังกาย"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"สภาพอากาศ"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"แนะนำให้คุณ"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"วิดเจ็ต<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>ทางด้านขวา การค้นหาและตัวเลือกทางด้านซ้าย"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{วิดเจ็ต # รายการ}other{วิดเจ็ต # รายการ}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"ส่วนตัว ล็อกอยู่"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"ล็อก"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"การเปลี่ยนไปใช้พื้นที่ส่วนตัว"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"ติดตั้งแอป"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"ติดตั้งแอปไปยังพื้นที่ส่วนตัว"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"การดำเนินการเพิ่มเติม"</string>
 </resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 87b89e3..b94f0c1 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Mga Suhestyon"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Mga essential"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Balita at mga magazine"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Ang Iyong Chill Zone"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entertainment"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Kalusugan at fitness"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Lagay ng panahon"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Iminumungkahi para sa iyo"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Mga widget ng <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> sa kanan, paghahanap at mga opsyon sa kaliwa"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}other{# na widget}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Pribado, naka-lock."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"I-lock"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Pag-transition ng Pribadong Space"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Mag-install ng mga app"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Mag-install ng mga app sa Pribadong Space"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Overflow"</string>
 </resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 5440378..df4afbd 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Öneriler"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Önemliler"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Haberler ve dergiler"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Huzur alanınız"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Eğlence"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sosyal"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Sağlık ve fitness"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Hava durumu"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sizin için önerilenler"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> widget\'ları sağda, arama ve seçenekler solda"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widget}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Gizli, kilitli."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Kilit"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Gizli Alana Geçiş"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Uygulamaları yükleme"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Uygulamaları özel alana yükleyin"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Taşma"</string>
 </resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 50c8f01..692e73e 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Пропозиції"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Основне"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Новини й журнали"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Ваша зона розваг"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Розваги"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Соціальні мережі"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Здоров’я і фітнес"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Погода"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Пропозиції для вас"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>: віджети праворуч, пошук і опції ліворуч"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# віджет}one{# віджет}few{# віджети}many{# віджетів}other{# віджета}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Приватний простір, заблоковано."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Заблокувати"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Перехід у приватний простір"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Установити додатки"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Установити додатки в особистому просторі"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Додаткове меню"</string>
 </resources>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 9cd3e07..7be222e 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"تجاویز"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"لوازمات"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"خبریں اور میگزینز"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"آپ کا آرام دہ زون"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"تفریح"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"سماجی"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"صحت اور تندرستی"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"موسم"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"آپ کے لیے تجویز کردہ"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> دائیں طرف وجیٹس، بائیں طرف تلاش اور اختیارات"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ویجیٹ}other{# ویجیٹس}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"نجی اسپیس مقفل ہے۔"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"مقفل کریں"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"نجی اسپیس کی منتقلی"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"ایپس انسٹال کریں"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"پرائیویٹ اسپیس میں ایپس انسٹال کریں"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"اوورفلو"</string>
 </resources>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 36df592..ae9d0f9 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Takliflar"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Asosiy ilovalar"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Yangiliklar va jurnallar"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Sokin hududingiz"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Hordiq"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Ijtimoiy"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Salomatlik va sport"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Ob-havo"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sizga tavsiya etiladi"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> vidjetlari oʻngda, qidiruv va sozlamalar chapda"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ta vidjet}other{# ta vidjet}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Shaxsiy, qulflandi."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Qulflash"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Maxfiy joyga almashtirish"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Ilovalar oʻrnatish"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Ilovalarni Maxfiy makonga oʻrnatish"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Kengaytirish"</string>
 </resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 01c7736..24d9c8e 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Nội dung đề xuất"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Các tiện ích thiết yếu"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Tin tức và tạp chí"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Giai điệu thư giãn của bạn"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Giải trí"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Mạng xã hội"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Sức khoẻ và thể chất"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Thời tiết"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Đề xuất cho bạn"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Tiện ích <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ở bên phải, công cụ tìm kiếm và tuỳ chọn ở bên trái"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# tiện ích}other{# tiện ích}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Riêng tư, đã khoá."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Khoá"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Chuyển đổi sang không gian riêng tư"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Cài đặt ứng dụng"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Cài đặt ứng dụng vào Không gian riêng tư"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Bong bóng bổ sung"</string>
 </resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 2975e97..bf6252a 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -44,13 +44,10 @@
     <string name="add_to_home_screen" msgid="9168649446635919791">"添加到主屏幕"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"已将“<xliff:g id="WIDGET_NAME">%1$s</xliff:g>”微件添加到主屏幕"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"建议"</string>
-    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"必备"</string>
+    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"必备之选"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"新闻与杂志"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"您的休闲区"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"娱乐"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"社交"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"健康与健身"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"天气"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"为您推荐"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"右边是<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>微件,左边是搜索功能和选项"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# 个微件}other{# 个微件}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"私密,已锁定。"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"锁定"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"私密空间转换"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"安装应用"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"将应用安装到私密空间"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"菜单"</string>
 </resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index e2adcc9..70e27c6 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"建議"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"必備之選"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"新聞和雜誌"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"放鬆專區"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"娛樂"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"社交"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"健康和健身"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"天氣"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"為你推薦"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"右邊係「<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>」小工具,左邊係搜尋功能同選項"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# 個小工具}other{# 個小工具}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"私人,已鎖定。"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"上鎖"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"轉為「私人空間」"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"安裝應用程式"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"將應用程式安裝在「私人空間」中"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"顯示更多"</string>
 </resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index f5eafe4..c14636a 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"建議"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"常用項目"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"新聞與雜誌"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"放鬆專區"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"娛樂"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"社群"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"健康與塑身"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"天氣"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"個人化建議"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"右邊是「<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>」小工具,左邊是搜尋功能和選項"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# 項小工具}other{# 項小工具}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"私人,已鎖定。"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"鎖定"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"轉換私人空間狀態"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"安裝應用程式"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"將應用程式安裝在私人空間中"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"溢位"</string>
 </resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 6de2436..449d2a2 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -46,11 +46,8 @@
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Iziphakamiso"</string>
     <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Okusemqoka"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Izindaba nomagazini"</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Indawo Ozipholela Kuyo"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Okokozijabulisa"</string>
     <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Okomphakathi"</string>
-    <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Ezempilo nokufaneleka"</string>
-    <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Isimo sezulu"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Okuphakanyiselwe wena"</string>
     <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Amawijethi okuthi <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> kwesokudla, ukusesha nokukhethwayo kwesobunxele"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{iwijethi #}one{amawijethi #}other{amawijethi #}}"</string>
@@ -193,7 +190,8 @@
     <string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Iyimfihlo, ikhiyiwe."</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Khiya"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Ukuguqulwa Kwendawo Yangasese"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Faka ama-app"</string>
+    <!-- no translation found for ps_add_button_label (8127988716897128773) -->
+    <skip />
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Faka ama-app Endaweni Engasese"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Ukugcwala kakhulu"</string>
 </resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 648a50c..2a3b588 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -103,8 +103,8 @@
     <item name="swipe_up_rect_scale_stiffness" type="dimen" format="float">200</item>
     <item name="swipe_up_rect_scale_higher_stiffness" type="dimen" format="float">400</item>
     <!-- Flag: enableScalingRevealHomeAnimation() -->
-    <item name="swipe_up_rect_scale_damping_ratio_v2" type="dimen" format="float">0.8</item>
-    <item name="swipe_up_rect_scale_stiffness_v2" type="dimen" format="float">650</item>
+    <item name="swipe_up_rect_scale_damping_ratio_v2" type="dimen" format="float">0.99</item>
+    <item name="swipe_up_rect_scale_stiffness_v2" type="dimen" format="float">500</item>
 
     <item name="swipe_up_rect_xy_fling_friction" type="dimen" format="float">1.5</item>
 
@@ -114,9 +114,9 @@
     <item name="swipe_up_rect_xy_stiffness" type="dimen" format="float">200</item>
     <!-- Flag: enableScalingRevealHomeAnimation() -->
     <item name="swipe_up_rect_x_damping_ratio" type="dimen" format="float">0.965</item>
-    <item name="swipe_up_rect_x_stiffness" type="dimen" format="float">300</item>
+    <item name="swipe_up_rect_x_stiffness" type="dimen" format="float">450</item>
     <item name="swipe_up_rect_y_damping_ratio" type="dimen" format="float">0.95</item>
-    <item name="swipe_up_rect_y_stiffness" type="dimen" format="float">190</item>
+    <item name="swipe_up_rect_y_stiffness" type="dimen" format="float">400</item>
 
     <!-- Taskbar -->
     <!-- This is a float because it is converted to dp later in DeviceProfile -->
@@ -155,11 +155,6 @@
 
     <string-array name="filtered_components" ></string-array>
 
-    <!-- Widget component names to be included in weather category of widget suggestions. -->
-    <string-array name="weather_recommendations"></string-array>
-    <!-- Widget component names to be included in fitness category of widget suggestions. -->
-    <string-array name="fitness_recommendations"></string-array>
-
     <!-- Swipe back to home related -->
     <dimen name="swipe_back_window_scale_x_margin">10dp</dimen>
     <dimen name="swipe_back_window_corner_radius">40dp</dimen>
@@ -223,4 +218,11 @@
     <string-array name="skip_private_profile_shortcut_packages" translatable="false">
         <item>com.android.settings</item>
     </string-array>
+
+    <!-- Legacy list of components supporting multiple instances.
+         DO NOT ADD TO THIS LIST.  Apps should use the PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI
+         property to declare multi-instance support in V+. This resource should match the resource
+         of the same name in SystemUI. -->
+    <string-array name="config_appsSupportMultiInstancesSplit">
+    </string-array>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 4e5ef12..b4f8a47 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -167,7 +167,6 @@
     <!-- (x) icon button inside work edu card -->
     <dimen name="rounded_button_width">24dp</dimen>
     <dimen name="x_icon_size">16dp</dimen>
-    <dimen name="x_icon_padding">4dp</dimen>
 
     <!-- rounded button shown inside card views, and snack bars  -->
     <dimen name="padded_rounded_button_height">48dp</dimen>
@@ -516,7 +515,7 @@
     <dimen name="default_ime_height">300dp</dimen>
 
     <!-- Private Space parameters -->
-    <dimen name="ps_container_corner_radius">24dp</dimen>
+    <dimen name="ps_container_corner_radius">28dp</dimen>
     <dimen name="ps_header_height">72dp</dimen>
     <dimen name="ps_header_relative_layout_height">48dp</dimen>
     <dimen name="ps_header_image_height">48dp</dimen>
@@ -536,6 +535,8 @@
     <dimen name="ps_lock_icon_text_margin_start_expanded">8dp</dimen>
     <dimen name="ps_lock_icon_text_margin_end_expanded">6dp</dimen>
     <dimen name="ps_lock_button_background_padding">10dp</dimen>
+    <dimen name="ps_floating_mask_corner_radius">28dp</dimen>
+    <dimen name="ps_floating_mask_end_padding">16dp</dimen>
 
     <!-- WindowManagerProxy -->
     <dimen name="max_width_and_height_of_small_display_cutout">136px</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c971223..02e84e0 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -83,13 +83,20 @@
     <!-- Widget suggestions header title in the full widgets picker for large screen devices
     in landscape mode. [CHAR_LIMIT=50] -->
     <string name="suggested_widgets_header_title">Suggestions</string>
+    <!-- Title for the widget suggestions category that displays widgets provided by
+    productivity apps for daily use [CHAR_LIMIT=50] -->
     <string name="productivity_widget_recommendation_category_label">Essentials</string>
+    <!-- Title for the widget suggestions category that displays widgets provided by
+    news and magazines related apps [CHAR_LIMIT=50] -->
     <string name="news_widget_recommendation_category_label">News &amp; magazines</string>
-    <string name="social_and_entertainment_widget_recommendation_category_label">Your Chill Zone</string>
+    <!-- Title for the widget suggestions category that displays widgets provided by
+    entertainment apps [CHAR_LIMIT=50] -->
     <string name="entertainment_widget_recommendation_category_label">Entertainment</string>
+    <!-- Title for the widget suggestions category that displays widgets provided by
+    social apps [CHAR_LIMIT=50] -->
     <string name="social_widget_recommendation_category_label">Social</string>
-    <string name="fitness_widget_recommendation_category_label">Health &amp; fitness</string>
-    <string name="weather_widget_recommendation_category_label">Weather</string>
+    <!-- Title for the widget suggestions category that displays general widget suggestions
+    [CHAR_LIMIT=50] -->
     <string name="others_widget_recommendation_category_label">Suggested for you</string>
     <!-- accessibilityPaneTitle for the right pane when showing suggested widgets. -->
     <string name="widget_picker_right_pane_accessibility_title"><xliff:g id="selected_header" example="Calendar">%1$s</xliff:g> widgets on right, search and options on left</string>
@@ -333,7 +340,7 @@
     <!-- Title for an app whose download has been started. -->
     <string name="app_waiting_download_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> waiting to install</string>
     <!-- Title for an app which is archived. -->
-    <string name="app_archived_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> is archived. Tap to download.</string>
+    <string name="app_archived_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> is archived. Tap to download and restore.</string>
 
 
     <!-- Title shown on the alert dialog prompting the user to update the application in market
@@ -485,7 +492,7 @@
     <!-- Description for Private Space Transition button -->
     <string name="ps_container_transition">Private Space Transitioning</string>
     <!-- Title for Private Space install app icon -->
-    <string name="ps_add_button_label">Install apps</string>
+    <string name="ps_add_button_label">Install</string>
     <!-- Content description for install app icon -->
     <string name="ps_add_button_content_description">Install apps to Private Space</string>
 
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index cf86528..175d6ec 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -19,6 +19,8 @@
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
 import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
 import static com.android.launcher3.provider.LauncherDbUtils.itemIdMatch;
+import static com.android.launcher3.util.UserIconInfo.TYPE_CLONED;
+import static com.android.launcher3.util.UserIconInfo.TYPE_WORK;
 
 import android.content.ComponentName;
 import android.content.ContentValues;
@@ -34,6 +36,7 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.os.Bundle;
 import android.os.Process;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
@@ -56,6 +59,7 @@
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.Partner;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.UserIconInfo;
 import com.android.launcher3.widget.LauncherWidgetHolder;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -63,6 +67,7 @@
 
 import java.io.IOException;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
 import java.util.function.Supplier;
@@ -125,34 +130,38 @@
     private static final String TAG_INCLUDE = "include";
     public static final String TAG_WORKSPACE = "workspace";
     private static final String TAG_APP_ICON = "appicon";
-    private static final String TAG_AUTO_INSTALL = "autoinstall";
-    private static final String TAG_FOLDER = "folder";
-    private static final String TAG_APPWIDGET = "appwidget";
+    public static final String TAG_AUTO_INSTALL = "autoinstall";
+    public static final String TAG_FOLDER = "folder";
+    public static final String TAG_APPWIDGET = "appwidget";
     protected static final String TAG_SEARCH_WIDGET = "searchwidget";
-    private static final String TAG_SHORTCUT = "shortcut";
+    public static final String TAG_SHORTCUT = "shortcut";
     private static final String TAG_EXTRA = "extra";
 
-    private static final String ATTR_CONTAINER = "container";
-    private static final String ATTR_RANK = "rank";
+    public static final String ATTR_CONTAINER = "container";
+    public static final String ATTR_RANK = "rank";
 
-    private static final String ATTR_PACKAGE_NAME = "packageName";
-    private static final String ATTR_CLASS_NAME = "className";
-    private static final String ATTR_TITLE = "title";
-    private static final String ATTR_TITLE_TEXT = "titleText";
-    private static final String ATTR_SCREEN = "screen";
-    private static final String ATTR_SHORTCUT_ID = "shortcutId";
+    public static final String ATTR_PACKAGE_NAME = "packageName";
+    public static final String ATTR_CLASS_NAME = "className";
+    public static final String ATTR_TITLE = "title";
+    public static final String ATTR_TITLE_TEXT = "titleText";
+    public static final String ATTR_SCREEN = "screen";
+    public static final String ATTR_SHORTCUT_ID = "shortcutId";
 
     // x and y can be specified as negative integers, in which case -1 represents the
     // last row / column, -2 represents the second last, and so on.
-    private static final String ATTR_X = "x";
-    private static final String ATTR_Y = "y";
+    public static final String ATTR_X = "x";
+    public static final String ATTR_Y = "y";
 
-    private static final String ATTR_SPAN_X = "spanX";
-    private static final String ATTR_SPAN_Y = "spanY";
+    public static final String ATTR_SPAN_X = "spanX";
+    public static final String ATTR_SPAN_Y = "spanY";
 
     // Attrs for "Include"
     private static final String ATTR_WORKSPACE = "workspace";
 
+    public static final String ATTR_USER_TYPE = "userType";
+    public static final String USER_TYPE_WORK = "work";
+    public static final String USER_TYPE_CLONED = "cloned";
+
     // Style attrs -- "Extra"
     private static final String ATTR_KEY = "key";
     private static final String ATTR_VALUE = "value";
@@ -168,6 +177,8 @@
     protected final SourceResources mSourceRes;
     protected final Supplier<XmlPullParser> mInitialLayoutSupplier;
 
+    private final Map<String, Long> mUserTypeToSerial;
+
     private final InvariantDeviceProfile mIdp;
     private final int mRowCount;
     private final int mColumnCount;
@@ -204,15 +215,25 @@
         mRowCount = mIdp.numRows;
         mColumnCount = mIdp.numColumns;
         mActivityOverride = ApiWrapper.INSTANCE.get(context).getActivityOverrides();
+
+        mUserTypeToSerial = new HashMap<>();
+        UserCache cache = UserCache.getInstance(context);
+        for (UserHandle user : cache.getUserProfiles()) {
+            UserIconInfo uii = cache.getUserInfo(user);
+            switch (uii.type) {
+                case TYPE_WORK -> mUserTypeToSerial.put(USER_TYPE_WORK, uii.userSerial);
+                case TYPE_CLONED -> mUserTypeToSerial.put(USER_TYPE_CLONED, uii.userSerial);
+            }
+        }
     }
 
     /**
      * Loads the layout in the db and returns the number of entries added on the desktop.
      */
-    public int loadLayout(SQLiteDatabase db, IntArray screenIds) {
+    public int loadLayout(SQLiteDatabase db) {
         mDb = db;
         try {
-            return parseLayout(mInitialLayoutSupplier.get(), screenIds);
+            return parseLayout(mInitialLayoutSupplier.get());
         } catch (Exception e) {
             Log.e(TAG, "Error parsing layout: ", e);
             return -1;
@@ -222,7 +243,7 @@
     /**
      * Parses the layout and returns the number of elements added on the homescreen.
      */
-    protected int parseLayout(XmlPullParser parser, IntArray screenIds)
+    protected int parseLayout(XmlPullParser parser)
             throws XmlPullParserException, IOException {
         beginDocument(parser, mRootTag);
         final int depth = parser.getDepth();
@@ -235,7 +256,7 @@
             if (type != XmlPullParser.START_TAG) {
                 continue;
             }
-            count += parseAndAddNode(parser, tagParserMap, screenIds);
+            count += parseAndAddNode(parser, tagParserMap);
         }
         return count;
     }
@@ -259,14 +280,14 @@
      * Parses the current node and returns the number of elements added.
      */
     protected int parseAndAddNode(
-            XmlPullParser parser, ArrayMap<String, TagParser> tagParserMap, IntArray screenIds)
+            XmlPullParser parser, ArrayMap<String, TagParser> tagParserMap)
             throws XmlPullParserException, IOException {
 
         if (TAG_INCLUDE.equals(parser.getName())) {
             final int resId = getAttributeResourceValue(parser, ATTR_WORKSPACE, 0);
             if (resId != 0) {
                 // recursively load some more favorites, why not?
-                return parseLayout(mSourceRes.getXml(resId), screenIds);
+                return parseLayout(mSourceRes.getXml(resId));
             } else {
                 return 0;
             }
@@ -284,22 +305,17 @@
                 convertToDistanceFromEnd(getAttributeValue(parser, ATTR_X), mColumnCount));
         mValues.put(Favorites.CELLY,
                 convertToDistanceFromEnd(getAttributeValue(parser, ATTR_Y), mRowCount));
+        Long profileId = mUserTypeToSerial.get(getAttributeValue(parser, ATTR_USER_TYPE));
+        if (profileId != null) {
+            mValues.put(Favorites.PROFILE_ID, profileId);
+        }
 
         TagParser tagParser = tagParserMap.get(parser.getName());
         if (tagParser == null) {
             if (LOGD) Log.d(TAG, "Ignoring unknown element tag: " + parser.getName());
             return 0;
         }
-        int newElementId = tagParser.parseAndAdd(parser);
-        if (newElementId >= 0) {
-            // Keep track of the set of screens which need to be added to the db.
-            if (!screenIds.contains(screenId) &&
-                    container == Favorites.CONTAINER_DESKTOP) {
-                screenIds.add(screenId);
-            }
-            return 1;
-        }
-        return 0;
+        return tagParser.parseAndAdd(parser) >= 0 ? 1 : 0;
     }
 
     protected int addShortcut(String title, Intent intent, int type) {
@@ -311,10 +327,11 @@
         mValues.put(Favorites.SPANY, 1);
         mValues.put(Favorites._ID, id);
 
-        if (type == ITEM_TYPE_APPLICATION) {
-            ComponentName cn = intent.getComponent();
-            if (cn != null && mActivityOverride.containsKey(cn.getPackageName())) {
-                LauncherActivityInfo replacementInfo = mActivityOverride.get(cn.getPackageName());
+        ComponentName cn = intent.getComponent();
+        if (cn != null && type == ITEM_TYPE_APPLICATION
+                && !mValues.containsKey(Favorites.PROFILE_ID)) {
+            LauncherActivityInfo replacementInfo = mActivityOverride.get(cn.getPackageName());
+            if (replacementInfo != null) {
                 mValues.put(Favorites.PROFILE_ID, UserCache.INSTANCE.get(mContext)
                         .getSerialNumberForUser(replacementInfo.getUser()));
                 mValues.put(Favorites.INTENT, AppInfo.makeLaunchIntent(replacementInfo).toUri(0));
@@ -420,11 +437,7 @@
             }
 
             mValues.put(Favorites.RESTORED, WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON);
-            final Intent intent = new Intent(Intent.ACTION_MAIN, null)
-                    .addCategory(Intent.CATEGORY_LAUNCHER)
-                    .setComponent(new ComponentName(packageName, className))
-                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                            | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+            Intent intent = AppInfo.makeLaunchIntent(new ComponentName(packageName, className));
             return addShortcut(mContext.getString(R.string.package_state_unknown), intent,
                     ITEM_TYPE_APPLICATION);
         }
diff --git a/src/com/android/launcher3/DevicePaddings.java b/src/com/android/launcher3/DevicePaddings.java
index 08fb47b..8494d11 100644
--- a/src/com/android/launcher3/DevicePaddings.java
+++ b/src/com/android/launcher3/DevicePaddings.java
@@ -47,7 +47,7 @@
     private static final String WORKSPACE_BOTTOM_PADDING = "workspaceBottomPadding";
     private static final String HOTSEAT_BOTTOM_PADDING = "hotseatBottomPadding";
 
-    private static final String TAG = DevicePaddings.class.getSimpleName();
+    private static final String TAG = "DevicePaddings";
     private static final boolean DEBUG = false;
 
     ArrayList<DevicePadding> mDevicePaddings = new ArrayList<>();
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 009d709..b89d05e 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -96,7 +96,9 @@
 import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
 import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
 import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
+import static com.android.launcher3.testing.shared.TestProtocol.CLOCK_ICON_DRAWABLE_LEAKING;
 import static com.android.launcher3.testing.shared.TestProtocol.LAUNCHER_ACTIVITY_STOPPED_MESSAGE;
+import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.ItemInfoMatcher.forFolderMatch;
 import static com.android.launcher3.util.SettingsCache.TOUCHPAD_NATURAL_SCROLLING;
@@ -421,6 +423,7 @@
     @Override
     @TargetApi(Build.VERSION_CODES.S)
     protected void onCreate(Bundle savedInstanceState) {
+        testLogD(CLOCK_ICON_DRAWABLE_LEAKING, "onCreate: instance=" + this);
         mStartupLatencyLogger = createStartupLatencyLogger(
                 sIsNewProcess
                         ? LockedUserState.get(this).isUserUnlockedAtLauncherStartup()
@@ -1079,6 +1082,7 @@
 
     @Override
     protected void onStart() {
+        testLogD(CLOCK_ICON_DRAWABLE_LEAKING, "onStart: instance=" + this);
         TraceHelper.INSTANCE.beginSection(ON_START_EVT);
         super.onStart();
         if (!mDeferOverlayCallbacks) {
@@ -1092,6 +1096,7 @@
     @Override
     @CallSuper
     protected void onDeferredResumed() {
+        testLogD(CLOCK_ICON_DRAWABLE_LEAKING, "onDeferredResumed: instance=" + this);
         logStopAndResume(true /* isResume */);
 
         // Process any items that were added while Launcher was away.
@@ -1279,6 +1284,7 @@
 
     @Override
     protected void onResume() {
+        testLogD(CLOCK_ICON_DRAWABLE_LEAKING, "onResume: instance=" + this);
         TraceHelper.INSTANCE.beginSection(ON_RESUME_EVT);
         super.onResume();
 
@@ -1294,6 +1300,7 @@
 
     @Override
     protected void onPause() {
+        testLogD(CLOCK_ICON_DRAWABLE_LEAKING, "onPause: instance=" + this);
         // Ensure that items added to Launcher are queued until Launcher returns
         ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_ACTIVITY_PAUSED);
 
@@ -1776,6 +1783,7 @@
 
     @Override
     public void onDestroy() {
+        testLogD(CLOCK_ICON_DRAWABLE_LEAKING, "onDestroy: instance=" + this);
         super.onDestroy();
         ACTIVITY_TRACKER.onActivityDestroyed(this);
 
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 03de334..a4ae1c8 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -52,6 +52,7 @@
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.util.LockedUserState;
 import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.SafeCloseable;
@@ -181,7 +182,7 @@
         mIconCache = new IconCache(mContext, mInvariantDeviceProfile,
                 iconCacheFileName, mIconProvider);
         mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext),
-                iconCacheFileName != null);
+                PackageManagerHelper.INSTANCE.get(context), iconCacheFileName != null);
         mOnTerminateCallback.add(mIconCache::close);
         mOnTerminateCallback.add(mModel::destroy);
     }
@@ -206,7 +207,7 @@
     }
 
     private void refreshAndReloadLauncher() {
-        LauncherIcons.clearPool();
+        LauncherIcons.clearPool(mContext);
         mIconCache.updateIconParams(
                 mInvariantDeviceProfile.fillResIconDpi, mInvariantDeviceProfile.iconBitmapSize);
         mModel.forceReload();
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index be98589..47a7115 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -19,6 +19,7 @@
 import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED;
 
 import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD;
+import static com.android.launcher3.LauncherPrefs.WORK_EDU_STEP;
 import static com.android.launcher3.config.FeatureFlags.IS_STUDIO_BUILD;
 import static com.android.launcher3.icons.cache.BaseIconCache.EMPTY_CLASS_NAME;
 import static com.android.launcher3.model.PackageUpdatedTask.OP_UPDATE;
@@ -98,6 +99,8 @@
     @NonNull
     private final LauncherAppState mApp;
     @NonNull
+    private final PackageManagerHelper mPmHelper;
+    @NonNull
     private final ModelDbController mModelDbController;
     @NonNull
     private final Object mLock = new Object();
@@ -152,12 +155,13 @@
 
     LauncherModel(@NonNull final Context context, @NonNull final LauncherAppState app,
             @NonNull final IconCache iconCache, @NonNull final AppFilter appFilter,
-            final boolean isPrimaryInstance) {
+            @NonNull final PackageManagerHelper pmHelper, final boolean isPrimaryInstance) {
         mApp = app;
+        mPmHelper = pmHelper;
         mModelDbController = new ModelDbController(context);
         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
-        mModelDelegate = ModelDelegate.newInstance(context, app, mBgAllAppsList, mBgDataModel,
-                isPrimaryInstance);
+        mModelDelegate = ModelDelegate.newInstance(context, app, mPmHelper, mBgAllAppsList,
+                mBgDataModel, isPrimaryInstance);
     }
 
     @NonNull
@@ -273,6 +277,9 @@
             enqueueModelUpdateTask(new PackageUpdatedTask(
                     PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user));
         }
+        if (Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
+            LauncherPrefs.get(mApp.getContext()).put(WORK_EDU_STEP, 0);
+        }
     }
 
     /**
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 3bdd863..72a3c53 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -83,7 +83,7 @@
     public static final int FLAG_HAS_SYS_UI_SCRIM = BaseState.getFlag(4);
     // Flag to inticate that all popups should be closed when this state is enabled.
     public static final int FLAG_CLOSE_POPUPS = BaseState.getFlag(5);
-    public static final int FLAG_OVERVIEW_UI = BaseState.getFlag(6);
+    public static final int FLAG_RECENTS_VIEW_VISIBLE = BaseState.getFlag(6);
 
     // Flag indicating that hotseat and its contents are not accessible.
     public static final int FLAG_HOTSEAT_INACCESSIBLE = BaseState.getFlag(7);
@@ -158,14 +158,14 @@
     /**
      * True if the state has overview panel visible.
      */
-    public final boolean overviewUi;
+    public final boolean isRecentsViewVisible;
 
     private final int mFlags;
 
     public LauncherState(int id, int statsLogOrdinal, int flags) {
         this.statsLogOrdinal = statsLogOrdinal;
         this.mFlags = flags;
-        this.overviewUi = (flags & FLAG_OVERVIEW_UI) != 0;
+        this.isRecentsViewVisible = (flags & FLAG_RECENTS_VIEW_VISIBLE) != 0;
         this.ordinal = id;
         sAllStates[id] = this;
     }
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index cc9f08e..365fbd3 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -788,7 +788,7 @@
         if (mScroller.isFinished() && pageScrollChanged) {
             // TODO(b/246283207): Remove logging once root cause of flake detected.
             if (Utilities.isRunningInTestHarness() && !(this instanceof Workspace)) {
-                Log.d("b/246283207", this.getClass().getSimpleName() + "#onLayout() -> "
+                Log.d("b/246283207", TAG + "#onLayout() -> "
                         + "if(mScroller.isFinished() && pageScrollChanged) -> getNextPage(): "
                         + getNextPage() + ", getScrollForPage(getNextPage()): "
                         + getScrollForPage(getNextPage()));
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index b9a62e2..19a3002 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -70,6 +70,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.view.ViewGroup;
 import android.view.animation.Interpolator;
 
 import androidx.annotation.ChecksSdkIntAtLeast;
@@ -104,6 +105,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
+import java.util.function.Predicate;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -132,6 +134,10 @@
     @ChecksSdkIntAtLeast(api = VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "U")
     public static final boolean ATLEAST_U = Build.VERSION.SDK_INT >= VERSION_CODES.UPSIDE_DOWN_CAKE;
 
+    @ChecksSdkIntAtLeast(api = VERSION_CODES.VANILLA_ICE_CREAM, codename = "V")
+    public static final boolean ATLEAST_V = Build.VERSION.SDK_INT
+            >= VERSION_CODES.VANILLA_ICE_CREAM;
+
     /**
      * Set on a motion event dispatched from the nav bar. See {@link MotionEvent#setEdgeFlags(int)}.
      */
@@ -639,7 +645,7 @@
         } else {
             // Wrap the main icon in AID
             try (LauncherIcons li = LauncherIcons.obtain(context)) {
-                result = li.wrapToAdaptiveIcon(mainIcon);
+                result = li.wrapToAdaptiveIcon(mainIcon, null);
             }
         }
         if (result == null) {
@@ -835,4 +841,27 @@
                 // No-Op
         }
     }
+
+    /**
+     * Does a depth-first search through the View hierarchy starting at root, to find a view that
+     * matches the predicate. Returns null if no View was found. View has a findViewByPredicate
+     * member function but it is currently a @hide API.
+     */
+    @Nullable
+    public static <T extends View> T findViewByPredicate(@NonNull View root,
+            @NonNull Predicate<View> predicate) {
+        if (predicate.test(root)) {
+            return (T) root;
+        }
+        if (root instanceof ViewGroup parent) {
+            int count = parent.getChildCount();
+            for (int i = 0; i < count; i++) {
+                View view = findViewByPredicate(parent.getChildAt(i), predicate);
+                if (view != null) {
+                    return (T) view;
+                }
+            }
+        }
+        return null;
+    }
 }
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 0792641..56a7fef 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -18,7 +18,6 @@
 import static com.android.launcher3.Flags.enableExpandingPauseWorkButton;
 import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.MAIN;
 import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
-import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.WORK;
 import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER;
 import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_DISABLED_CARD;
 import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_EDU_CARD;
@@ -587,12 +586,6 @@
             return;
         }
 
-        if (!FeatureFlags.ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES.get()) {
-            RecyclerView.ItemDecoration decoration = getMainAdapterProvider().getDecorator();
-            getSearchRecyclerView().removeItemDecoration(decoration);
-            getSearchRecyclerView().addItemDecoration(decoration);
-        }
-
         // replaceAppsRVcontainer() needs to use both mUsingTabs value to remove the old view AND
         // showTabs value to create new view. Hence the mUsingTabs new value assignment MUST happen
         // after this call.
diff --git a/src/com/android/launcher3/allapps/FloatingMaskView.java b/src/com/android/launcher3/allapps/FloatingMaskView.java
new file mode 100644
index 0000000..606eb03
--- /dev/null
+++ b/src/com/android/launcher3/allapps/FloatingMaskView.java
@@ -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.launcher3.allapps;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.android.launcher3.R;
+import com.android.launcher3.views.ActivityContext;
+
+public class FloatingMaskView extends ConstraintLayout {
+
+    private final ActivityContext mActivityContext;
+    private ImageView mBottomBox;
+
+    public FloatingMaskView(Context context) {
+        this(context, null, 0);
+    }
+
+    public FloatingMaskView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public FloatingMaskView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mActivityContext = ActivityContext.lookupContext(context);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mBottomBox = findViewById(R.id.bottom_box);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) getLayoutParams();
+        AllAppsRecyclerView allAppsContainerView =
+                mActivityContext.getAppsView().getActiveRecyclerView();
+        if (lp != null) {
+            lp.rightMargin = allAppsContainerView.getPaddingRight();
+            lp.leftMargin = allAppsContainerView.getPaddingLeft();
+            mBottomBox.setMinimumHeight(allAppsContainerView.getPaddingBottom());
+        }
+    }
+}
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index ae0e80c..27340a3 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -20,7 +20,6 @@
 import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
 
-import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PRIVATESPACE;
 import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.MAIN;
 import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_ICON;
@@ -28,8 +27,12 @@
 import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER;
 import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_NOTHING;
 import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_BEGIN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_END;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_LOCK_TAP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_BEGIN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_END;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_NOT_PINNABLE;
@@ -57,6 +60,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
+import androidx.constraintlayout.widget.ConstraintLayout;
 import androidx.recyclerview.widget.LinearSmoothScroller;
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -93,14 +97,17 @@
     private static final int TEXT_UNLOCK_OPACITY_DURATION = 300;
     private static final int TEXT_LOCK_OPACITY_DURATION = 50;
     private static final int APP_OPACITY_DURATION = 400;
+    private static final int MASK_VIEW_DURATION = 200;
     private static final int APP_OPACITY_DELAY = 400;
     private static final int SETTINGS_AND_LOCK_GROUP_TRANSITION_DELAY = 400;
     private static final int SETTINGS_OPACITY_DELAY = 400;
     private static final int LOCK_TEXT_OPACITY_DELAY = 500;
+    private static final int MASK_VIEW_DELAY = 400;
     private static final int NO_DELAY = 0;
     private final ActivityAllAppsContainerView<?> mAllApps;
     private final Predicate<UserHandle> mPrivateProfileMatcher;
     private final int mPsHeaderHeight;
+    private final int mFloatingMaskViewCornerRadius;
     private final RecyclerView.OnScrollListener mOnIdleScrollListener =
             new RecyclerView.OnScrollListener() {
         @Override
@@ -120,6 +127,7 @@
     private Runnable mOnPSHeaderAdded;
     @Nullable
     private RelativeLayout mPSHeader;
+    private ConstraintLayout mFloatingMaskView;
     private final String mLockedStateContentDesc;
     private final String mUnLockedStateContentDesc;
 
@@ -139,6 +147,8 @@
                 .getString(R.string.ps_container_lock_button_content_description);
         mUnLockedStateContentDesc = mAllApps.getContext()
                 .getString(R.string.ps_container_unlock_button_content_description);
+        mFloatingMaskViewCornerRadius = mAllApps.getContext().getResources().getDimensionPixelSize(
+                R.dimen.ps_floating_mask_corner_radius);
     }
 
     /** Adds Private Space Header to the layout. */
@@ -216,6 +226,7 @@
                 .hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED);
         int updatedState = isEnabled ? STATE_ENABLED : STATE_DISABLED;
         setCurrentState(updatedState);
+        mFloatingMaskView = null;
         if (mPSHeader != null) {
             mPSHeader.setAlpha(1);
         }
@@ -491,12 +502,15 @@
                 RecyclerView.LayoutManager layoutManager = allAppsRecyclerView.getLayoutManager();
                 if (layoutManager != null) {
                     startAnimationScroll(allAppsRecyclerView, layoutManager, smoothScroller);
-                    currentItem.decorationInfo = null;
+                    // Preserve decorator if floating mask view exists.
+                    if (mFloatingMaskView == null) {
+                        currentItem.decorationInfo = null;
+                    }
                 }
                 break;
             }
             // Make the private space apps gone to "collapse".
-            if (isPrivateSpaceItem(currentItem)) {
+            if (mFloatingMaskView == null && isPrivateSpaceItem(currentItem)) {
                 RecyclerView.ViewHolder viewHolder =
                         allAppsRecyclerView.findViewHolderForAdapterPosition(i);
                 if (viewHolder != null) {
@@ -634,9 +648,8 @@
             setAnimationRunning(false);
             return;
         }
+        attachFloatingMaskView(expand);
         ViewGroup settingsAndLockGroup = mPSHeader.findViewById(R.id.settingsAndLockGroup);
-        ViewGroup lockButton = mPSHeader.findViewById(R.id.ps_lock_unlock_button);
-        TextView lockText = lockButton.findViewById(R.id.lock_text);
         if (settingsAndLockGroup.getLayoutTransition() == null) {
             // Set a new transition if the current ViewGroup does not already contain one as each
             // transition should only happen once when applied.
@@ -646,21 +659,35 @@
                 LayoutTransition.CHANGING,
                 expand ? SETTINGS_AND_LOCK_GROUP_TRANSITION_DELAY : NO_DELAY);
         PropertySetter headerSetter = new AnimatedPropertySetter();
-        ImageButton settingsButton = mPSHeader.findViewById(R.id.ps_settings_button);
-        updateSettingsGearAlpha(settingsButton, expand, headerSetter);
-        updateLockTextAlpha(lockText, expand, headerSetter);
+        headerSetter.add(updateSettingsGearAlpha(expand));
+        headerSetter.add(updateLockTextAlpha(expand));
         AnimatorSet animatorSet = headerSetter.buildAnim();
         animatorSet.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
+                mStatsLogManager.logger().sendToInteractionJankMonitor(
+                        expand
+                                ? LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_BEGIN
+                                : LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_BEGIN,
+                        mAllApps.getActiveRecyclerView());
                 // Animate the collapsing of the text at the same time while updating lock button.
-                lockText.setVisibility(expand ? VISIBLE : GONE);
+                mPSHeader.findViewById(R.id.lock_text).setVisibility(expand ? VISIBLE : GONE);
                 setAnimationRunning(true);
             }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                detachFloatingMaskView();
+            }
         });
         animatorSet.addListener(forEndCallback(() -> {
             setAnimationRunning(false);
             getMainRecyclerView().setChildAttachedConsumer(child -> child.setAlpha(1));
+            mStatsLogManager.logger().sendToInteractionJankMonitor(
+                    expand
+                            ? LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_END
+                            : LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_END,
+                    mAllApps.getActiveRecyclerView());
             if (!expand) {
                 // Call onAppsUpdated() because it may be canceled when this animation occurs.
                 mAllApps.getPersonalAppList().onAppsUpdated();
@@ -671,13 +698,17 @@
             }
         }));
         if (expand) {
-            animatorSet.playTogether(animateAlphaOfIcons(true));
+            animatorSet.playTogether(animateAlphaOfIcons(true),
+                    translateFloatingMaskView(false));
         } else {
             if (isPrivateSpaceHidden()) {
-                animatorSet.playSequentially(animateAlphaOfIcons(false),
-                        animateCollapseAnimation(), fadeOutHeaderAlpha());
+                animatorSet.playSequentially(translateFloatingMaskView(false),
+                        animateAlphaOfIcons(false),
+                        animateCollapseAnimation(),
+                        fadeOutHeaderAlpha());
             } else {
-                animatorSet.playSequentially(animateAlphaOfIcons(false),
+                animatorSet.playSequentially(translateFloatingMaskView(true),
+                        animateAlphaOfIcons(false),
                         animateCollapseAnimation());
             }
         }
@@ -705,6 +736,27 @@
         return alphaAnim;
     }
 
+    /** Fades out the private space container. */
+    private ValueAnimator translateFloatingMaskView(boolean animateIn) {
+        if (!Flags.privateSpaceFloatingMaskView() || mFloatingMaskView == null) {
+            return new ValueAnimator();
+        }
+        // Translate base on the height amount. Translates out on expand and in on collapse.
+        float floatingMaskViewHeight = getFloatingMaskViewHeight();
+        float from = animateIn ? floatingMaskViewHeight : 0;
+        float to = animateIn ? 0 : floatingMaskViewHeight;
+        ValueAnimator alphaAnim = ObjectAnimator.ofFloat(from, to);
+        alphaAnim.setDuration(MASK_VIEW_DURATION);
+        alphaAnim.setStartDelay(MASK_VIEW_DELAY);
+        alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                mFloatingMaskView.setTranslationY((float) valueAnimator.getAnimatedValue());
+            }
+        });
+        return alphaAnim;
+    }
+
     /** Animates the layout changes when the text of the button becomes visible/gone. */
     private void enableLayoutTransition(ViewGroup settingsAndLockGroup) {
         LayoutTransition settingsAndLockTransition = new LayoutTransition();
@@ -728,19 +780,44 @@
     }
 
     /** Change the settings gear alpha when expanded or collapsed. */
-    private void updateSettingsGearAlpha(ImageButton settingsButton, boolean expand,
-            PropertySetter setter) {
-        float toAlpha = expand ? 1 : 0;
-        setter.setFloat(settingsButton, VIEW_ALPHA, toAlpha, Interpolators.LINEAR)
-                .setDuration(SETTINGS_OPACITY_DURATION).setStartDelay(expand ?
-                        SETTINGS_OPACITY_DELAY : NO_DELAY);
+    private ValueAnimator updateSettingsGearAlpha(boolean expand) {
+        if (mPSHeader == null) {
+            return new ValueAnimator();
+        }
+        float from = expand ? 0 : 1;
+        float to = expand ? 1 : 0;
+        ValueAnimator settingsAlphaAnim = ObjectAnimator.ofFloat(from, to);
+        settingsAlphaAnim.setDuration(SETTINGS_OPACITY_DURATION);
+        settingsAlphaAnim.setStartDelay(expand ? SETTINGS_OPACITY_DELAY : NO_DELAY);
+        settingsAlphaAnim.setInterpolator(Interpolators.LINEAR);
+        settingsAlphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                mPSHeader.findViewById(R.id.ps_settings_button)
+                        .setAlpha((float) valueAnimator.getAnimatedValue());
+            }
+        });
+        return settingsAlphaAnim;
     }
 
-    private void updateLockTextAlpha(TextView textView, boolean expand, PropertySetter setter) {
-        float toAlpha = expand ? 1 : 0;
-        setter.setFloat(textView, VIEW_ALPHA, toAlpha, Interpolators.LINEAR)
-                .setDuration(expand ? TEXT_UNLOCK_OPACITY_DURATION : TEXT_LOCK_OPACITY_DURATION)
-                .setStartDelay(expand ? LOCK_TEXT_OPACITY_DELAY : NO_DELAY);
+    private ValueAnimator updateLockTextAlpha(boolean expand) {
+        if (mPSHeader == null) {
+            return new ValueAnimator();
+        }
+        float from = expand ? 0 : 1;
+        float to = expand ? 1 : 0;
+        ValueAnimator alphaAnim = ObjectAnimator.ofFloat(from, to);
+        alphaAnim.setDuration(expand ? TEXT_UNLOCK_OPACITY_DURATION : TEXT_LOCK_OPACITY_DURATION);
+        alphaAnim.setStartDelay(expand ? LOCK_TEXT_OPACITY_DELAY : NO_DELAY);
+        alphaAnim.setInterpolator(Interpolators.LINEAR);
+        alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                mPSHeader.findViewById(R.id.lock_text).setAlpha(
+                        (float) valueAnimator.getAnimatedValue());
+            }
+        });
+        return alphaAnim;
     }
 
     void expandPrivateSpace() {
@@ -771,6 +848,28 @@
         });
     }
 
+    private void attachFloatingMaskView(boolean expand) {
+        if (!Flags.privateSpaceFloatingMaskView()) {
+            return;
+        }
+        mFloatingMaskView = (FloatingMaskView) mAllApps.getLayoutInflater().inflate(
+                R.layout.private_space_mask_view, mAllApps, false);
+        mAllApps.addView(mFloatingMaskView);
+        // Translate off the screen first if its collapsing so this header view isn't visible to
+        // user when animation starts.
+        if (!expand) {
+            mFloatingMaskView.setTranslationY(getFloatingMaskViewHeight());
+        }
+        mFloatingMaskView.setVisibility(VISIBLE);
+    }
+
+    private void detachFloatingMaskView() {
+        if (mFloatingMaskView != null) {
+            mAllApps.removeView(mFloatingMaskView);
+        }
+        mFloatingMaskView = null;
+    }
+
     /** Starts the smooth scroll with the provided smoothScroller and add idle listener. */
     private void startAnimationScroll(AllAppsRecyclerView allAppsRecyclerView,
             RecyclerView.LayoutManager layoutManager, RecyclerView.SmoothScroller smoothScroller) {
@@ -780,6 +879,10 @@
         allAppsRecyclerView.addOnScrollListener(mOnIdleScrollListener);
     }
 
+    private float getFloatingMaskViewHeight() {
+        return mFloatingMaskViewCornerRadius + getMainRecyclerView().getPaddingBottom();
+    }
+
     AllAppsRecyclerView getMainRecyclerView() {
         return mAllApps.mAH.get(ActivityAllAppsContainerView.AdapterHolder.MAIN).mRecyclerView;
     }
diff --git a/src/com/android/launcher3/allapps/UserProfileManager.java b/src/com/android/launcher3/allapps/UserProfileManager.java
index 6a1f37a..3351ee3 100644
--- a/src/com/android/launcher3/allapps/UserProfileManager.java
+++ b/src/com/android/launcher3/allapps/UserProfileManager.java
@@ -52,11 +52,11 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface UserProfileState { }
 
+    protected final StatsLogManager mStatsLogManager;
     @UserProfileState
     private int mCurrentState;
 
     private final UserManager mUserManager;
-    private final StatsLogManager mStatsLogManager;
     private final UserCache mUserCache;
 
     protected UserProfileManager(UserManager userManager,
diff --git a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
index 64fd237..4a8c96b 100644
--- a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
@@ -19,8 +19,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import androidx.recyclerview.widget.RecyclerView;
-
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.allapps.AllAppsGridAdapter;
 import com.android.launcher3.model.data.ItemInfo;
@@ -30,13 +28,10 @@
  * Provides views for local search results.
  */
 public class DefaultSearchAdapterProvider extends SearchAdapterProvider<ActivityContext> {
-
-    private final RecyclerView.ItemDecoration mDecoration;
     private View mHighlightedView;
 
     public DefaultSearchAdapterProvider(ActivityContext launcher) {
         super(launcher);
-        mDecoration = new RecyclerView.ItemDecoration() { };
     }
 
     @Override
@@ -74,11 +69,6 @@
     }
 
     @Override
-    public RecyclerView.ItemDecoration getDecorator() {
-        return mDecoration;
-    }
-
-    @Override
     public void clearHighlightedItem() {
         mHighlightedView = null;
     }
diff --git a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
index 15756f5..82c9c90 100644
--- a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
@@ -20,8 +20,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import androidx.recyclerview.widget.RecyclerView;
-
 import com.android.launcher3.allapps.AllAppsGridAdapter;
 import com.android.launcher3.views.ActivityContext;
 
@@ -50,11 +48,6 @@
     public abstract View getHighlightedItem();
 
     /**
-     * Returns the item decorator.
-     */
-    public abstract RecyclerView.ItemDecoration getDecorator();
-
-    /**
      * Clear the highlighted view.
      */
     public abstract void clearHighlightedItem();
diff --git a/src/com/android/launcher3/apppairs/AppPairIcon.java b/src/com/android/launcher3/apppairs/AppPairIcon.java
index 1f73241..32445ec 100644
--- a/src/com/android/launcher3/apppairs/AppPairIcon.java
+++ b/src/com/android/launcher3/apppairs/AppPairIcon.java
@@ -30,6 +30,8 @@
 
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.Reorderable;
@@ -85,6 +87,11 @@
                 : activity.getLayoutInflater();
         AppPairIcon icon = (AppPairIcon) inflater.inflate(resId, group, false);
 
+        if (Flags.enableFocusOutline() && activity instanceof Launcher) {
+            icon.setOnFocusChangeListener(((Launcher) activity).getFocusHandler());
+            icon.setDefaultFocusHighlightEnabled(false);
+        }
+
         // Sort contents, so that left-hand app comes first
         appPairInfo.getContents().sort(Comparator.comparingInt(a -> a.rank));
 
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 60c413e..4b908bf 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -61,15 +61,6 @@
      * and set a default value for the flag. This will be the default value on Debug builds.
      * <p>
      */
-    // TODO(Block 1): Clean up flags
-    public static final BooleanFlag ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES = getReleaseFlag(
-            270394041, "ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES", ENABLED,
-            "Enable option to replace decorator-based search result backgrounds with drawables");
-
-    public static final BooleanFlag ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION = getReleaseFlag(
-            270394392, "ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION", ENABLED,
-            "Enable option to launch search results using the new view container transitions");
-
     // TODO(Block 2): Clean up flags
     public static final BooleanFlag ENABLE_MULTI_DISPLAY_PARTIAL_DEPTH = getDebugFlag(270395073,
             "ENABLE_MULTI_DISPLAY_PARTIAL_DEPTH", DISABLED,
@@ -203,9 +194,6 @@
             "ENABLE_EXPANDING_PAUSE_WORK_BUTTON", DISABLED,
             "Expand and collapse pause work button while scrolling");
 
-    public static final BooleanFlag COLLECT_SEARCH_HISTORY = getReleaseFlag(270391455,
-            "COLLECT_SEARCH_HISTORY", DISABLED, "Allow launcher to collect search history for log");
-
     // Aconfig migration complete for ENABLE_TWOLINE_ALLAPPS.
     public static final BooleanFlag ENABLE_TWOLINE_ALLAPPS = getDebugFlag(270390937,
             "ENABLE_TWOLINE_ALLAPPS", DISABLED, "Enables two line label inside all apps.");
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 1f388c2..ae8f1d5 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -76,7 +76,6 @@
 import com.android.launcher3.celllayout.CellPosMapper;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.BgDataModel;
 import com.android.launcher3.model.BgDataModel.FixedContainerItems;
 import com.android.launcher3.model.WidgetItem;
@@ -107,7 +106,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.ConcurrentLinkedQueue;
 
 /**
  * Utility class for generating the preview of Launcher for a given InvariantDeviceProfile.
@@ -126,44 +124,12 @@
      */
     public static class PreviewContext extends SandboxContext {
 
-        private final InvariantDeviceProfile mIdp;
-        private final ConcurrentLinkedQueue<LauncherIconsForPreview> mIconPool =
-                new ConcurrentLinkedQueue<>();
-
         public PreviewContext(Context base, InvariantDeviceProfile idp) {
             super(base);
-            mIdp = idp;
             putObject(InvariantDeviceProfile.INSTANCE, idp);
             putObject(LauncherAppState.INSTANCE,
                     new LauncherAppState(this, null /* iconCacheFileName */));
         }
-
-        /**
-         * Creates a new LauncherIcons for the preview, skipping the global pool
-         */
-        public LauncherIcons newLauncherIcons(Context context) {
-            LauncherIconsForPreview launcherIconsForPreview = mIconPool.poll();
-            if (launcherIconsForPreview != null) {
-                return launcherIconsForPreview;
-            }
-            return new LauncherIconsForPreview(context, mIdp.fillResIconDpi, mIdp.iconBitmapSize,
-                    -1 /* poolId */);
-        }
-
-        private final class LauncherIconsForPreview extends LauncherIcons {
-
-            private LauncherIconsForPreview(Context context, int fillResIconDpi, int iconBitmapSize,
-                    int poolId) {
-                super(context, fillResIconDpi, iconBitmapSize, poolId);
-            }
-
-            @Override
-            public void recycle() {
-                // Clear any temporary state variables
-                clear();
-                mIconPool.offer(this);
-            }
-        }
     }
 
     private final List<OnDeviceProfileChangeListener> mDpChangeListeners = new ArrayList<>();
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 329f717..04d8ac0 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -373,8 +373,13 @@
                     infoInOut.user, activityInfoProvider, mLauncherActivityInfoCachingLogic,
                     usePkgIcon, useLowResIcon);
             applyPackageEntry(packageEntry, infoInOut, entry);
-        } else {
+        } else if (useLowResIcon || !entry.bitmap.isNullOrLowRes()
+                || infoInOut.bitmap.isNullOrLowRes()) {
+            // Only use cache entry if it will not downgrade the current bitmap in infoInOut
             applyCacheEntry(entry, infoInOut);
+        } else {
+            Log.d(TAG, "getTitleAndIcon: Cache entry bitmap was a downgrade of existing bitmap"
+                    + " in ItemInfo. Skipping.");
         }
     }
 
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index 7331c6f..e90a1e0 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -25,79 +25,53 @@
 import com.android.launcher3.Flags;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.graphics.IconShape;
-import com.android.launcher3.graphics.LauncherPreviewRenderer;
 import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.UserIconInfo;
 
+import java.util.concurrent.ConcurrentLinkedQueue;
+
 /**
  * Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class
  * that are threadsafe.
  */
 public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
 
-    private static final Object sPoolSync = new Object();
-    private static LauncherIcons sPool;
-    private static int sPoolId = 0;
+    private static final MainThreadInitializedObject<Pool> POOL =
+            new MainThreadInitializedObject<>(Pool::new);
 
     /**
      * Return a new Message instance from the global pool. Allows us to
      * avoid allocating new objects in many cases.
      */
     public static LauncherIcons obtain(Context context) {
-        if (context instanceof LauncherPreviewRenderer.PreviewContext) {
-            return ((LauncherPreviewRenderer.PreviewContext) context).newLauncherIcons(context);
-        }
-
-        int poolId;
-        synchronized (sPoolSync) {
-            if (sPool != null) {
-                LauncherIcons m = sPool;
-                sPool = m.next;
-                m.next = null;
-                return m;
-            }
-            poolId = sPoolId;
-        }
-
-        InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(context);
-        return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize, poolId);
+        return POOL.get(context).obtain();
     }
 
-    public static void clearPool() {
-        synchronized (sPoolSync) {
-            sPool = null;
-            sPoolId++;
-        }
+    public static void clearPool(Context context) {
+        POOL.get(context).close();
     }
 
-    private final int mPoolId;
-
-    private LauncherIcons next;
+    private final ConcurrentLinkedQueue<LauncherIcons> mPool;
 
     private MonochromeIconFactory mMonochromeIconFactory;
 
-    protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId) {
+    protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize,
+            ConcurrentLinkedQueue<LauncherIcons> pool) {
         super(context, fillResIconDpi, iconBitmapSize,
                 IconShape.INSTANCE.get(context).getShape().enableShapeDetection());
         mMonoIconEnabled = Themes.isThemedIconEnabled(context);
-        mPoolId = poolId;
+        mPool = pool;
     }
 
     /**
      * Recycles a LauncherIcons that may be in-use.
      */
     public void recycle() {
-        synchronized (sPoolSync) {
-            if (sPoolId != mPoolId) {
-                return;
-            }
-            // Clear any temporary state variables
-            clear();
-
-            next = sPool;
-            sPool = this;
-        }
+        clear();
+        mPool.add(this);
     }
 
     @Override
@@ -122,4 +96,33 @@
     public void close() {
         recycle();
     }
+
+    private static class Pool implements SafeCloseable {
+
+        private final Context mContext;
+
+        @NonNull
+        private ConcurrentLinkedQueue<LauncherIcons> mPool = new ConcurrentLinkedQueue<>();
+
+        private Pool(Context context) {
+            mContext = context;
+        }
+
+        public LauncherIcons obtain() {
+            ConcurrentLinkedQueue<LauncherIcons> pool = mPool;
+            LauncherIcons m = pool.poll();
+
+            if (m == null) {
+                InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext);
+                return new LauncherIcons(mContext, idp.fillResIconDpi, idp.iconBitmapSize, pool);
+            } else {
+                return m;
+            }
+        }
+
+        @Override
+        public void close() {
+            mPool = new ConcurrentLinkedQueue<>();
+        }
+    }
 }
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 2f3c2b6..861631d 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -769,7 +769,31 @@
         LAUNCHER_PRIVATE_SPACE_USER_INSTALLED_APPS_COUNT(1672),
 
         @UiEvent(doc = "Number of preinstalled Private profile apps, shown under separator line")
-        LAUNCHER_PRIVATE_SPACE_PREINSTALLED_APPS_COUNT(1673)
+        LAUNCHER_PRIVATE_SPACE_PREINSTALLED_APPS_COUNT(1673),
+
+        @UiEvent(doc = "Private space lock animation started")
+        LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_BEGIN(1725),
+
+        @UiEvent(doc = "Private space lock animation finished")
+        LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_END(1726),
+
+        @UiEvent(doc = "Private space unlock animation started")
+        LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_BEGIN(1727),
+
+        @UiEvent(doc = "Private space unlock animation finished")
+        LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_END(1728),
+
+        @UiEvent(doc = "User rotates whilst in Overview / RecentsView")
+        LAUNCHER_OVERVIEW_ORIENTATION_CHANGED(1762),
+
+        @UiEvent(doc = "User launches Overview from 3 button navigation")
+        LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_3_BUTTON(1763),
+
+        @UiEvent(doc = "User launches Overview from alt+tab keyboard quick switch")
+        LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_QUICK_SWITCH(1764),
+
+        @UiEvent(doc = "User launches Overview from meta+tab keyboard shortcut")
+        LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT(1765),
 
         // ADD MORE
         ;
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index 39c1243..64ebbf3 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -60,7 +60,7 @@
 
     private static final String TAG = "AllAppsList";
     private static final Consumer<AppInfo> NO_OP_CONSUMER = a -> { };
-
+    private static final boolean DEBUG = true;
 
     public static final int DEFAULT_APPLICATIONS_NUMBER = 42;
 
@@ -220,6 +220,11 @@
                     updatedAppInfos.add(appInfo);
                 } else if (installInfo.state == PackageInstallInfo.STATUS_FAILED
                         && !appInfo.isAppStartable()) {
+                    if (DEBUG) {
+                        Log.w(TAG, "updatePromiseInstallInfo: removing app due to install"
+                                + " failure and appInfo not startable."
+                                + " package=" + appInfo.getTargetPackage());
+                    }
                     removeApp(i);
                 }
             }
@@ -301,6 +306,7 @@
             Context context, String packageName, UserHandle user) {
         final ApiWrapper apiWrapper = ApiWrapper.INSTANCE.get(context);
         final UserCache userCache = UserCache.getInstance(context);
+        final PackageManagerHelper pmHelper = PackageManagerHelper.INSTANCE.get(context);
         final List<LauncherActivityInfo> matches = context.getSystemService(LauncherApps.class)
                 .getActivityList(packageName, user);
         if (matches.size() > 0) {
@@ -311,7 +317,10 @@
                 if (user.equals(applicationInfo.user)
                         && packageName.equals(applicationInfo.componentName.getPackageName())) {
                     if (!findActivity(matches, applicationInfo.componentName)) {
-                        Log.w(TAG, "Changing shortcut target due to app component name change.");
+                        if (DEBUG) {
+                            Log.w(TAG, "Changing shortcut target due to app component name change."
+                                    + " package=" + packageName);
+                        }
                         removeApp(i);
                     }
                 }
@@ -330,12 +339,16 @@
                     applicationInfo.sectionName = mIndex.computeSectionName(applicationInfo.title);
                     applicationInfo.intent = launchIntent;
                     AppInfo.updateRuntimeFlagsForActivityTarget(applicationInfo, info,
-                            userCache.getUserInfo(user), apiWrapper);
+                            userCache.getUserInfo(user), apiWrapper, pmHelper);
                     mDataChanged = true;
                 }
             }
         } else {
             // Remove all data for this package.
+            if (DEBUG) {
+                Log.w(TAG, "updatePromiseInstallInfo: no Activities matched updated package,"
+                        + " removing all apps from package=" + packageName);
+            }
             for (int i = data.size() - 1; i >= 0; i--) {
                 final AppInfo applicationInfo = data.get(i);
                 if (user.equals(applicationInfo.user)
diff --git a/src/com/android/launcher3/model/DatabaseHelper.java b/src/com/android/launcher3/model/DatabaseHelper.java
index 132b606..8368256 100644
--- a/src/com/android/launcher3/model/DatabaseHelper.java
+++ b/src/com/android/launcher3/model/DatabaseHelper.java
@@ -505,7 +505,7 @@
 
     public int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) {
         // TODO: Use multiple loaders with fall-back and transaction.
-        int count = loader.loadLayout(db, new IntArray());
+        int count = loader.loadLayout(db);
 
         // Ensure that the max ids are initialized
         mMaxItemId = initializeMaxItemId(db);
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 0875974..84130c7 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -57,6 +57,7 @@
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.UserIconInfo;
 
 import java.net.URISyntaxException;
@@ -73,6 +74,7 @@
 
     private final LauncherAppState mApp;
     private final Context mContext;
+    private final PackageManagerHelper mPmHelper;
     private final IconCache mIconCache;
     private final InvariantDeviceProfile mIDP;
     private final @Nullable LauncherRestoreEventLogger mRestoreEventLogger;
@@ -114,6 +116,7 @@
     public int restoreFlag;
 
     public LoaderCursor(Cursor cursor, LauncherAppState app, UserManagerState userManagerState,
+            PackageManagerHelper pmHelper,
             @Nullable LauncherRestoreEventLogger restoreEventLogger) {
         super(cursor);
 
@@ -121,6 +124,7 @@
         allUsers = userManagerState.allUsers;
         mContext = app.getContext();
         mIconCache = app.getIconCache();
+        mPmHelper = pmHelper;
         mIDP = app.getInvariantDeviceProfile();
         mRestoreEventLogger = restoreEventLogger;
 
@@ -368,7 +372,7 @@
 
         if (mActivityInfo != null) {
             AppInfo.updateRuntimeFlagsForActivityTarget(info, mActivityInfo, userIconInfo,
-                    ApiWrapper.INSTANCE.get(mContext));
+                    ApiWrapper.INSTANCE.get(mContext), mPmHelper);
         }
 
         // from the db
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 876bed4..0d40a24 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -435,7 +435,8 @@
             mShortcutKeyToPinnedShortcuts = new HashMap<>();
             final LoaderCursor c = new LoaderCursor(
                     dbController.query(TABLE_NAME, null, selection, null, null),
-                    mApp, mUserManagerState, mIsRestoreFromBackup ? restoreEventLogger : null);
+                    mApp, mUserManagerState, mPmHelper,
+                    mIsRestoreFromBackup ? restoreEventLogger : null);
             final Bundle extras = c.getExtras();
             mDbName = extras == null ? null : extras.getString(ModelDbController.EXTRA_DB_NAME);
             try {
@@ -697,7 +698,7 @@
             for (int i = 0; i < apps.size(); i++) {
                 LauncherActivityInfo app = apps.get(i);
                 AppInfo appInfo = new AppInfo(app, mUserCache.getUserInfo(user),
-                        ApiWrapper.INSTANCE.get(mApp.getContext()), quietMode);
+                        ApiWrapper.INSTANCE.get(mApp.getContext()), mPmHelper, quietMode);
                 if (Flags.enableSupportForArchiving() && app.getApplicationInfo().isArchived) {
                     // For archived apps, include progress info in case there is a pending
                     // install session post restart of device.
diff --git a/src/com/android/launcher3/model/ModelDelegate.java b/src/com/android/launcher3/model/ModelDelegate.java
index 8360b14..2264d35 100644
--- a/src/com/android/launcher3/model/ModelDelegate.java
+++ b/src/com/android/launcher3/model/ModelDelegate.java
@@ -26,6 +26,7 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.ResourceBasedOverride;
 
 import java.io.FileDescriptor;
@@ -41,15 +42,16 @@
      * Creates and initializes a new instance of the delegate
      */
     public static ModelDelegate newInstance(
-            Context context, LauncherAppState app, AllAppsList appsList, BgDataModel dataModel,
-            boolean isPrimaryInstance) {
+            Context context, LauncherAppState app, PackageManagerHelper pmHelper,
+            AllAppsList appsList, BgDataModel dataModel, boolean isPrimaryInstance) {
         ModelDelegate delegate = Overrides.getObject(
                 ModelDelegate.class, context, R.string.model_delegate_class);
-        delegate.init(app, appsList, dataModel, isPrimaryInstance);
+        delegate.init(app, pmHelper, appsList, dataModel, isPrimaryInstance);
         return delegate;
     }
 
     protected final Context mContext;
+    protected PackageManagerHelper mPmHelper;
     protected LauncherAppState mApp;
     protected AllAppsList mAppsList;
     protected BgDataModel mDataModel;
@@ -62,9 +64,10 @@
     /**
      * Initializes the object with the given params.
      */
-    private void init(LauncherAppState app, AllAppsList appsList,
+    private void init(LauncherAppState app, PackageManagerHelper pmHelper, AllAppsList appsList,
             BgDataModel dataModel, boolean isPrimaryInstance) {
         this.mApp = app;
+        this.mPmHelper = pmHelper;
         this.mAppsList = appsList;
         this.mDataModel = dataModel;
         this.mIsPrimaryInstance = isPrimaryInstance;
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 802faae..079987b 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -74,8 +74,8 @@
 public class PackageUpdatedTask implements ModelUpdateTask {
 
     // TODO(b/290090023): Set to false after root causing is done.
-    private static final boolean DEBUG = true;
     private static final String TAG = "PackageUpdatedTask";
+    private static final boolean DEBUG = true;
 
     public static final int OP_NONE = 0;
     public static final int OP_ADD = 1;
@@ -117,29 +117,39 @@
                 : ItemInfoMatcher.ofPackages(packageSet, mUser);
         final HashSet<ComponentName> removedComponents = new HashSet<>();
         final HashMap<String, List<LauncherActivityInfo>> activitiesLists = new HashMap<>();
-
+        if (DEBUG) {
+            Log.d(TAG, "Package updated: mOp=" + getOpString()
+                    + " packages=" + Arrays.toString(packages));
+        }
         switch (mOp) {
             case OP_ADD: {
                 for (int i = 0; i < N; i++) {
-                    if (DEBUG) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
                     iconCache.updateIconsForPkg(packages[i], mUser);
                     if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
+                        if (DEBUG) {
+                            Log.d(TAG, "OP_ADD: PROMISE_APPS_IN_ALL_APPS enabled:"
+                                    + " removing promise icon apps from package=" + packages[i]);
+                        }
                         appsList.removePackage(packages[i], mUser);
                     }
-                    activitiesLists.put(
-                            packages[i], appsList.addPackage(context, packages[i], mUser));
+                    activitiesLists.put(packages[i],
+                            appsList.addPackage(context, packages[i], mUser));
                 }
                 flagOp = FlagOp.NO_OP.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
                 break;
             }
             case OP_UPDATE:
-                try (SafeCloseable t =
-                             appsList.trackRemoves(a -> removedComponents.add(a.componentName))) {
+                try (SafeCloseable t = appsList.trackRemoves(a -> {
+                    Log.d(TAG, "OP_UPDATE - AllAppsList.trackRemoves callback:"
+                            + " removed component=" + a.componentName
+                            + " id=" + a.id
+                            + " Look for earlier AllAppsList logs to find more information.");
+                    removedComponents.add(a.componentName);
+                })) {
                     for (int i = 0; i < N; i++) {
-                        if (DEBUG) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
                         iconCache.updateIconsForPkg(packages[i], mUser);
-                        activitiesLists.put(
-                                packages[i], appsList.updatePackage(context, packages[i], mUser));
+                        activitiesLists.put(packages[i],
+                                appsList.updatePackage(context, packages[i], mUser));
                     }
                 }
                 // Since package was just updated, the target must be available now.
@@ -147,14 +157,15 @@
                 break;
             case OP_REMOVE: {
                 for (int i = 0; i < N; i++) {
-                    FileLog.d(TAG, "Removing app icon: " + packages[i]);
                     iconCache.removeIconsForPkg(packages[i], mUser);
                 }
                 // Fall through
             }
             case OP_UNAVAILABLE:
                 for (int i = 0; i < N; i++) {
-                    if (DEBUG) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
+                    if (DEBUG) {
+                        Log.d(TAG, getOpString() + ": removing package=" + packages[i]);
+                    }
                     appsList.removePackage(packages[i], mUser);
                 }
                 flagOp = FlagOp.NO_OP.addFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
@@ -163,7 +174,6 @@
             case OP_UNSUSPEND:
                 flagOp = FlagOp.NO_OP.setFlag(
                         WorkspaceItemInfo.FLAG_DISABLED_SUSPENDED, mOp == OP_SUSPEND);
-                if (DEBUG) Log.d(TAG, "mAllAppsList.(un)suspend " + N);
                 appsList.updateDisabledFlags(matcher, flagOp);
                 break;
             case OP_USER_AVAILABILITY_CHANGE: {
@@ -234,7 +244,16 @@
                                                 .query(ShortcutRequest.PINNED);
                                 if (shortcut.isEmpty()) {
                                     isTargetValid = false;
+                                    if (DEBUG) {
+                                        Log.d(TAG, "Pinned Shortcut not found for updated"
+                                                + " package=" + si.getTargetPackage());
+                                    }
                                 } else {
+                                    if (DEBUG) {
+                                        Log.d(TAG, "Found pinned shortcut for updated"
+                                                + " package=" + si.getTargetPackage()
+                                                + ", isTargetValid=" + isTargetValid);
+                                    }
                                     si.updateFromDeepShortcutInfo(shortcut.get(0), context);
                                     infoUpdated = true;
                                 }
@@ -242,6 +261,7 @@
                                 isTargetValid = context.getSystemService(LauncherApps.class)
                                         .isActivityEnabled(cn, mUser);
                             }
+
                             if (!isTargetValid && (si.hasStatusFlag(
                                     FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON)
                                     || si.isArchived())) {
@@ -249,12 +269,25 @@
                                     infoUpdated = true;
                                 } else if (si.hasPromiseIconUi()) {
                                     removedShortcuts.add(si.id);
+                                    if (DEBUG) {
+                                        FileLog.w(TAG, "Removing restored shortcut promise icon"
+                                                + " that no longer points to valid component."
+                                                + " id=" + si.id
+                                                + ", package=" + si.getTargetPackage()
+                                                + ", status=" + si.status
+                                                + ", isArchived=" + si.isArchived());
+                                    }
                                     return;
                                 }
                             } else if (!isTargetValid) {
                                 removedShortcuts.add(si.id);
-                                FileLog.e(TAG, "Restored shortcut no longer valid "
-                                        + si.getIntent());
+                                if (DEBUG) {
+                                    FileLog.w(TAG, "Removing shortcut that no longer points to"
+                                            + " valid component."
+                                            + " id=" + si.id
+                                            + " package=" + si.getTargetPackage()
+                                            + " status=" + si.status);
+                                }
                                 return;
                             } else {
                                 si.status = WorkspaceItemInfo.DEFAULT;
@@ -269,6 +302,8 @@
                         if (isNewApkAvailable) {
                             List<LauncherActivityInfo> activities = activitiesLists.get(
                                     packageName);
+                            // TODO: See if we can migrate this to
+                            //  AppInfo#updateRuntimeFlagsForActivityTarget
                             si.setProgressLevel(
                                     activities == null || activities.isEmpty()
                                             ? 100
@@ -334,7 +369,8 @@
             if (!removedShortcuts.isEmpty()) {
                 taskController.deleteAndBindComponentsRemoved(
                         ItemInfoMatcher.ofItemIds(removedShortcuts),
-                        "removed because the target component is invalid");
+                        "removing shortcuts with invalid target components."
+                                + " ids=" + removedShortcuts);
             }
 
             if (!widgets.isEmpty()) {
@@ -346,6 +382,9 @@
         if (mOp == OP_REMOVE) {
             // Mark all packages in the broadcast to be removed
             Collections.addAll(removedPackages, packages);
+            if (DEBUG) {
+                Log.d(TAG, "OP_REMOVE: removing packages=" + Arrays.toString(packages));
+            }
 
             // No need to update the removedComponents as
             // removedPackages is a super-set of removedComponents
@@ -354,6 +393,10 @@
             final LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
             for (int i=0; i<N; i++) {
                 if (!launcherApps.isPackageEnabled(packages[i], mUser)) {
+                    if (DEBUG) {
+                        Log.d(TAG, "OP_UPDATE:"
+                                + " package " + packages[i] + " is disabled, removing package.");
+                    }
                     removedPackages.add(packages[i]);
                 }
             }
@@ -399,7 +442,8 @@
             return false;
         }
         // Try to find the best match activity.
-        Intent intent = new PackageManagerHelper(context).getAppLaunchIntent(packageName, mUser);
+        Intent intent = PackageManagerHelper.INSTANCE.get(context)
+                .getAppLaunchIntent(packageName, mUser);
         if (intent != null) {
             si.intent = intent;
             si.status = WorkspaceItemInfo.DEFAULT;
@@ -407,4 +451,18 @@
         }
         return false;
     }
+
+    private String getOpString() {
+        return switch (mOp) {
+            case OP_NONE -> "NONE";
+            case OP_ADD -> "ADD";
+            case OP_UPDATE -> "UPDATE";
+            case OP_REMOVE -> "REMOVE";
+            case OP_UNAVAILABLE -> "UNAVAILABLE";
+            case OP_SUSPEND -> "SUSPEND";
+            case OP_UNSUSPEND -> "UNSUSPEND";
+            case OP_USER_AVAILABILITY_CHANGE -> "USER_AVAILABILITY_CHANGE";
+            default -> "UNKNOWN";
+        };
+    }
 }
diff --git a/src/com/android/launcher3/model/UserManagerState.java b/src/com/android/launcher3/model/UserManagerState.java
index 720f08e..ed32430 100644
--- a/src/com/android/launcher3/model/UserManagerState.java
+++ b/src/com/android/launcher3/model/UserManagerState.java
@@ -17,6 +17,7 @@
 
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.SparseBooleanArray;
 
@@ -27,6 +28,8 @@
  */
 public class UserManagerState {
 
+    private static final String TAG = "UserManagerState";
+
     public final LongSparseArray<UserHandle> allUsers = new LongSparseArray<>();
 
     private final LongSparseArray<Boolean> mQuietUsersSerialNoMap = new LongSparseArray<>();
@@ -39,6 +42,13 @@
         for (UserHandle user : userManager.getUserProfiles()) {
             long serialNo = userCache.getSerialNumberForUser(user);
             boolean isUserQuiet = userManager.isQuietModeEnabled(user);
+            // Mapping different UserHandles to the same serialNo in allUsers could lead to losing
+            // UserHandle and cause a series of problems, such as incorrectly marking app as
+            // disabled and deleting app icons from workspace.
+            if (allUsers.get(serialNo) != null) {
+                Log.w(TAG, String.format("Override allUsers[%d]=%s with %s",
+                        serialNo, allUsers.get(serialNo), user));
+            }
             allUsers.put(serialNo, user);
             mQuietUsersHashCodeMap.put(user.hashCode(), isUserQuiet);
             mQuietUsersSerialNoMap.put(serialNo, isUserQuiet);
diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
index cea4380..90e47d6 100644
--- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
+++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
@@ -336,7 +336,8 @@
                     info,
                     activityInfo,
                     userCache.getUserInfo(c.user),
-                    ApiWrapper.INSTANCE[app.context]
+                    ApiWrapper.INSTANCE[app.context],
+                    pmHelper
                 )
             }
             if (
diff --git a/src/com/android/launcher3/model/data/AppInfo.java b/src/com/android/launcher3/model/data/AppInfo.java
index 18aa6e7..a4281f8 100644
--- a/src/com/android/launcher3/model/data/AppInfo.java
+++ b/src/com/android/launcher3/model/data/AppInfo.java
@@ -90,12 +90,12 @@
      */
     public AppInfo(Context context, LauncherActivityInfo info, UserHandle user) {
         this(info, UserCache.INSTANCE.get(context).getUserInfo(user),
-                ApiWrapper.INSTANCE.get(context),
+                ApiWrapper.INSTANCE.get(context), PackageManagerHelper.INSTANCE.get(context),
                 context.getSystemService(UserManager.class).isQuietModeEnabled(user));
     }
 
     public AppInfo(LauncherActivityInfo info, UserIconInfo userIconInfo,
-            ApiWrapper apiWrapper, boolean quietModeEnabled) {
+            ApiWrapper apiWrapper, PackageManagerHelper pmHelper, boolean quietModeEnabled) {
         this.componentName = info.getComponentName();
         this.container = CONTAINER_ALL_APPS;
         this.user = userIconInfo.user;
@@ -105,7 +105,7 @@
             runtimeStatusFlags |= FLAG_DISABLED_QUIET_USER;
         }
         uid = info.getApplicationInfo().uid;
-        updateRuntimeFlagsForActivityTarget(this, info, userIconInfo, apiWrapper);
+        updateRuntimeFlagsForActivityTarget(this, info, userIconInfo, apiWrapper, pmHelper);
     }
 
     public AppInfo(AppInfo info) {
@@ -184,7 +184,7 @@
      */
     public static boolean updateRuntimeFlagsForActivityTarget(
             ItemInfoWithIcon info, LauncherActivityInfo lai, UserIconInfo userIconInfo,
-            ApiWrapper apiWrapper) {
+            ApiWrapper apiWrapper, PackageManagerHelper pmHelper) {
         final int oldProgressLevel = info.getProgressLevel();
         final int oldRuntimeStatusFlags = info.runtimeStatusFlags;
         ApplicationInfo appInfo = lai.getApplicationInfo();
@@ -216,6 +216,8 @@
                 PackageManagerHelper.getLoadingProgress(lai),
                 PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
         info.setNonResizeable(apiWrapper.isNonResizeableActivity(lai));
+        info.setSupportsMultiInstance(
+                pmHelper.supportsMultiInstance(lai.getComponentName()));
         return (oldProgressLevel != info.getProgressLevel())
                 || (oldRuntimeStatusFlags != info.runtimeStatusFlags);
     }
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 72e85c7..b82d0a0 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -73,6 +73,7 @@
  * Represents an item in the launcher.
  */
 public class ItemInfo {
+    private static final String TAG = "ItemInfo";
 
     public static final boolean DEBUG = false;
     public static final int NO_ID = -1;
@@ -285,7 +286,7 @@
     @Override
     @NonNull
     public final String toString() {
-        return getClass().getSimpleName() + "(" + dumpProperties() + ")";
+        return TAG + "(" + dumpProperties() + ")";
     }
 
     @NonNull
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index d4c25cb..6ac44ff 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -127,6 +127,11 @@
     public static final int FLAG_NOT_RESIZEABLE = 1 << 15;
 
     /**
+     * Flag indicating whether the package related to the item & user supports multiple instances.
+     */
+    public static final int FLAG_SUPPORTS_MULTI_INSTANCE = 1 << 16;
+
+    /**
      * Status associated with the system state of the underlying item. This is calculated every
      * time a new info is created and not persisted on the disk.
      */
@@ -252,6 +257,24 @@
     }
 
     /**
+     * Sets whether this app info supports multi-instance.
+     */
+    protected void setSupportsMultiInstance(boolean supportsMultiInstance) {
+        if (supportsMultiInstance) {
+            runtimeStatusFlags |= FLAG_SUPPORTS_MULTI_INSTANCE;
+        } else {
+            runtimeStatusFlags &= ~FLAG_SUPPORTS_MULTI_INSTANCE;
+        }
+    }
+
+    /**
+     * Returns whether this app info supports multi-instance.
+     */
+    public boolean supportsMultiInstance() {
+        return (runtimeStatusFlags & FLAG_SUPPORTS_MULTI_INSTANCE) != 0;
+    }
+
+    /**
      * Sets whether this app info is non-resizeable.
      */
     public void setNonResizeable(boolean nonResizeable) {
@@ -301,4 +324,11 @@
         drawable.setIsDisabled(isDisabled());
         return drawable;
     }
+
+    @Override
+    protected String dumpProperties() {
+        return super.dumpProperties()
+                + " supportsMultiInstance=" + supportsMultiInstance()
+                + " nonResizeable=" + isNonResizeable();
+    }
 }
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index 2c533ac..40e3813 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.content.pm.ShortcutInfo;
 import android.text.TextUtils;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
 
@@ -186,9 +187,12 @@
         if (shortcutInfo.isEnabled()) {
             runtimeStatusFlags &= ~FLAG_DISABLED_BY_PUBLISHER;
         } else {
+            Log.w(TAG, "updateFromDeepShortcutInfo: Updated shortcut has been disabled. "
+                    + " package=" + shortcutInfo.getPackage()
+                    + " disabledReason=" + shortcutInfo.getDisabledReason());
             runtimeStatusFlags |= FLAG_DISABLED_BY_PUBLISHER;
         }
-        disabledMessage = shortcutInfo.getDisabledMessage();
+
         if (shortcutInfo.getDisabledReason() == ShortcutInfo.DISABLED_REASON_VERSION_LOWER) {
             runtimeStatusFlags |= FLAG_DISABLED_VERSION_LOWER;
         } else {
diff --git a/src/com/android/launcher3/pm/PackageInstallInfo.java b/src/com/android/launcher3/pm/PackageInstallInfo.java
index 1797c1f..23d3b61 100644
--- a/src/com/android/launcher3/pm/PackageInstallInfo.java
+++ b/src/com/android/launcher3/pm/PackageInstallInfo.java
@@ -22,6 +22,7 @@
 import androidx.annotation.NonNull;
 
 public final class PackageInstallInfo {
+    private static final String TAG = "PackageInstallInfo";
 
     public static final int STATUS_INSTALLED = 0;
     public static final int STATUS_INSTALLING = 1;
@@ -61,7 +62,7 @@
 
     @Override
     public String toString() {
-        return getClass().getSimpleName() + "(" + dumpProperties() + ")";
+        return TAG + "(" + dumpProperties() + ")";
     }
 
     private String dumpProperties() {
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index b7b557d..ed25186 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -101,6 +101,7 @@
         mUserChangeReceiver.register(mContext,
                 Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
                 Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
+                Intent.ACTION_MANAGED_PROFILE_REMOVED,
                 ACTION_PROFILE_ADDED,
                 ACTION_PROFILE_REMOVED,
                 ACTION_PROFILE_UNLOCKED,
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
index b992a92..3ae643e 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -44,6 +44,7 @@
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.PackageManagerHelper;
 
 /**
  * A set of utility methods for Launcher DB used for DB updates and migration.
@@ -107,9 +108,11 @@
         Cursor c = db.query(
                 Favorites.TABLE_NAME, null, "itemType = 1", null, null, null, null);
         UserManagerState ums = new UserManagerState();
+        PackageManagerHelper pmHelper = PackageManagerHelper.INSTANCE.get(context);
         ums.init(UserCache.INSTANCE.get(context),
                 context.getSystemService(UserManager.class));
-        LoaderCursor lc = new LoaderCursor(c, LauncherAppState.getInstance(context), ums, null);
+        LoaderCursor lc = new LoaderCursor(c, LauncherAppState.getInstance(context), ums, pmHelper,
+                null);
         IntSet deletedShortcuts = new IntSet();
 
         while (lc.moveToNext()) {
diff --git a/src/com/android/launcher3/responsive/HotseatSpecsProvider.kt b/src/com/android/launcher3/responsive/HotseatSpecsProvider.kt
index 7502a43..2c3035f 100644
--- a/src/com/android/launcher3/responsive/HotseatSpecsProvider.kt
+++ b/src/com/android/launcher3/responsive/HotseatSpecsProvider.kt
@@ -135,7 +135,7 @@
         hotseatQsbSpace.fixedSize + edgePadding.fixedSize <= maxAvailableSize
 
     private fun logError(message: String) {
-        Log.e(LOG_TAG, "${this::class.simpleName}#isValid - $message - $this")
+        Log.e(LOG_TAG, "$LOG_TAG #isValid - $message - $this")
     }
 
     companion object {
diff --git a/src/com/android/launcher3/responsive/ResponsiveCellSpecsProvider.kt b/src/com/android/launcher3/responsive/ResponsiveCellSpecsProvider.kt
index a4b25e5..ef9b7df 100644
--- a/src/com/android/launcher3/responsive/ResponsiveCellSpecsProvider.kt
+++ b/src/com/android/launcher3/responsive/ResponsiveCellSpecsProvider.kt
@@ -31,7 +31,7 @@
             groupOfSpecs
                 .onEach { group ->
                     check(group.widthSpecs.isEmpty() && group.heightSpecs.isNotEmpty()) {
-                        "${this::class.simpleName} is invalid, only heightSpecs are allowed - " +
+                        "$LOG_TAG is invalid, only heightSpecs are allowed - " +
                             "width list size = ${group.widthSpecs.size}; " +
                             "height list size = ${group.heightSpecs.size}."
                     }
@@ -65,6 +65,7 @@
     }
 
     companion object {
+        private const val LOG_TAG = "ResponsiveCellSpecsProvider"
         @JvmStatic
         fun create(resourceHelper: ResourceHelper): ResponsiveCellSpecsProvider {
             val parser = ResponsiveSpecsParser(resourceHelper)
@@ -137,11 +138,11 @@
     }
 
     private fun logError(message: String) {
-        Log.e(LOG_TAG, "${this::class.simpleName}#isValid - $message - $this")
+        Log.e(LOG_TAG, "$LOG_TAG#isValid - $message - $this")
     }
 
     companion object {
-        private const val LOG_TAG = "CellSpec"
+        const val LOG_TAG = "CellSpec"
     }
 }
 
@@ -182,6 +183,7 @@
     )
 
     companion object {
+        private const val LOG_TAG = "CalculatedCellSpec"
         private fun getCalculatedValue(
             availableSpace: Int,
             spec: SizeSpec,
@@ -191,10 +193,10 @@
     }
 
     override fun toString(): String {
-        return "${this::class.simpleName}(" +
+        return "$LOG_TAG(" +
             "availableSpace=$availableSpace, iconSize=$iconSize, " +
             "iconTextSize=$iconTextSize, iconDrawablePadding=$iconDrawablePadding, " +
-            "${spec::class.simpleName}.maxAvailableSize=${spec.maxAvailableSize}" +
+            "${CellSpec.LOG_TAG}.maxAvailableSize=${spec.maxAvailableSize}" +
             ")"
     }
 }
diff --git a/src/com/android/launcher3/responsive/ResponsiveSpec.kt b/src/com/android/launcher3/responsive/ResponsiveSpec.kt
index 65e0b32..e69324d 100644
--- a/src/com/android/launcher3/responsive/ResponsiveSpec.kt
+++ b/src/com/android/launcher3/responsive/ResponsiveSpec.kt
@@ -154,7 +154,7 @@
     }
 
     private fun logError(message: String) {
-        Log.e(LOG_TAG, "${this::class.simpleName}#isValid - $message - $this")
+        Log.e(LOG_TAG, "$LOG_TAG#isValid - $message - $this")
     }
 
     enum class DimensionType {
diff --git a/src/com/android/launcher3/responsive/ResponsiveSpecsProvider.kt b/src/com/android/launcher3/responsive/ResponsiveSpecsProvider.kt
index 67eaac0..654608d 100644
--- a/src/com/android/launcher3/responsive/ResponsiveSpecsProvider.kt
+++ b/src/com/android/launcher3/responsive/ResponsiveSpecsProvider.kt
@@ -40,7 +40,7 @@
             groupOfSpecs
                 .onEach { group ->
                     check(group.widthSpecs.isNotEmpty() && group.heightSpecs.isNotEmpty()) {
-                        "${this::class.simpleName} is incomplete - " +
+                        "$LOG_TAG is incomplete - " +
                             "width list size = ${group.widthSpecs.size}; " +
                             "height list size = ${group.heightSpecs.size}."
                     }
@@ -124,6 +124,7 @@
     }
 
     companion object {
+        private const val LOG_TAG = "ResponsiveSpecsProvider"
         @JvmStatic
         fun create(
             resourceHelper: ResourceHelper,
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 50f98f2..3817563 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -411,17 +411,29 @@
         mLauncher.getStatsLogManager().logger()
                 .withSrcState(mStartState.statsLogOrdinal)
                 .withDstState(targetState.statsLogOrdinal)
-                .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
-                        .setWorkspace(
-                                LauncherAtom.WorkspaceContainer.newBuilder()
-                                        .setPageIndex(mLauncher.getWorkspace().getCurrentPage()))
-                        .build())
+                .withContainerInfo(getContainerInfo(targetState))
                 .log(StatsLogManager.getLauncherAtomEvent(mStartState.statsLogOrdinal,
                             targetState.statsLogOrdinal, mToState.ordinal > mFromState.ordinal
                                     ? LAUNCHER_UNKNOWN_SWIPEUP
                                     : LAUNCHER_UNKNOWN_SWIPEDOWN));
     }
 
+    private LauncherAtom.ContainerInfo getContainerInfo(LauncherState targetState) {
+        if (targetState.isRecentsViewVisible) {
+            return LauncherAtom.ContainerInfo.newBuilder()
+                    .setTaskSwitcherContainer(
+                            LauncherAtom.TaskSwitcherContainer.getDefaultInstance()
+                    )
+                    .build();
+        }
+
+        return LauncherAtom.ContainerInfo.newBuilder()
+                .setWorkspace(
+                        LauncherAtom.WorkspaceContainer.newBuilder()
+                                .setPageIndex(mLauncher.getWorkspace().getCurrentPage()))
+                .build();
+    }
+
     protected void clearState() {
         cancelAnimationControllers();
         mGoingBetweenStates = true;
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 0ed6ea0..f46dcd3 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -84,7 +84,8 @@
  */
 public class ItemClickHandler {
 
-    private static final String TAG = ItemClickHandler.class.getSimpleName();
+    private static final String TAG = "ItemClickHandler";
+    private static final boolean DEBUG = true;
 
     /**
      * Instance used for click handling on items
@@ -110,7 +111,19 @@
             startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher);
         } else if (tag instanceof LauncherAppWidgetInfo) {
             if (v instanceof PendingAppWidgetHostView) {
+                if (DEBUG) {
+                    String targetPackage = ((LauncherAppWidgetInfo) tag).getTargetPackage();
+                    Log.d(TAG, "onClick: PendingAppWidgetHostView clicked for"
+                            + " package=" + targetPackage);
+                }
                 onClickPendingWidget((PendingAppWidgetHostView) v, launcher);
+            } else {
+                if (DEBUG) {
+                    String targetPackage = ((LauncherAppWidgetInfo) tag).getTargetPackage();
+                    Log.d(TAG, "onClick: LauncherAppWidgetInfo clicked,"
+                            + " but not instance of PendingAppWidgetHostView. Returning."
+                            + " package=" + targetPackage);
+                }
             }
         } else if (tag instanceof ItemClickProxy) {
             ((ItemClickProxy) tag).onItemClicked(v);
@@ -120,6 +133,10 @@
                     launcher.getString(R.string.long_accessible_way_to_add_shortcut));
             Snackbar.show(launcher, msg, null);
         } else if (tag instanceof PendingAddWidgetInfo) {
+            if (DEBUG) {
+                String targetPackage = ((PendingAddWidgetInfo) tag).getTargetPackage();
+                Log.d(TAG, "onClick: PendingAddWidgetInfo clicked for package=" + targetPackage);
+            }
             CharSequence msg = Utilities.wrapForTts(
                     launcher.getText(R.string.long_press_widget_to_add),
                     launcher.getString(R.string.long_accessible_way_to_add));
@@ -199,6 +216,9 @@
             LauncherAppWidgetProviderInfo appWidgetInfo = new WidgetManagerHelper(launcher)
                     .findProvider(info.providerName, info.user);
             if (appWidgetInfo == null) {
+                Log.e(TAG, "onClickPendingWidget: Pending widget ready for click setup,"
+                        + " but LauncherAppWidgetProviderInfo was null. Returning."
+                        + " component=" + info.getTargetComponent());
                 return;
             }
             WidgetAddFlowHandler addFlowHandler = new WidgetAddFlowHandler(appWidgetInfo);
@@ -206,6 +226,10 @@
             if (info.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
                 if (!info.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
                     // This should not happen, as we make sure that an Id is allocated during bind.
+                    Log.e(TAG, "onClickPendingWidget: Pending widget ready for click setup,"
+                            + " and LauncherAppWidgetProviderInfo was found. However,"
+                            + " no appWidgetId was allocated. Returning."
+                            + " component=" + info.getTargetComponent());
                     return;
                 }
                 addFlowHandler.startBindFlow(launcher, info.appWidgetId, info,
diff --git a/src/com/android/launcher3/util/FlagDebugUtils.kt b/src/com/android/launcher3/util/FlagDebugUtils.kt
index f281943..33b8330 100644
--- a/src/com/android/launcher3/util/FlagDebugUtils.kt
+++ b/src/com/android/launcher3/util/FlagDebugUtils.kt
@@ -2,6 +2,7 @@
 
 import java.util.StringJoiner
 import java.util.function.IntFunction
+import java.util.function.LongFunction
 
 object FlagDebugUtils {
 
@@ -12,6 +13,13 @@
             str.add(flagName)
         }
     }
+    /** Appends the [flagName] to [str] when the [flag] is set in [flags]. */
+    @JvmStatic
+    fun appendFlag(str: StringJoiner, flags: Long, flag: Long, flagName: String) {
+        if (flags and flag != 0L) {
+            str.add(flagName)
+        }
+    }
 
     /**
      * Produces a human-readable representation of the [current] flags, followed by a diff from from
@@ -34,4 +42,30 @@
         }
         return result.toString()
     }
+
+    /**
+     * Produces a human-readable representation of the [current] flags, followed by a diff from from
+     * [previous].
+     *
+     * The resulting string is intented for logging and debugging.
+     */
+    @JvmStatic
+    fun formatFlagChange(
+        current: Long,
+        previous: Long,
+        flagSerializer: LongFunction<String>
+    ): String {
+        val result = StringJoiner(" ")
+        result.add("[" + flagSerializer.apply(current) + "]")
+        val changed = current xor previous
+        val added = current and changed
+        if (added != 0L) {
+            result.add("+[" + flagSerializer.apply(added) + "]")
+        }
+        val removed = previous and changed
+        if (removed != 0L) {
+            result.add("-[" + flagSerializer.apply(removed) + "]")
+        }
+        return result.toString()
+    }
 }
diff --git a/src/com/android/launcher3/util/LauncherLayoutBuilder.kt b/src/com/android/launcher3/util/LauncherLayoutBuilder.kt
new file mode 100644
index 0000000..ecc9953
--- /dev/null
+++ b/src/com/android/launcher3/util/LauncherLayoutBuilder.kt
@@ -0,0 +1,200 @@
+/*
+ * 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.launcher3.util
+
+import android.util.Xml
+import com.android.launcher3.AutoInstallsLayout.ATTR_CLASS_NAME
+import com.android.launcher3.AutoInstallsLayout.ATTR_CONTAINER
+import com.android.launcher3.AutoInstallsLayout.ATTR_PACKAGE_NAME
+import com.android.launcher3.AutoInstallsLayout.ATTR_RANK
+import com.android.launcher3.AutoInstallsLayout.ATTR_SCREEN
+import com.android.launcher3.AutoInstallsLayout.ATTR_SHORTCUT_ID
+import com.android.launcher3.AutoInstallsLayout.ATTR_SPAN_X
+import com.android.launcher3.AutoInstallsLayout.ATTR_SPAN_Y
+import com.android.launcher3.AutoInstallsLayout.ATTR_TITLE
+import com.android.launcher3.AutoInstallsLayout.ATTR_TITLE_TEXT
+import com.android.launcher3.AutoInstallsLayout.ATTR_USER_TYPE
+import com.android.launcher3.AutoInstallsLayout.ATTR_X
+import com.android.launcher3.AutoInstallsLayout.ATTR_Y
+import com.android.launcher3.AutoInstallsLayout.TAG_APPWIDGET
+import com.android.launcher3.AutoInstallsLayout.TAG_AUTO_INSTALL
+import com.android.launcher3.AutoInstallsLayout.TAG_FOLDER
+import com.android.launcher3.AutoInstallsLayout.TAG_SHORTCUT
+import com.android.launcher3.AutoInstallsLayout.TAG_WORKSPACE
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
+import com.android.launcher3.LauncherSettings.Favorites.containerToString
+import java.io.IOException
+import java.io.StringWriter
+import java.io.Writer
+import org.xmlpull.v1.XmlSerializer
+
+/** Helper class to build xml for Launcher Layout */
+class LauncherLayoutBuilder {
+    private val nodes = ArrayList<Node>()
+
+    fun atHotseat(rank: Int) =
+        ItemTarget(
+            mapOf(
+                ATTR_CONTAINER to containerToString(CONTAINER_HOTSEAT),
+                ATTR_RANK to rank.toString()
+            )
+        )
+
+    fun atWorkspace(x: Int, y: Int, screen: Int) =
+        ItemTarget(
+            mapOf(
+                ATTR_CONTAINER to containerToString(CONTAINER_DESKTOP),
+                ATTR_X to x.toString(),
+                ATTR_Y to y.toString(),
+                ATTR_SCREEN to screen.toString()
+            )
+        )
+
+    @Throws(IOException::class) fun build() = StringWriter().apply { build(this) }.toString()
+
+    @Throws(IOException::class)
+    fun build(writer: Writer) {
+        Xml.newSerializer().apply {
+            setOutput(writer)
+            setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true)
+            startDocument("UTF-8", true)
+            startTag(null, TAG_WORKSPACE)
+            writeNodes(nodes)
+            endTag(null, TAG_WORKSPACE)
+            endDocument()
+            flush()
+        }
+    }
+
+    open inner class ItemTarget(private val baseValues: Map<String, String>) {
+        @JvmOverloads
+        fun putApp(packageName: String, className: String?, userType: String? = null) =
+            addItem(
+                TAG_AUTO_INSTALL,
+                userType,
+                mapOf(
+                    ATTR_PACKAGE_NAME to packageName,
+                    ATTR_CLASS_NAME to (className ?: packageName)
+                )
+            )
+
+        @JvmOverloads
+        fun putShortcut(packageName: String, shortcutId: String, userType: String? = null) =
+            addItem(
+                TAG_SHORTCUT,
+                userType,
+                mapOf(ATTR_PACKAGE_NAME to packageName, ATTR_SHORTCUT_ID to shortcutId)
+            )
+
+        @JvmOverloads
+        fun putWidget(
+            packageName: String,
+            className: String,
+            spanX: Int,
+            spanY: Int,
+            userType: String? = null
+        ) =
+            addItem(
+                TAG_APPWIDGET,
+                userType,
+                mapOf(
+                    ATTR_PACKAGE_NAME to packageName,
+                    ATTR_CLASS_NAME to className,
+                    ATTR_SPAN_X to spanX.toString(),
+                    ATTR_SPAN_Y to spanY.toString()
+                )
+            )
+
+        fun putFolder(titleResId: Int) = putFolder(ATTR_TITLE, titleResId.toString())
+
+        fun putFolder(title: String?) = putFolder(ATTR_TITLE_TEXT, title)
+
+        protected open fun addItem(
+            tag: String,
+            userType: String?,
+            props: Map<String, String>,
+            children: List<Node>? = null
+        ): LauncherLayoutBuilder {
+            nodes.add(
+                Node(
+                    tag,
+                    HashMap(baseValues).apply {
+                        putAll(props)
+                        userType?.let { put(ATTR_USER_TYPE, it) }
+                    },
+                    children
+                )
+            )
+            return this@LauncherLayoutBuilder
+        }
+
+        protected open fun putFolder(titleKey: String, titleValue: String?): FolderBuilder {
+            val folderBuilder = FolderBuilder()
+            addItem(TAG_FOLDER, null, mapOf(titleKey to (titleValue ?: "")), folderBuilder.children)
+            return folderBuilder
+        }
+    }
+
+    inner class FolderBuilder : ItemTarget(mapOf()) {
+
+        val children = ArrayList<Node>()
+
+        fun addApp(packageName: String, className: String?): FolderBuilder {
+            putApp(packageName, className)
+            return this
+        }
+
+        fun addShortcut(packageName: String, shortcutId: String): FolderBuilder {
+            putShortcut(packageName, shortcutId)
+            return this
+        }
+
+        override fun addItem(
+            tag: String,
+            userType: String?,
+            props: Map<String, String>,
+            childrenIgnored: List<Node>?
+        ): LauncherLayoutBuilder {
+            children.add(
+                Node(tag, HashMap(props).apply { userType?.let { put(ATTR_USER_TYPE, it) } })
+            )
+            return this@LauncherLayoutBuilder
+        }
+
+        override fun putFolder(titleKey: String, titleValue: String?): FolderBuilder {
+            throw IllegalArgumentException("Can't have folder inside a folder")
+        }
+
+        fun build() = this@LauncherLayoutBuilder
+    }
+
+    @Throws(IOException::class)
+    private fun XmlSerializer.writeNodes(nodes: List<Node>) {
+        nodes.forEach { node ->
+            startTag(null, node.name)
+            node.attrs.forEach { (key, value) -> attribute(null, key, value) }
+            node.children?.let { writeNodes(it) }
+            endTag(null, node.name)
+        }
+    }
+
+    data class Node(
+        val name: String,
+        val attrs: Map<String, String>,
+        val children: List<Node>? = null
+    )
+}
diff --git a/src/com/android/launcher3/util/LogConfig.java b/src/com/android/launcher3/util/LogConfig.java
index d59c339..3d4b409 100644
--- a/src/com/android/launcher3/util/LogConfig.java
+++ b/src/com/android/launcher3/util/LogConfig.java
@@ -70,4 +70,10 @@
      * When turned on, we enable long press nav handle related logging.
      */
     public static final String NAV_HANDLE_LONG_PRESS = "NavHandleLongPress";
+
+
+    /**
+     * When turned on, we enable zero state web data loader related logging.
+     */
+    public static final String ZERO_WEB_DATA_LOADER = "ZeroStateWebDataLoaderLog";
 }
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 3684f56..f7c4df4 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.util;
 
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
+import static android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI;
 
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
@@ -73,10 +74,14 @@
     @NonNull
     private final LauncherApps mLauncherApps;
 
+    private final String[] mLegacyMultiInstanceSupportedApps;
+
     public PackageManagerHelper(@NonNull final Context context) {
         mContext = context;
         mPm = context.getPackageManager();
         mLauncherApps = Objects.requireNonNull(context.getSystemService(LauncherApps.class));
+        mLegacyMultiInstanceSupportedApps = mContext.getResources().getStringArray(
+                R.array.config_appsSupportMultiInstancesSplit);
     }
 
     @Override
@@ -159,11 +164,23 @@
         }
     }
 
+    /**
+     * Returns the preferred launch activity intent for a given package.
+     */
     @Nullable
     public Intent getAppLaunchIntent(@Nullable final String pkg, @NonNull final UserHandle user) {
+        LauncherActivityInfo info = getAppLaunchInfo(pkg, user);
+        return info != null ? AppInfo.makeLaunchIntent(info) : null;
+    }
+
+    /**
+     * Returns the preferred launch activity for a given package.
+     */
+    @Nullable
+    public LauncherActivityInfo getAppLaunchInfo(@Nullable final String pkg,
+            @NonNull final UserHandle user) {
         List<LauncherActivityInfo> activities = mLauncherApps.getActivityList(pkg, user);
-        return activities.isEmpty() ? null :
-                AppInfo.makeLaunchIntent(activities.get(0));
+        return activities.isEmpty() ? null : activities.get(0);
     }
 
     /**
@@ -285,4 +302,47 @@
         return (info.flags & ApplicationInfo.FLAG_INSTALLED) != 0 || (
                 Flags.enableSupportForArchiving() && info.isArchived);
     }
+
+    /**
+     * Returns whether the given component or its application has the multi-instance property set.
+     */
+    public boolean supportsMultiInstance(@NonNull ComponentName component) {
+        // Check the legacy hardcoded allowlist first
+        for (String pkg : mLegacyMultiInstanceSupportedApps) {
+            if (pkg.equals(component.getPackageName())) {
+                return true;
+            }
+        }
+
+        // Check app multi-instance properties after V
+        if (!Utilities.ATLEAST_V) {
+            return false;
+        }
+
+        try {
+            // Check if the component has the multi-instance property
+            return mPm.getProperty(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, component)
+                    .getBoolean();
+        } catch (PackageManager.NameNotFoundException e1) {
+            try {
+                // Check if the application has the multi-instance property
+                return mPm.getProperty(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI,
+                                component.getPackageName())
+                    .getBoolean();
+            } catch (PackageManager.NameNotFoundException e2) {
+                // Fall through
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns whether two apps should be considered the same for multi-instance purposes, which
+     * requires additional checks to ensure they can be started as multiple instances.
+     */
+    public static boolean isSameAppForMultiInstance(@NonNull ItemInfo app1,
+            @NonNull ItemInfo app2) {
+        return app1.getTargetPackage().equals(app2.getTargetPackage())
+                && app1.user.equals(app2.user);
+    }
 }
diff --git a/src/com/android/launcher3/util/rects/Rects.kt b/src/com/android/launcher3/util/rects/Rects.kt
new file mode 100644
index 0000000..1e6d717
--- /dev/null
+++ b/src/com/android/launcher3/util/rects/Rects.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.util.rects
+
+import android.graphics.Rect
+import android.view.View
+
+/** Copy the coordinates of the [view] relative to its parent into this rectangle. */
+fun Rect.set(view: View) {
+    set(0, 0, view.width, view.height)
+    offset(view.x.toInt(), view.y.toInt())
+}
diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java
index 2f0da03..bb4f040 100644
--- a/src/com/android/launcher3/views/ArrowTipView.java
+++ b/src/com/android/launcher3/views/ArrowTipView.java
@@ -54,7 +54,7 @@
  */
 public class ArrowTipView extends AbstractFloatingView {
 
-    private static final String TAG = ArrowTipView.class.getSimpleName();
+    private static final String TAG = "ArrowTipView";
     private static final long AUTO_CLOSE_TIMEOUT_MILLIS = 10 * 1000;
     private static final long SHOW_DELAY_MS = 200;
     private static final long SHOW_DURATION_MS = 300;
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 0d07f63..1d5a9dc 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -68,7 +68,7 @@
 public class FloatingIconView extends FrameLayout implements
         Animator.AnimatorListener, OnGlobalLayoutListener, FloatingView {
 
-    private static final String TAG = FloatingIconView.class.getSimpleName();
+    private static final String TAG = "FloatingIconView";
 
     // Manages loading the icon on a worker thread
     private static @Nullable IconLoadResult sIconLoadResult;
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 8892a18..1368084 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -17,6 +17,8 @@
 
 import static com.android.app.animation.Interpolators.EMPHASIZED;
 import static com.android.launcher3.Flags.enableWidgetTapToAdd;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_ADD_BUTTON_TAP;
 
 import android.content.Context;
@@ -42,6 +44,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.TestProtocol;
@@ -74,6 +77,7 @@
     private boolean mDisableNavBarScrim = false;
 
     @Nullable private WidgetCell mWidgetCellWithAddButton = null;
+    @Nullable private WidgetItem mLastSelectedWidgetItem = null;
 
     public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
@@ -161,13 +165,26 @@
             }
 
             mWidgetCellWithAddButton = mWidgetCellWithAddButton != wc ? wc : null;
+            if (mWidgetCellWithAddButton != null) {
+                mLastSelectedWidgetItem = mWidgetCellWithAddButton.getWidgetItem();
+            } else {
+                mLastSelectedWidgetItem = null;
+            }
         } else {
             mActivityContext.getItemOnClickListener().onClick(wc);
         }
     }
 
+    @Override
+    protected float getShiftRange() {
+        // We add the extra height added during predictive back / swipe up to the shift range, so
+        // that the idle interpolator knows to animate the view off fully.
+        return mContent.getHeight() + getBottomOffsetPx();
+    }
+
     /**
-     * Click handler for tap to add button.
+     * Click handler for tap to add button. This handler assumes we are in the Launcher activity and
+     * should not be used when the widget sheet is displayed elsewhere.
      */
     private void addWidget(@NonNull PendingAddItemInfo info) {
         // Using a boolean flag here to make sure the callback is only run once. This should never
@@ -175,19 +192,23 @@
         // needed.
         final AtomicBoolean hasRun = new AtomicBoolean(false);
         addOnCloseListener(() -> {
-            if (!hasRun.get()) {
-                Launcher.getLauncher(mActivityContext).getAccessibilityDelegate().addToWorkspace(
-                        info, /*accessibility=*/ false,
+            if (hasRun.get()) return;
+            hasRun.set(true);
+
+            // Going to NORMAL state will also dismiss the All Apps view if it is showing.
+            Launcher launcher = Launcher.getLauncher(mActivityContext);
+            launcher.getStateManager().goToState(NORMAL, forSuccessCallback(() -> {
+                launcher.getAccessibilityDelegate().addToWorkspace(info,
+                        /*accessibility=*/ false,
                         /*finishCallback=*/ (success) -> {
                             mActivityContext.getStatsLogManager()
                                     .logger()
                                     .withItemInfo(info)
                                     .log(LAUNCHER_WIDGET_ADD_BUTTON_TAP);
                         });
-                hasRun.set(true);
-            }
+            }));
         });
-        handleClose(true);
+        close(/* animate= */ true);
     }
 
     /**
@@ -236,6 +257,14 @@
         return 0;
     }
 
+    /**
+     * Returns the component of the widget that is currently showing an add button, if any.
+     */
+    @Nullable
+    protected WidgetItem getLastSelectedWidgetItem() {
+        return mLastSelectedWidgetItem;
+    }
+
     @Override
     public boolean onLongClick(View v) {
         TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Widgets.onLongClick");
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 5dacfb0..eac2ce7 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -503,6 +503,15 @@
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     }
 
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+
+        if (changed && isShowingAddButton()) {
+            post(this::setupIconOrTextButton);
+        }
+    }
+
     /**
      * Loads a high resolution package icon to show next to the widget title.
      */
@@ -627,4 +636,19 @@
         set.playSequentially(hideAnim, showAnim);
         set.start();
     }
+
+    /**
+     * Returns true if this WidgetCell is displaying the same item as info.
+     */
+    public boolean matchesItem(WidgetItem info) {
+        if (info == null || mItem == null) return false;
+        if (info.widgetInfo != null && mItem.widgetInfo != null) {
+            return info.widgetInfo.getUser().equals(mItem.widgetInfo.getUser())
+                    && info.widgetInfo.getComponent().equals(mItem.widgetInfo.getComponent());
+        } else if (info.activityInfo != null && mItem.activityInfo != null) {
+            return info.activityInfo.getUser().equals(mItem.activityInfo.getUser())
+                    && info.activityInfo.getComponent().equals(mItem.activityInfo.getComponent());
+        }
+        return false;
+    }
 }
diff --git a/src/com/android/launcher3/widget/WidgetManagerHelper.java b/src/com/android/launcher3/widget/WidgetManagerHelper.java
index d293d15..9132b4f 100644
--- a/src/com/android/launcher3/widget/WidgetManagerHelper.java
+++ b/src/com/android/launcher3/widget/WidgetManagerHelper.java
@@ -26,6 +26,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.util.Log;
 import android.widget.RemoteViews;
 
 import androidx.annotation.NonNull;
@@ -104,6 +105,8 @@
             // If exception is thrown because of device is locked, it means a race condition occurs
             // that the user got locked again while launcher is processing the event. In this case
             // we should return empty list.
+            Log.e(TAG, "getAllProviders: Error getting installed providers for"
+                    + " package=" + packageUser.mPackageName, e);
             return Collections.emptyList();
         }
     }
@@ -133,6 +136,8 @@
                 return LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
             }
         }
+        Log.w(TAG, "findProvider: No App Widget Provider found for component=" + provider
+                + " user=" + user);
         return null;
     }
 
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index b4c4623..4ea2426 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -142,6 +142,9 @@
                     row.forEach(widgetItem -> {
                         WidgetCell widget = addItemCell(tableRow);
                         widget.applyFromCellItem(widgetItem);
+                        if (widget.matchesItem(getLastSelectedWidgetItem())) {
+                            widget.callOnClick();
+                        }
                     });
                     widgetsTable.addView(tableRow);
                 });
diff --git a/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java b/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java
index 80bda22..9253b37 100644
--- a/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java
+++ b/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java
@@ -64,14 +64,11 @@
         Preconditions.assertWorkerThread();
         try (PackageManagerHelper pmHelper = new PackageManagerHelper(context)) {
             if (item.widgetInfo != null && item.widgetInfo.getComponent() != null) {
-                String widgetComponentName = item.widgetInfo.getComponent().getClassName();
                 ApplicationInfo applicationInfo = pmHelper.getApplicationInfo(
                         item.widgetInfo.getComponent().getPackageName(), item.widgetInfo.getUser(),
                         0 /* flags */);
                 if (applicationInfo != null) {
-                    int predictionCategory = applicationInfo.category;
-                    return getCategoryFromApplicationCategory(context, predictionCategory,
-                            widgetComponentName);
+                    return getCategoryFromApplicationCategory(applicationInfo.category);
                 }
             }
         }
@@ -80,29 +77,7 @@
 
     /** Maps application category to an appropriate displayable category. */
     private static WidgetRecommendationCategory getCategoryFromApplicationCategory(
-            Context context, int applicationCategory, String componentName) {
-        // Weather categories don't map to a specific application category, so, we maintain an
-        // allowlist.
-        String[] weatherRecommendationAllowlist =
-                context.getResources().getStringArray(R.array.weather_recommendations);
-        for (String allowedWeatherComponentName : weatherRecommendationAllowlist) {
-            if (componentName.equalsIgnoreCase(allowedWeatherComponentName)) {
-                return new WidgetRecommendationCategory(
-                        R.string.weather_widget_recommendation_category_label, /*order=*/3);
-            }
-        }
-
-        // Fitness categories don't map to a specific application category, so, we maintain an
-        // allowlist.
-        String[] fitnessRecommendationAllowlist =
-                context.getResources().getStringArray(R.array.fitness_recommendations);
-        for (String allowedFitnessComponentName : fitnessRecommendationAllowlist) {
-            if (componentName.equalsIgnoreCase(allowedFitnessComponentName)) {
-                return new WidgetRecommendationCategory(
-                        R.string.fitness_widget_recommendation_category_label, /*order=*/2);
-            }
-        }
-
+            int applicationCategory) {
         if (applicationCategory == ApplicationInfo.CATEGORY_PRODUCTIVITY) {
             return new WidgetRecommendationCategory(
                     R.string.productivity_widget_recommendation_category_label, /*order=*/0);
@@ -116,7 +91,7 @@
         if (applicationCategory == ApplicationInfo.CATEGORY_SOCIAL) {
             return new WidgetRecommendationCategory(
                     R.string.social_widget_recommendation_category_label,
-                    /*order=*/5);
+                    /*order=*/3);
         }
 
         if (applicationCategory == ApplicationInfo.CATEGORY_AUDIO
@@ -124,11 +99,10 @@
                 || applicationCategory == ApplicationInfo.CATEGORY_IMAGE) {
             return new WidgetRecommendationCategory(
                     R.string.entertainment_widget_recommendation_category_label,
-                    /*order=*/6);
+                    /*order=*/4);
         }
 
         return new WidgetRecommendationCategory(
-                R.string.others_widget_recommendation_category_label, /*order=*/4);
+                R.string.others_widget_recommendation_category_label, /*order=*/2);
     }
-
 }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index 5292ee2..f835e18 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -29,17 +29,20 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.Px;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.recyclerview.ViewHolderBinder;
 import com.android.launcher3.util.PackageUserKey;
@@ -76,6 +79,7 @@
 
     private boolean mOldIsSwipeToDismissInProgress;
     private int mActivePage = -1;
+    @Nullable
     private PackageUserKey mSelectedHeader;
 
     public WidgetsTwoPaneSheet(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -194,15 +198,21 @@
                 layoutParams.width = 0;
             }
             layoutParams.weight = layoutParams.width == 0 ? 0.33F : 0;
-            leftPane.setLayoutParams(layoutParams);
-            requestApplyInsets();
-            if (mSelectedHeader != null) {
-                if (mSelectedHeader.equals(mSuggestedWidgetsPackageUserKey)) {
-                    mSuggestedWidgetsHeader.callOnClick();
-                } else {
-                    getHeaderChangeListener().onHeaderChanged(mSelectedHeader);
+
+            post(() -> {
+                // The following calls all trigger requestLayout, so we post them to avoid
+                // calling requestLayout during a layout pass. This also fixes the related warnings
+                // in logcat.
+                leftPane.setLayoutParams(layoutParams);
+                requestApplyInsets();
+                if (mSelectedHeader != null) {
+                    if (mSelectedHeader.equals(mSuggestedWidgetsPackageUserKey)) {
+                        mSuggestedWidgetsHeader.callOnClick();
+                    } else {
+                        getHeaderChangeListener().onHeaderChanged(mSelectedHeader);
+                    }
                 }
-            }
+            });
         }
     }
 
@@ -222,6 +232,10 @@
         if (mSuggestedWidgetsContainer == null && mRecommendedWidgetsCount > 0) {
             setupSuggestedWidgets(LayoutInflater.from(getContext()));
             mSuggestedWidgetsHeader.callOnClick();
+        } else if (mSelectedHeader != null
+                && mSelectedHeader.equals(mSuggestedWidgetsPackageUserKey)) {
+            // Reselect widget if we are reloading recommendations while it is currently showing.
+            selectWidgetCell(mWidgetRecommendationsContainer, getLastSelectedWidgetItem());
         }
     }
 
@@ -269,6 +283,16 @@
             mRightPaneScrollView.setScrollY(0);
             mRightPane.setAccessibilityPaneTitle(suggestionsRightPaneTitle);
             mSuggestedWidgetsPackageUserKey = PackageUserKey.fromPackageItemInfo(packageItemInfo);
+            final boolean isChangingHeaders = mSelectedHeader == null
+                    || !mSelectedHeader.equals(mSuggestedWidgetsPackageUserKey);
+            if (isChangingHeaders)  {
+                // If switching from another header, unselect any WidgetCells. This is necessary
+                // because we do not clear/recycle the WidgetCells in the recommendations container
+                // when the header is clicked, only when onRecommendationsBound is called. That
+                // means a WidgetCell in the recommendations container may still be selected from
+                // the last time the recommendations were shown.
+                unselectWidgetCell(mWidgetRecommendationsContainer, getLastSelectedWidgetItem());
+            }
             mSelectedHeader = mSuggestedWidgetsPackageUserKey;
         });
         mSuggestedWidgetsContainer.addView(mSuggestedWidgetsHeader);
@@ -357,6 +381,8 @@
         return new HeaderChangeListener() {
             @Override
             public void onHeaderChanged(@NonNull PackageUserKey selectedHeader) {
+                final boolean isSameHeader = mSelectedHeader != null
+                        && mSelectedHeader.equals(selectedHeader);
                 mSelectedHeader = selectedHeader;
                 WidgetsListContentEntry contentEntry = mActivityContext.getPopupDataProvider()
                         .getSelectedAppWidgets(selectedHeader);
@@ -384,11 +410,20 @@
                         contentEntryToBind,
                         ViewHolderBinder.POSITION_FIRST | ViewHolderBinder.POSITION_LAST,
                         Collections.EMPTY_LIST);
+                if (isSameHeader) {
+                    // Reselect the last selected widget if we are reloading the same header.
+                    selectWidgetCell(widgetsRowViewHolder.tableContainer,
+                            getLastSelectedWidgetItem());
+                }
                 widgetsRowViewHolder.mDataCallback = data -> {
                     mWidgetsListTableViewHolderBinder.bindViewHolder(widgetsRowViewHolder,
                             contentEntryToBind,
                             ViewHolderBinder.POSITION_FIRST | ViewHolderBinder.POSITION_LAST,
                             Collections.singletonList(data));
+                    if (isSameHeader) {
+                        selectWidgetCell(widgetsRowViewHolder.tableContainer,
+                                getLastSelectedWidgetItem());
+                    }
                 };
                 mRightPane.removeAllViews();
                 mRightPane.addView(widgetsRowViewHolder.itemView);
@@ -401,6 +436,24 @@
         };
     }
 
+    private static void selectWidgetCell(ViewGroup parent, WidgetItem item) {
+        if (parent == null || item == null) return;
+        WidgetCell cell = Utilities.findViewByPredicate(parent, v -> v instanceof WidgetCell wc
+                && wc.matchesItem(item));
+        if (cell != null && !cell.isShowingAddButton()) {
+            cell.callOnClick();
+        }
+    }
+
+    private static void unselectWidgetCell(ViewGroup parent, WidgetItem item) {
+        if (parent == null || item == null) return;
+        WidgetCell cell = Utilities.findViewByPredicate(parent, v -> v instanceof WidgetCell wc
+                && wc.matchesItem(item));
+        if (cell != null && cell.isShowingAddButton()) {
+            cell.hideAddButton(/* animate= */ false);
+        }
+    }
+
     @Override
     public void setInsets(Rect insets) {
         super.setInsets(insets);
diff --git a/tests/Android.bp b/tests/Android.bp
index 5ec2263..cc0021b 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -105,6 +105,7 @@
 android_library {
     name: "Launcher3TestResources",
     resource_dirs: ["res"],
+    asset_dirs: ["assets"],
     // TODO(b/319712088): re-enable use_resource_processor
     use_resource_processor: false,
 }
@@ -198,8 +199,8 @@
         "androidx.test.ext.junit",
         "androidx.test.rules",
         "uiautomator-helpers",
-        "mockito-robolectric-prebuilt",
-        "mockito-kotlin2",
+        "inline-mockito-robolectric-prebuilt",
+        "mockito-kotlin-nodeps",
         "platform-parametric-runner-lib",
         "testables",
         "Launcher3TestResources",
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index a9b75ea..4b926a8 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -407,10 +407,14 @@
             </intent-filter>
         </activity>
 
-        <!-- [b/197780098] Disable eager initialization of Jetpack libraries. -->
+        <!-- Disable eager initialization of Jetpack libraries. See bug 197780098. -->
         <provider
             android:name="androidx.startup.InitializationProvider"
             android:authorities="${applicationId}.androidx-startup"
             tools:node="remove" />
+
+        <property
+            android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"
+            android:value="true"/>
     </application>
 </manifest>
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape3Button_decoupleDepth.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape3Button_decoupleDepth.txt
new file mode 100644
index 0000000..46cce24
--- /dev/null
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape3Button_decoupleDepth.txt
@@ -0,0 +1,130 @@
+DeviceProfile:
+	1 dp = 2.625 px
+	isTablet:true
+	isPhone:false
+	transposeLayoutWithOrientation:false
+	isGestureMode:false
+	isLandscape:true
+	isMultiWindowMode:false
+	isTwoPanels:true
+	isLeftRightSplit:true
+	windowX: 0.0px (0.0dp)
+	windowY: 0.0px (0.0dp)
+	widthPx: 2208.0px (841.1429dp)
+	heightPx: 1840.0px (700.9524dp)
+	availableWidthPx: 2208.0px (841.1429dp)
+	availableHeightPx: 1730.0px (659.0476dp)
+	mInsets.left: 0.0px (0.0dp)
+	mInsets.top: 110.0px (41.904762dp)
+	mInsets.right: 0.0px (0.0dp)
+	mInsets.bottom: 0.0px (0.0dp)
+	aspectRatio:1.2
+	isResponsiveGrid:false
+	isScalableGrid:false
+	inv.numRows: 4
+	inv.numColumns: 4
+	inv.numSearchContainerColumns: 4
+	minCellSize: PointF(0.0, 0.0)dp
+	cellWidthPx: 154.0px (58.666668dp)
+	cellHeightPx: 218.0px (83.04762dp)
+	getCellSize().x: 270.0px (102.85714dp)
+	getCellSize().y: 342.0px (130.28572dp)
+	cellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)
+	cellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)
+	cellLayoutPaddingPx.left: 0.0px (0.0dp)
+	cellLayoutPaddingPx.top: 0.0px (0.0dp)
+	cellLayoutPaddingPx.right: 0.0px (0.0dp)
+	cellLayoutPaddingPx.bottom: 0.0px (0.0dp)
+	iconSizePx: 141.0px (53.714287dp)
+	iconTextSizePx: 34.0px (12.952381dp)
+	iconDrawablePaddingPx: 13.0px (4.952381dp)
+	numFolderRows: 3
+	numFolderColumns: 4
+	folderCellWidthPx: 189.0px (72.0dp)
+	folderCellHeightPx: 219.0px (83.42857dp)
+	folderChildIconSizePx: 141.0px (53.714287dp)
+	folderChildTextSizePx: 34.0px (12.952381dp)
+	folderChildDrawablePaddingPx: 5.0px (1.9047619dp)
+	folderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)
+	folderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)
+	folderContentPaddingLeftRight: 21.0px (8.0dp)
+	folderTopPadding: 63.0px (24.0dp)
+	folderFooterHeight: 147.0px (56.0dp)
+	bottomSheetTopPadding: 110.0px (41.904762dp)
+	bottomSheetOpenDuration: 500
+	bottomSheetCloseDuration: 500
+	bottomSheetWorkspaceScale: 0.97
+	bottomSheetDepth: 0.3
+	allAppsShiftRange: 1840.0px (700.9524dp)
+	allAppsOpenDuration: 500
+	allAppsCloseDuration: 500
+	allAppsIconSizePx: 141.0px (53.714287dp)
+	allAppsIconTextSizePx: 34.0px (12.952381dp)
+	allAppsIconDrawablePaddingPx: 21.0px (8.0dp)
+	allAppsCellHeightPx: 361.0px (137.5238dp)
+	allAppsCellWidthPx: 183.0px (69.71429dp)
+	allAppsBorderSpacePxX: 42.0px (16.0dp)
+	allAppsBorderSpacePxY: 42.0px (16.0dp)
+	numShownAllAppsColumns: 8
+	allAppsPadding.top: 110.0px (41.904762dp)
+	allAppsPadding.left: 42.0px (16.0dp)
+	allAppsPadding.right: 42.0px (16.0dp)
+	allAppsLeftRightMargin: 183.0px (69.71429dp)
+	hotseatBarSizePx: 267.0px (101.71429dp)
+	mHotseatColumnSpan: 4
+	mHotseatWidthPx: 0.0px (0.0dp)
+	hotseatCellHeightPx: 159.0px (60.57143dp)
+	hotseatBarBottomSpacePx: 126.0px (48.0dp)
+	mHotseatBarEdgePaddingPx: 0.0px (0.0dp)
+	mHotseatBarWorkspaceSpacePx: 0.0px (0.0dp)
+	hotseatBarEndOffset: 0.0px (0.0dp)
+	hotseatQsbSpace: 0.0px (0.0dp)
+	hotseatQsbHeight: 0.0px (0.0dp)
+	springLoadedHotseatBarTopMarginPx: 116.0px (44.190475dp)
+	getHotseatLayoutPadding(context).top: 0.0px (0.0dp)
+	getHotseatLayoutPadding(context).bottom: 108.0px (41.142857dp)
+	getHotseatLayoutPadding(context).left: 113.0px (43.04762dp)
+	getHotseatLayoutPadding(context).right: 113.0px (43.04762dp)
+	numShownHotseatIcons: 6
+	hotseatBorderSpace: 0.0px (0.0dp)
+	isQsbInline: false
+	hotseatQsbWidth: 0.0px (0.0dp)
+	isTaskbarPresent:false
+	isTaskbarPresentInApps:true
+	taskbarHeight: 0.0px (0.0dp)
+	stashedTaskbarHeight: 0.0px (0.0dp)
+	taskbarBottomMargin: 0.0px (0.0dp)
+	taskbarIconSize: 0.0px (0.0dp)
+	desiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)
+	workspacePadding.left: 21.0px (8.0dp)
+	workspacePadding.top: 30.0px (11.428572dp)
+	workspacePadding.right: 21.0px (8.0dp)
+	workspacePadding.bottom: 330.0px (125.71429dp)
+	iconScale: 1.0px (0.3809524dp)
+	cellScaleToFit : 1.0px (0.3809524dp)
+	extraSpace: 498.0px (189.71428dp)
+	unscaled extraSpace: 498.0px (189.71428dp)
+	maxEmptySpace: 0.0px (0.0dp)
+	workspaceTopPadding: 0.0px (0.0dp)
+	workspaceBottomPadding: 0.0px (0.0dp)
+	overviewTaskMarginPx: 0.0px (0.0dp)
+	overviewTaskIconSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)
+	overviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)
+	overviewActionsTopMarginPx: 0.0px (0.0dp)
+	overviewActionsHeight: 0.0px (0.0dp)
+	overviewActionsClaimedSpaceBelow: 0.0px (0.0dp)
+	overviewActionsButtonSpacing: 0.0px (0.0dp)
+	overviewPageSpacing: 0.0px (0.0dp)
+	overviewRowSpacing: 0.0px (0.0dp)
+	overviewGridSideMargin: 0.0px (0.0dp)
+	dropTargetBarTopMarginPx: 0.0px (0.0dp)
+	dropTargetBarSizePx: 147.0px (56.0dp)
+	dropTargetBarBottomMarginPx: 42.0px (16.0dp)
+	getCellLayoutSpringLoadShrunkTop(): 299.0px (113.90476dp)
+	getCellLayoutSpringLoadShrunkBottom(): 1457.0px (555.0476dp)
+	workspaceSpringLoadedMinNextPageVisiblePx: 126.0px (48.0dp)
+	getWorkspaceSpringLoadScale(): 0.8452555px (0.32200208dp)
+	getCellLayoutHeight(): 1370.0px (521.9048dp)
+	getCellLayoutWidth(): 1083.0px (412.57144dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape_decoupleDepth.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape_decoupleDepth.txt
new file mode 100644
index 0000000..44b99e9
--- /dev/null
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape_decoupleDepth.txt
@@ -0,0 +1,130 @@
+DeviceProfile:
+	1 dp = 2.625 px
+	isTablet:true
+	isPhone:false
+	transposeLayoutWithOrientation:false
+	isGestureMode:true
+	isLandscape:true
+	isMultiWindowMode:false
+	isTwoPanels:true
+	isLeftRightSplit:true
+	windowX: 0.0px (0.0dp)
+	windowY: 0.0px (0.0dp)
+	widthPx: 2208.0px (841.1429dp)
+	heightPx: 1840.0px (700.9524dp)
+	availableWidthPx: 2208.0px (841.1429dp)
+	availableHeightPx: 1730.0px (659.0476dp)
+	mInsets.left: 0.0px (0.0dp)
+	mInsets.top: 110.0px (41.904762dp)
+	mInsets.right: 0.0px (0.0dp)
+	mInsets.bottom: 0.0px (0.0dp)
+	aspectRatio:1.2
+	isResponsiveGrid:false
+	isScalableGrid:false
+	inv.numRows: 4
+	inv.numColumns: 4
+	inv.numSearchContainerColumns: 4
+	minCellSize: PointF(0.0, 0.0)dp
+	cellWidthPx: 154.0px (58.666668dp)
+	cellHeightPx: 218.0px (83.04762dp)
+	getCellSize().x: 270.0px (102.85714dp)
+	getCellSize().y: 342.0px (130.28572dp)
+	cellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)
+	cellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)
+	cellLayoutPaddingPx.left: 0.0px (0.0dp)
+	cellLayoutPaddingPx.top: 0.0px (0.0dp)
+	cellLayoutPaddingPx.right: 0.0px (0.0dp)
+	cellLayoutPaddingPx.bottom: 0.0px (0.0dp)
+	iconSizePx: 141.0px (53.714287dp)
+	iconTextSizePx: 34.0px (12.952381dp)
+	iconDrawablePaddingPx: 13.0px (4.952381dp)
+	numFolderRows: 3
+	numFolderColumns: 4
+	folderCellWidthPx: 189.0px (72.0dp)
+	folderCellHeightPx: 219.0px (83.42857dp)
+	folderChildIconSizePx: 141.0px (53.714287dp)
+	folderChildTextSizePx: 34.0px (12.952381dp)
+	folderChildDrawablePaddingPx: 5.0px (1.9047619dp)
+	folderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)
+	folderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)
+	folderContentPaddingLeftRight: 21.0px (8.0dp)
+	folderTopPadding: 63.0px (24.0dp)
+	folderFooterHeight: 147.0px (56.0dp)
+	bottomSheetTopPadding: 110.0px (41.904762dp)
+	bottomSheetOpenDuration: 500
+	bottomSheetCloseDuration: 500
+	bottomSheetWorkspaceScale: 0.97
+	bottomSheetDepth: 0.3
+	allAppsShiftRange: 1840.0px (700.9524dp)
+	allAppsOpenDuration: 500
+	allAppsCloseDuration: 500
+	allAppsIconSizePx: 141.0px (53.714287dp)
+	allAppsIconTextSizePx: 34.0px (12.952381dp)
+	allAppsIconDrawablePaddingPx: 21.0px (8.0dp)
+	allAppsCellHeightPx: 361.0px (137.5238dp)
+	allAppsCellWidthPx: 183.0px (69.71429dp)
+	allAppsBorderSpacePxX: 42.0px (16.0dp)
+	allAppsBorderSpacePxY: 42.0px (16.0dp)
+	numShownAllAppsColumns: 8
+	allAppsPadding.top: 110.0px (41.904762dp)
+	allAppsPadding.left: 42.0px (16.0dp)
+	allAppsPadding.right: 42.0px (16.0dp)
+	allAppsLeftRightMargin: 183.0px (69.71429dp)
+	hotseatBarSizePx: 267.0px (101.71429dp)
+	mHotseatColumnSpan: 4
+	mHotseatWidthPx: 0.0px (0.0dp)
+	hotseatCellHeightPx: 159.0px (60.57143dp)
+	hotseatBarBottomSpacePx: 126.0px (48.0dp)
+	mHotseatBarEdgePaddingPx: 0.0px (0.0dp)
+	mHotseatBarWorkspaceSpacePx: 0.0px (0.0dp)
+	hotseatBarEndOffset: 0.0px (0.0dp)
+	hotseatQsbSpace: 0.0px (0.0dp)
+	hotseatQsbHeight: 0.0px (0.0dp)
+	springLoadedHotseatBarTopMarginPx: 116.0px (44.190475dp)
+	getHotseatLayoutPadding(context).top: 0.0px (0.0dp)
+	getHotseatLayoutPadding(context).bottom: 108.0px (41.142857dp)
+	getHotseatLayoutPadding(context).left: 113.0px (43.04762dp)
+	getHotseatLayoutPadding(context).right: 113.0px (43.04762dp)
+	numShownHotseatIcons: 6
+	hotseatBorderSpace: 0.0px (0.0dp)
+	isQsbInline: false
+	hotseatQsbWidth: 0.0px (0.0dp)
+	isTaskbarPresent:false
+	isTaskbarPresentInApps:true
+	taskbarHeight: 0.0px (0.0dp)
+	stashedTaskbarHeight: 0.0px (0.0dp)
+	taskbarBottomMargin: 0.0px (0.0dp)
+	taskbarIconSize: 0.0px (0.0dp)
+	desiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)
+	workspacePadding.left: 21.0px (8.0dp)
+	workspacePadding.top: 30.0px (11.428572dp)
+	workspacePadding.right: 21.0px (8.0dp)
+	workspacePadding.bottom: 330.0px (125.71429dp)
+	iconScale: 1.0px (0.3809524dp)
+	cellScaleToFit : 1.0px (0.3809524dp)
+	extraSpace: 498.0px (189.71428dp)
+	unscaled extraSpace: 498.0px (189.71428dp)
+	maxEmptySpace: 0.0px (0.0dp)
+	workspaceTopPadding: 0.0px (0.0dp)
+	workspaceBottomPadding: 0.0px (0.0dp)
+	overviewTaskMarginPx: 0.0px (0.0dp)
+	overviewTaskIconSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)
+	overviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)
+	overviewActionsTopMarginPx: 0.0px (0.0dp)
+	overviewActionsHeight: 0.0px (0.0dp)
+	overviewActionsClaimedSpaceBelow: 0.0px (0.0dp)
+	overviewActionsButtonSpacing: 0.0px (0.0dp)
+	overviewPageSpacing: 0.0px (0.0dp)
+	overviewRowSpacing: 0.0px (0.0dp)
+	overviewGridSideMargin: 0.0px (0.0dp)
+	dropTargetBarTopMarginPx: 0.0px (0.0dp)
+	dropTargetBarSizePx: 147.0px (56.0dp)
+	dropTargetBarBottomMarginPx: 42.0px (16.0dp)
+	getCellLayoutSpringLoadShrunkTop(): 299.0px (113.90476dp)
+	getCellLayoutSpringLoadShrunkBottom(): 1457.0px (555.0476dp)
+	workspaceSpringLoadedMinNextPageVisiblePx: 126.0px (48.0dp)
+	getWorkspaceSpringLoadScale(): 0.8452555px (0.32200208dp)
+	getCellLayoutHeight(): 1370.0px (521.9048dp)
+	getCellLayoutWidth(): 1083.0px (412.57144dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button_decoupleDepth.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button_decoupleDepth.txt
new file mode 100644
index 0000000..e7b72f2
--- /dev/null
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button_decoupleDepth.txt
@@ -0,0 +1,130 @@
+DeviceProfile:
+	1 dp = 2.625 px
+	isTablet:true
+	isPhone:false
+	transposeLayoutWithOrientation:false
+	isGestureMode:false
+	isLandscape:false
+	isMultiWindowMode:false
+	isTwoPanels:true
+	isLeftRightSplit:false
+	windowX: 0.0px (0.0dp)
+	windowY: 0.0px (0.0dp)
+	widthPx: 1840.0px (700.9524dp)
+	heightPx: 2208.0px (841.1429dp)
+	availableWidthPx: 1840.0px (700.9524dp)
+	availableHeightPx: 2075.0px (790.4762dp)
+	mInsets.left: 0.0px (0.0dp)
+	mInsets.top: 133.0px (50.666668dp)
+	mInsets.right: 0.0px (0.0dp)
+	mInsets.bottom: 0.0px (0.0dp)
+	aspectRatio:1.2
+	isResponsiveGrid:false
+	isScalableGrid:false
+	inv.numRows: 4
+	inv.numColumns: 4
+	inv.numSearchContainerColumns: 4
+	minCellSize: PointF(0.0, 0.0)dp
+	cellWidthPx: 154.0px (58.666668dp)
+	cellHeightPx: 218.0px (83.04762dp)
+	getCellSize().x: 224.0px (85.333336dp)
+	getCellSize().y: 430.0px (163.80952dp)
+	cellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)
+	cellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)
+	cellLayoutPaddingPx.left: 0.0px (0.0dp)
+	cellLayoutPaddingPx.top: 0.0px (0.0dp)
+	cellLayoutPaddingPx.right: 0.0px (0.0dp)
+	cellLayoutPaddingPx.bottom: 0.0px (0.0dp)
+	iconSizePx: 141.0px (53.714287dp)
+	iconTextSizePx: 34.0px (12.952381dp)
+	iconDrawablePaddingPx: 13.0px (4.952381dp)
+	numFolderRows: 3
+	numFolderColumns: 4
+	folderCellWidthPx: 189.0px (72.0dp)
+	folderCellHeightPx: 219.0px (83.42857dp)
+	folderChildIconSizePx: 141.0px (53.714287dp)
+	folderChildTextSizePx: 34.0px (12.952381dp)
+	folderChildDrawablePaddingPx: 5.0px (1.9047619dp)
+	folderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)
+	folderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)
+	folderContentPaddingLeftRight: 21.0px (8.0dp)
+	folderTopPadding: 63.0px (24.0dp)
+	folderFooterHeight: 147.0px (56.0dp)
+	bottomSheetTopPadding: 133.0px (50.666668dp)
+	bottomSheetOpenDuration: 500
+	bottomSheetCloseDuration: 500
+	bottomSheetWorkspaceScale: 0.97
+	bottomSheetDepth: 0.3
+	allAppsShiftRange: 2208.0px (841.1429dp)
+	allAppsOpenDuration: 500
+	allAppsCloseDuration: 500
+	allAppsIconSizePx: 141.0px (53.714287dp)
+	allAppsIconTextSizePx: 34.0px (12.952381dp)
+	allAppsIconDrawablePaddingPx: 21.0px (8.0dp)
+	allAppsCellHeightPx: 361.0px (137.5238dp)
+	allAppsCellWidthPx: 183.0px (69.71429dp)
+	allAppsBorderSpacePxX: 42.0px (16.0dp)
+	allAppsBorderSpacePxY: 42.0px (16.0dp)
+	numShownAllAppsColumns: 8
+	allAppsPadding.top: 133.0px (50.666668dp)
+	allAppsPadding.left: 42.0px (16.0dp)
+	allAppsPadding.right: 42.0px (16.0dp)
+	allAppsLeftRightMargin: 1.0px (0.3809524dp)
+	hotseatBarSizePx: 267.0px (101.71429dp)
+	mHotseatColumnSpan: 4
+	mHotseatWidthPx: 0.0px (0.0dp)
+	hotseatCellHeightPx: 159.0px (60.57143dp)
+	hotseatBarBottomSpacePx: 126.0px (48.0dp)
+	mHotseatBarEdgePaddingPx: 0.0px (0.0dp)
+	mHotseatBarWorkspaceSpacePx: 0.0px (0.0dp)
+	hotseatBarEndOffset: 0.0px (0.0dp)
+	hotseatQsbSpace: 0.0px (0.0dp)
+	hotseatQsbHeight: 0.0px (0.0dp)
+	springLoadedHotseatBarTopMarginPx: 168.0px (64.0dp)
+	getHotseatLayoutPadding(context).top: 0.0px (0.0dp)
+	getHotseatLayoutPadding(context).bottom: 108.0px (41.142857dp)
+	getHotseatLayoutPadding(context).left: 98.0px (37.333332dp)
+	getHotseatLayoutPadding(context).right: 98.0px (37.333332dp)
+	numShownHotseatIcons: 6
+	hotseatBorderSpace: 0.0px (0.0dp)
+	isQsbInline: false
+	hotseatQsbWidth: 0.0px (0.0dp)
+	isTaskbarPresent:false
+	isTaskbarPresentInApps:true
+	taskbarHeight: 0.0px (0.0dp)
+	stashedTaskbarHeight: 0.0px (0.0dp)
+	taskbarBottomMargin: 0.0px (0.0dp)
+	taskbarIconSize: 0.0px (0.0dp)
+	desiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)
+	workspacePadding.left: 21.0px (8.0dp)
+	workspacePadding.top: 24.0px (9.142858dp)
+	workspacePadding.right: 21.0px (8.0dp)
+	workspacePadding.bottom: 330.0px (125.71429dp)
+	iconScale: 1.0px (0.3809524dp)
+	cellScaleToFit : 1.0px (0.3809524dp)
+	extraSpace: 849.0px (323.42856dp)
+	unscaled extraSpace: 849.0px (323.42856dp)
+	maxEmptySpace: 0.0px (0.0dp)
+	workspaceTopPadding: 0.0px (0.0dp)
+	workspaceBottomPadding: 0.0px (0.0dp)
+	overviewTaskMarginPx: 0.0px (0.0dp)
+	overviewTaskIconSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)
+	overviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)
+	overviewActionsTopMarginPx: 0.0px (0.0dp)
+	overviewActionsHeight: 0.0px (0.0dp)
+	overviewActionsClaimedSpaceBelow: 0.0px (0.0dp)
+	overviewActionsButtonSpacing: 0.0px (0.0dp)
+	overviewPageSpacing: 0.0px (0.0dp)
+	overviewRowSpacing: 0.0px (0.0dp)
+	overviewGridSideMargin: 0.0px (0.0dp)
+	dropTargetBarTopMarginPx: 0.0px (0.0dp)
+	dropTargetBarSizePx: 147.0px (56.0dp)
+	dropTargetBarBottomMarginPx: 84.0px (32.0dp)
+	getCellLayoutSpringLoadShrunkTop(): 364.0px (138.66667dp)
+	getCellLayoutSpringLoadShrunkBottom(): 1773.0px (675.4286dp)
+	workspaceSpringLoadedMinNextPageVisiblePx: 126.0px (48.0dp)
+	getWorkspaceSpringLoadScale(): 0.81871px (0.31188953dp)
+	getCellLayoutHeight(): 1721.0px (655.619dp)
+	getCellLayoutWidth(): 899.0px (342.4762dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait_decoupleDepth.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait_decoupleDepth.txt
new file mode 100644
index 0000000..eae50f1
--- /dev/null
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait_decoupleDepth.txt
@@ -0,0 +1,130 @@
+DeviceProfile:
+	1 dp = 2.625 px
+	isTablet:true
+	isPhone:false
+	transposeLayoutWithOrientation:false
+	isGestureMode:true
+	isLandscape:false
+	isMultiWindowMode:false
+	isTwoPanels:true
+	isLeftRightSplit:false
+	windowX: 0.0px (0.0dp)
+	windowY: 0.0px (0.0dp)
+	widthPx: 1840.0px (700.9524dp)
+	heightPx: 2208.0px (841.1429dp)
+	availableWidthPx: 1840.0px (700.9524dp)
+	availableHeightPx: 2075.0px (790.4762dp)
+	mInsets.left: 0.0px (0.0dp)
+	mInsets.top: 133.0px (50.666668dp)
+	mInsets.right: 0.0px (0.0dp)
+	mInsets.bottom: 0.0px (0.0dp)
+	aspectRatio:1.2
+	isResponsiveGrid:false
+	isScalableGrid:false
+	inv.numRows: 4
+	inv.numColumns: 4
+	inv.numSearchContainerColumns: 4
+	minCellSize: PointF(0.0, 0.0)dp
+	cellWidthPx: 154.0px (58.666668dp)
+	cellHeightPx: 218.0px (83.04762dp)
+	getCellSize().x: 224.0px (85.333336dp)
+	getCellSize().y: 430.0px (163.80952dp)
+	cellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)
+	cellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)
+	cellLayoutPaddingPx.left: 0.0px (0.0dp)
+	cellLayoutPaddingPx.top: 0.0px (0.0dp)
+	cellLayoutPaddingPx.right: 0.0px (0.0dp)
+	cellLayoutPaddingPx.bottom: 0.0px (0.0dp)
+	iconSizePx: 141.0px (53.714287dp)
+	iconTextSizePx: 34.0px (12.952381dp)
+	iconDrawablePaddingPx: 13.0px (4.952381dp)
+	numFolderRows: 3
+	numFolderColumns: 4
+	folderCellWidthPx: 189.0px (72.0dp)
+	folderCellHeightPx: 219.0px (83.42857dp)
+	folderChildIconSizePx: 141.0px (53.714287dp)
+	folderChildTextSizePx: 34.0px (12.952381dp)
+	folderChildDrawablePaddingPx: 5.0px (1.9047619dp)
+	folderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)
+	folderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)
+	folderContentPaddingLeftRight: 21.0px (8.0dp)
+	folderTopPadding: 63.0px (24.0dp)
+	folderFooterHeight: 147.0px (56.0dp)
+	bottomSheetTopPadding: 133.0px (50.666668dp)
+	bottomSheetOpenDuration: 500
+	bottomSheetCloseDuration: 500
+	bottomSheetWorkspaceScale: 0.97
+	bottomSheetDepth: 0.3
+	allAppsShiftRange: 2208.0px (841.1429dp)
+	allAppsOpenDuration: 500
+	allAppsCloseDuration: 500
+	allAppsIconSizePx: 141.0px (53.714287dp)
+	allAppsIconTextSizePx: 34.0px (12.952381dp)
+	allAppsIconDrawablePaddingPx: 21.0px (8.0dp)
+	allAppsCellHeightPx: 361.0px (137.5238dp)
+	allAppsCellWidthPx: 183.0px (69.71429dp)
+	allAppsBorderSpacePxX: 42.0px (16.0dp)
+	allAppsBorderSpacePxY: 42.0px (16.0dp)
+	numShownAllAppsColumns: 8
+	allAppsPadding.top: 133.0px (50.666668dp)
+	allAppsPadding.left: 42.0px (16.0dp)
+	allAppsPadding.right: 42.0px (16.0dp)
+	allAppsLeftRightMargin: 1.0px (0.3809524dp)
+	hotseatBarSizePx: 267.0px (101.71429dp)
+	mHotseatColumnSpan: 4
+	mHotseatWidthPx: 0.0px (0.0dp)
+	hotseatCellHeightPx: 159.0px (60.57143dp)
+	hotseatBarBottomSpacePx: 126.0px (48.0dp)
+	mHotseatBarEdgePaddingPx: 0.0px (0.0dp)
+	mHotseatBarWorkspaceSpacePx: 0.0px (0.0dp)
+	hotseatBarEndOffset: 0.0px (0.0dp)
+	hotseatQsbSpace: 0.0px (0.0dp)
+	hotseatQsbHeight: 0.0px (0.0dp)
+	springLoadedHotseatBarTopMarginPx: 168.0px (64.0dp)
+	getHotseatLayoutPadding(context).top: 0.0px (0.0dp)
+	getHotseatLayoutPadding(context).bottom: 108.0px (41.142857dp)
+	getHotseatLayoutPadding(context).left: 98.0px (37.333332dp)
+	getHotseatLayoutPadding(context).right: 98.0px (37.333332dp)
+	numShownHotseatIcons: 6
+	hotseatBorderSpace: 0.0px (0.0dp)
+	isQsbInline: false
+	hotseatQsbWidth: 0.0px (0.0dp)
+	isTaskbarPresent:false
+	isTaskbarPresentInApps:true
+	taskbarHeight: 0.0px (0.0dp)
+	stashedTaskbarHeight: 0.0px (0.0dp)
+	taskbarBottomMargin: 0.0px (0.0dp)
+	taskbarIconSize: 0.0px (0.0dp)
+	desiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)
+	workspacePadding.left: 21.0px (8.0dp)
+	workspacePadding.top: 24.0px (9.142858dp)
+	workspacePadding.right: 21.0px (8.0dp)
+	workspacePadding.bottom: 330.0px (125.71429dp)
+	iconScale: 1.0px (0.3809524dp)
+	cellScaleToFit : 1.0px (0.3809524dp)
+	extraSpace: 849.0px (323.42856dp)
+	unscaled extraSpace: 849.0px (323.42856dp)
+	maxEmptySpace: 0.0px (0.0dp)
+	workspaceTopPadding: 0.0px (0.0dp)
+	workspaceBottomPadding: 0.0px (0.0dp)
+	overviewTaskMarginPx: 0.0px (0.0dp)
+	overviewTaskIconSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)
+	overviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)
+	overviewActionsTopMarginPx: 0.0px (0.0dp)
+	overviewActionsHeight: 0.0px (0.0dp)
+	overviewActionsClaimedSpaceBelow: 0.0px (0.0dp)
+	overviewActionsButtonSpacing: 0.0px (0.0dp)
+	overviewPageSpacing: 0.0px (0.0dp)
+	overviewRowSpacing: 0.0px (0.0dp)
+	overviewGridSideMargin: 0.0px (0.0dp)
+	dropTargetBarTopMarginPx: 0.0px (0.0dp)
+	dropTargetBarSizePx: 147.0px (56.0dp)
+	dropTargetBarBottomMarginPx: 84.0px (32.0dp)
+	getCellLayoutSpringLoadShrunkTop(): 364.0px (138.66667dp)
+	getCellLayoutSpringLoadShrunkBottom(): 1773.0px (675.4286dp)
+	workspaceSpringLoadedMinNextPageVisiblePx: 126.0px (48.0dp)
+	getWorkspaceSpringLoadScale(): 0.81871px (0.31188953dp)
+	getCellLayoutHeight(): 1721.0px (655.619dp)
+	getCellLayoutWidth(): 899.0px (342.4762dp)
diff --git a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index 8c47332..c3b7a2a 100644
--- a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -170,19 +170,12 @@
 
     public static final String PERMANENT_DIAG_TAG = "TaplTarget";
     public static final String TWO_NEXUS_LAUNCHER_ACTIVITY_WHILE_UNLOCKING = "b/273347463";
-    public static final String TWO_TASKBAR_LONG_CLICKS = "b/262282528";
     public static final String ICON_MISSING = "b/282963545";
-    public static final String OVERVIEW_OVER_HOME = "b/279059025";
     public static final String UIOBJECT_STALE_ELEMENT = "b/319501259";
     public static final String TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE = "b/326908466";
-    public static final String TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE = "b/326073471";
     public static final String WIDGET_CONFIG_NULL_EXTRA_INTENT = "b/324419890";
-    public static final String ACTIVITY_NOT_RESUMED_AFTER_BACK = "b/322823209";
     public static final String OVERVIEW_SELECT_TOOLTIP_MISALIGNED = "b/332485341";
-    public static final String REQUEST_EMULATE_DISPLAY = "emulate-display";
-    public static final String REQUEST_STOP_EMULATE_DISPLAY = "stop-emulate-display";
-    public static final String REQUEST_IS_EMULATE_DISPLAY_RUNNING = "is-emulate-display-running";
-    public static final String REQUEST_EMULATE_PRINT_DEVICE = "emulate-print-device";
+    public static final String CLOCK_ICON_DRAWABLE_LEAKING = "b/319168409";
 
     public static final String REQUEST_FLAG_ENABLE_GRID_ONLY_OVERVIEW = "enable-grid-only-overview";
     public static final String REQUEST_FLAG_ENABLE_APP_PAIRS = "enable-app-pairs";
diff --git a/tests/multivalentTests/src/com/android/launcher3/AutoInstallsLayoutTest.kt b/tests/multivalentTests/src/com/android/launcher3/AutoInstallsLayoutTest.kt
new file mode 100644
index 0000000..b04bcca
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/AutoInstallsLayoutTest.kt
@@ -0,0 +1,216 @@
+/*
+ * 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
+
+import android.content.ComponentName
+import android.content.ContentValues
+import android.database.sqlite.SQLiteDatabase
+import android.os.Process.myUserHandle
+import android.os.UserHandle
+import android.util.Xml
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback
+import com.android.launcher3.AutoInstallsLayout.SourceResources
+import com.android.launcher3.AutoInstallsLayout.TAG_WORKSPACE
+import com.android.launcher3.AutoInstallsLayout.USER_TYPE_WORK
+import com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_PROVIDER
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
+import com.android.launcher3.LauncherSettings.Favorites.INTENT
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER
+import com.android.launcher3.LauncherSettings.Favorites.PROFILE_ID
+import com.android.launcher3.LauncherSettings.Favorites.SPANX
+import com.android.launcher3.LauncherSettings.Favorites.SPANY
+import com.android.launcher3.LauncherSettings.Favorites._ID
+import com.android.launcher3.model.data.AppInfo
+import com.android.launcher3.pm.UserCache
+import com.android.launcher3.util.ApiWrapper
+import com.android.launcher3.util.Executors
+import com.android.launcher3.util.LauncherLayoutBuilder
+import com.android.launcher3.util.LauncherModelHelper
+import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext
+import com.android.launcher3.util.TestUtil
+import com.android.launcher3.util.UserIconInfo
+import com.android.launcher3.util.UserIconInfo.TYPE_MAIN
+import com.android.launcher3.util.UserIconInfo.TYPE_WORK
+import com.android.launcher3.widget.LauncherWidgetHolder
+import com.google.common.truth.Truth.assertThat
+import java.io.StringReader
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
+
+/** Tests for [AutoInstallsLayout] */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AutoInstallsLayoutTest {
+
+    lateinit var modelHelper: LauncherModelHelper
+    lateinit var targetContext: SandboxModelContext
+
+    lateinit var callback: MyCallback
+
+    @Mock lateinit var widgetHolder: LauncherWidgetHolder
+    @Mock lateinit var db: SQLiteDatabase
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        modelHelper = LauncherModelHelper()
+        targetContext = modelHelper.sandboxContext
+        callback = MyCallback()
+    }
+
+    @After
+    fun tearDown() {
+        modelHelper.destroy()
+    }
+
+    @Test
+    fun pending_icon_added_on_home() {
+        LauncherLayoutBuilder()
+            .atWorkspace(1, 1, 0)
+            .putApp("p1", "c1")
+            .toAutoInstallsLayout()
+            .loadLayout(db)
+
+        assertThat(callback.items.size).isEqualTo(1)
+        assertThat(callback.items[0][ITEM_TYPE]).isEqualTo(ITEM_TYPE_APPLICATION)
+        assertThat(callback.items[0][INTENT])
+            .isEqualTo(AppInfo.makeLaunchIntent(ComponentName("p1", "c1")).toUri(0))
+        assertThat(callback.items[0][CONTAINER]).isEqualTo(CONTAINER_DESKTOP)
+        assertThat(callback.items[0].containsKey(PROFILE_ID)).isFalse()
+    }
+
+    @Test
+    fun pending_icon_added_on_hotseat() {
+        LauncherLayoutBuilder()
+            .atHotseat(1)
+            .putApp("p1", "c1")
+            .toAutoInstallsLayout()
+            .loadLayout(db)
+
+        assertThat(callback.items.size).isEqualTo(1)
+        assertThat(callback.items[0][ITEM_TYPE]).isEqualTo(ITEM_TYPE_APPLICATION)
+        assertThat(callback.items[0][CONTAINER]).isEqualTo(CONTAINER_HOTSEAT)
+    }
+
+    @Test
+    fun widget_added_to_home() {
+        LauncherLayoutBuilder()
+            .atWorkspace(1, 1, 0)
+            .putWidget("p1", "c1", 2, 3)
+            .toAutoInstallsLayout()
+            .loadLayout(db)
+
+        assertThat(callback.items.size).isEqualTo(1)
+        assertThat(callback.items[0][ITEM_TYPE]).isEqualTo(ITEM_TYPE_APPWIDGET)
+        assertThat(callback.items[0][CONTAINER]).isEqualTo(CONTAINER_DESKTOP)
+        assertThat(callback.items[0][APPWIDGET_PROVIDER])
+            .isEqualTo(ComponentName("p1", "c1").flattenToString())
+        assertThat(callback.items[0][SPANX]).isEqualTo(2.toString())
+        assertThat(callback.items[0][SPANY]).isEqualTo(3.toString())
+    }
+
+    @Test
+    fun items_added_to_folder() {
+        LauncherLayoutBuilder()
+            .atHotseat(1)
+            .putFolder("Test")
+            .addApp("p1", "c")
+            .addApp("p2", "c")
+            .addApp("p3", "c")
+            .build()
+            .toAutoInstallsLayout()
+            .loadLayout(db)
+
+        assertThat(callback.items.size).isEqualTo(4)
+        assertThat(callback.items[0][ITEM_TYPE]).isEqualTo(ITEM_TYPE_FOLDER)
+        assertThat(callback.items[0][CONTAINER]).isEqualTo(CONTAINER_HOTSEAT)
+
+        val folderId = callback.items[0][_ID]
+        assertThat(callback.items[1][CONTAINER]).isEqualTo(folderId)
+        assertThat(callback.items[2][CONTAINER]).isEqualTo(folderId)
+        assertThat(callback.items[3][CONTAINER]).isEqualTo(folderId)
+    }
+
+    @Test
+    fun work_item_added_to_home() {
+        val apiWrapperMock = spy(ApiWrapper.INSTANCE[targetContext])
+        targetContext.putObject(ApiWrapper.INSTANCE, apiWrapperMock)
+        doReturn(
+                mapOf(
+                    myUserHandle() to UserIconInfo(myUserHandle(), TYPE_MAIN, 0),
+                    UserHandle.of(20) to UserIconInfo(UserHandle.of(20), TYPE_WORK, 20),
+                )
+            )
+            .whenever(apiWrapperMock)
+            .queryAllUsers()
+
+        val cache = UserCache.getInstance(targetContext)
+        TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) {
+            assertThat(cache.userProfiles.size).isEqualTo(2)
+        }
+
+        LauncherLayoutBuilder()
+            .atWorkspace(1, 1, 0)
+            .putApp("p1", "c1", USER_TYPE_WORK)
+            .toAutoInstallsLayout()
+            .loadLayout(db)
+
+        assertThat(callback.items.size).isEqualTo(1)
+        assertThat(callback.items[0][ITEM_TYPE]).isEqualTo(ITEM_TYPE_APPLICATION)
+        assertThat(callback.items[0][INTENT])
+            .isEqualTo(AppInfo.makeLaunchIntent(ComponentName("p1", "c1")).toUri(0))
+        assertThat(callback.items[0][CONTAINER]).isEqualTo(CONTAINER_DESKTOP)
+        assertThat(callback.items[0][PROFILE_ID]).isEqualTo(20)
+    }
+
+    private fun LauncherLayoutBuilder.toAutoInstallsLayout() =
+        AutoInstallsLayout(
+            targetContext,
+            widgetHolder,
+            callback,
+            SourceResources.wrap(targetContext.resources),
+            { Xml.newPullParser().also { it.setInput(StringReader(build())) } },
+            TAG_WORKSPACE
+        )
+
+    class MyCallback : LayoutParserCallback {
+
+        val items = ArrayList<ContentValues>()
+
+        override fun generateNewItemId() = items.size
+
+        override fun insertAndCheck(db: SQLiteDatabase?, values: ContentValues): Int {
+            val id = values[_ID]
+            items.add(ContentValues(values))
+            return if (id is Int) id else 0
+        }
+    }
+}
diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutTestCaseReader.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/CellLayoutTestCaseReader.java
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/CellLayoutTestCaseReader.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/CellLayoutTestCaseReader.java
diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java
diff --git a/tests/src/com/android/launcher3/celllayout/HotseatReorderUnitTest.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/HotseatReorderUnitTest.kt
similarity index 97%
rename from tests/src/com/android/launcher3/celllayout/HotseatReorderUnitTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/HotseatReorderUnitTest.kt
index 13dfd5e..c32461e 100644
--- a/tests/src/com/android/launcher3/celllayout/HotseatReorderUnitTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/HotseatReorderUnitTest.kt
@@ -22,6 +22,8 @@
 import android.view.View
 import androidx.core.view.get
 import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
 import com.android.launcher3.CellLayout
 import com.android.launcher3.celllayout.board.CellLayoutBoard
 import com.android.launcher3.celllayout.board.IconPoint
@@ -34,6 +36,7 @@
 import org.junit.Assert
 import org.junit.Rule
 import org.junit.Test
+import org.junit.runner.RunWith
 
 private class HotseatReorderTestCase(
     val startBoard: CellLayoutBoard,
@@ -44,6 +47,8 @@
     }
 }
 
+@SmallTest
+@RunWith(AndroidJUnit4::class)
 class HotseatReorderUnitTest {
 
     private val applicationContext: Context =
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTestCase.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTestCase.java
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTestCase.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTestCase.java
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderPreviewAnimationTest.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderPreviewAnimationTest.kt
similarity index 95%
rename from tests/src/com/android/launcher3/celllayout/ReorderPreviewAnimationTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderPreviewAnimationTest.kt
index 0bec1b2..a9355ec 100644
--- a/tests/src/com/android/launcher3/celllayout/ReorderPreviewAnimationTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderPreviewAnimationTest.kt
@@ -141,11 +141,14 @@
             ReorderPreviewAnimation.MODE_PREVIEW,
             AnimationValues(dx = 0, dy = 0, scale = 100)
         )
-        testAnimationAtGivenProgress(
-            PREVIEW_DURATION * 99,
-            ReorderPreviewAnimation.MODE_PREVIEW,
-            AnimationValues(dx = 5, dy = -10, scale = 96)
-        )
+        // (b/339313407) Temporarily disable this test as the behavior is
+        // inconsistent between Soong & Gradle builds.
+        //
+        // testAnimationAtGivenProgress(
+        //     PREVIEW_DURATION * 99,
+        //     ReorderPreviewAnimation.MODE_PREVIEW,
+        //     AnimationValues(dx = 5, dy = -10, scale = 96)
+        // )
         testAnimationAtGivenProgress(
             PREVIEW_DURATION * 98,
             ReorderPreviewAnimation.MODE_PREVIEW,
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderTestCase.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderTestCase.java
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/ReorderTestCase.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderTestCase.java
diff --git a/tests/src/com/android/launcher3/celllayout/UnitTestCellLayoutBuilderRule.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/UnitTestCellLayoutBuilderRule.kt
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/UnitTestCellLayoutBuilderRule.kt
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/UnitTestCellLayoutBuilderRule.kt
diff --git a/tests/src/com/android/launcher3/celllayout/testgenerator/DeterministicRandomGenerator.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/testgenerator/DeterministicRandomGenerator.kt
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/testgenerator/DeterministicRandomGenerator.kt
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/testgenerator/DeterministicRandomGenerator.kt
diff --git a/tests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt
diff --git a/tests/src/com/android/launcher3/celllayout/testgenerator/RandomMultiBoardGenerator.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/testgenerator/RandomMultiBoardGenerator.kt
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/testgenerator/RandomMultiBoardGenerator.kt
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/testgenerator/RandomMultiBoardGenerator.kt
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/LauncherLayoutBuilder.java b/tests/multivalentTests/src/com/android/launcher3/util/LauncherLayoutBuilder.java
deleted file mode 100644
index ba01b04..0000000
--- a/tests/multivalentTests/src/com/android/launcher3/util/LauncherLayoutBuilder.java
+++ /dev/null
@@ -1,199 +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.launcher3.util;
-
-
-import android.text.TextUtils;
-import android.util.Pair;
-import android.util.Xml;
-
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.IOException;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Helper class to build xml for Launcher Layout
- */
-public class LauncherLayoutBuilder {
-
-    // Object Tags
-    private static final String TAG_WORKSPACE = "workspace";
-    private static final String TAG_AUTO_INSTALL = "autoinstall";
-    private static final String TAG_FOLDER = "folder";
-    private static final String TAG_APPWIDGET = "appwidget";
-    private static final String TAG_SHORTCUT = "shortcut";
-    private static final String TAG_EXTRA = "extra";
-
-    private static final String ATTR_CONTAINER = "container";
-    private static final String ATTR_RANK = "rank";
-
-    private static final String ATTR_PACKAGE_NAME = "packageName";
-    private static final String ATTR_CLASS_NAME = "className";
-    private static final String ATTR_TITLE = "title";
-    private static final String ATTR_TITLE_TEXT = "titleText";
-    private static final String ATTR_SCREEN = "screen";
-    private static final String ATTR_SHORTCUT_ID = "shortcutId";
-
-    // x and y can be specified as negative integers, in which case -1 represents the
-    // last row / column, -2 represents the second last, and so on.
-    private static final String ATTR_X = "x";
-    private static final String ATTR_Y = "y";
-    private static final String ATTR_SPAN_X = "spanX";
-    private static final String ATTR_SPAN_Y = "spanY";
-
-    private static final String ATTR_CHILDREN = "children";
-
-
-    // Style attrs -- "Extra"
-    private static final String ATTR_KEY = "key";
-    private static final String ATTR_VALUE = "value";
-
-    private static final String CONTAINER_DESKTOP = "desktop";
-    private static final String CONTAINER_HOTSEAT = "hotseat";
-
-    private final ArrayList<Pair<String, HashMap<String, Object>>> mNodes = new ArrayList<>();
-
-    public Location atHotseat(int rank) {
-        Location l = new Location();
-        l.items.put(ATTR_CONTAINER, CONTAINER_HOTSEAT);
-        l.items.put(ATTR_RANK, Integer.toString(rank));
-        return l;
-    }
-
-    public Location atWorkspace(int x, int y, int screen) {
-        Location l = new Location();
-        l.items.put(ATTR_CONTAINER, CONTAINER_DESKTOP);
-        l.items.put(ATTR_X, Integer.toString(x));
-        l.items.put(ATTR_Y, Integer.toString(y));
-        l.items.put(ATTR_SCREEN, Integer.toString(screen));
-        return l;
-    }
-
-    public String build() throws IOException {
-        StringWriter writer = new StringWriter();
-        build(writer);
-        return writer.toString();
-    }
-
-    public void build(Writer writer) throws IOException {
-        XmlSerializer serializer = Xml.newSerializer();
-        serializer.setOutput(writer);
-
-        serializer.startDocument("UTF-8", true);
-        serializer.startTag(null, TAG_WORKSPACE);
-        writeNodes(serializer, mNodes);
-        serializer.endTag(null, TAG_WORKSPACE);
-        serializer.endDocument();
-        serializer.flush();
-    }
-
-    private static void writeNodes(XmlSerializer serializer,
-            ArrayList<Pair<String, HashMap<String, Object>>> nodes) throws IOException {
-        for (Pair<String, HashMap<String, Object>> node : nodes) {
-            ArrayList<Pair<String, HashMap<String, Object>>> children = null;
-
-            serializer.startTag(null, node.first);
-            for (Map.Entry<String, Object> attr : node.second.entrySet()) {
-                if (ATTR_CHILDREN.equals(attr.getKey())) {
-                    children = (ArrayList<Pair<String, HashMap<String, Object>>>) attr.getValue();
-                } else {
-                    serializer.attribute(null, attr.getKey(), (String) attr.getValue());
-                }
-            }
-
-            if (children != null) {
-                writeNodes(serializer, children);
-            }
-            serializer.endTag(null, node.first);
-        }
-    }
-
-    public class Location {
-
-        final HashMap<String, Object> items = new HashMap<>();
-
-        public LauncherLayoutBuilder putApp(String packageName, String className) {
-            items.put(ATTR_PACKAGE_NAME, packageName);
-            items.put(ATTR_CLASS_NAME, TextUtils.isEmpty(className) ? packageName : className);
-            mNodes.add(Pair.create(TAG_AUTO_INSTALL, items));
-            return LauncherLayoutBuilder.this;
-        }
-
-        public LauncherLayoutBuilder putShortcut(String packageName, String shortcutId) {
-            items.put(ATTR_PACKAGE_NAME, packageName);
-            items.put(ATTR_SHORTCUT_ID, shortcutId);
-            mNodes.add(Pair.create(TAG_SHORTCUT, items));
-            return LauncherLayoutBuilder.this;
-        }
-
-        public LauncherLayoutBuilder putWidget(String packageName, String className,
-                int spanX, int spanY) {
-            items.put(ATTR_PACKAGE_NAME, packageName);
-            items.put(ATTR_CLASS_NAME, className);
-            items.put(ATTR_SPAN_X, Integer.toString(spanX));
-            items.put(ATTR_SPAN_Y, Integer.toString(spanY));
-            mNodes.add(Pair.create(TAG_APPWIDGET, items));
-            return LauncherLayoutBuilder.this;
-        }
-
-        public FolderBuilder putFolder(int titleResId) {
-            items.put(ATTR_TITLE, Integer.toString(titleResId));
-            return putFolder();
-        }
-
-        public FolderBuilder putFolder(String title) {
-            items.put(ATTR_TITLE_TEXT, title);
-            return putFolder();
-        }
-
-        private FolderBuilder putFolder() {
-            FolderBuilder folderBuilder = new FolderBuilder();
-            items.put(ATTR_CHILDREN, folderBuilder.mChildren);
-            mNodes.add(Pair.create(TAG_FOLDER, items));
-            return folderBuilder;
-        }
-    }
-
-    public class FolderBuilder {
-
-        final ArrayList<Pair<String, HashMap<String, Object>>> mChildren = new ArrayList<>();
-
-        public FolderBuilder addApp(String packageName, String className) {
-            HashMap<String, Object> items = new HashMap<>();
-            items.put(ATTR_PACKAGE_NAME, packageName);
-            items.put(ATTR_CLASS_NAME, TextUtils.isEmpty(className) ? packageName : className);
-            mChildren.add(Pair.create(TAG_AUTO_INSTALL, items));
-            return this;
-        }
-
-        public FolderBuilder addShortcut(String packageName, String shortcutId) {
-            HashMap<String, Object> items = new HashMap<>();
-            items.put(ATTR_PACKAGE_NAME, packageName);
-            items.put(ATTR_SHORTCUT_ID, shortcutId);
-            mChildren.add(Pair.create(TAG_SHORTCUT, items));
-            return this;
-        }
-
-        public LauncherLayoutBuilder build() {
-            return LauncherLayoutBuilder.this;
-        }
-    }
-}
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/WidgetUtils.java b/tests/multivalentTests/src/com/android/launcher3/util/WidgetUtils.java
index 027a31a..deb0ef3 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/WidgetUtils.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/WidgetUtils.java
@@ -15,15 +15,12 @@
  */
 package com.android.launcher3.util;
 
-import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.os.Bundle;
-import android.os.Process;
 
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -87,10 +84,10 @@
      * Creates a {@link AppWidgetProviderInfo} for the provided component name
      */
     public static AppWidgetProviderInfo createAppWidgetProviderInfo(ComponentName cn) {
-        AppWidgetProviderInfo info = AppWidgetManager.getInstance(getApplicationContext())
-                .getInstalledProvidersForPackage(
-                        getInstrumentation().getContext().getPackageName(), Process.myUserHandle())
-                .get(0);
+        ActivityInfo activityInfo = new ActivityInfo();
+        activityInfo.applicationInfo = new ApplicationInfo();
+        AppWidgetProviderInfo info = new AppWidgetProviderInfo();
+        info.providerInfo = activityInfo;
         info.provider = cn;
         return info;
     }
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetImageViewTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetImageViewTest.kt
similarity index 100%
rename from tests/src/com/android/launcher3/widget/picker/WidgetImageViewTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetImageViewTest.kt
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java
similarity index 91%
rename from tests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java
rename to tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java
index 60a4cd3..3024d26 100644
--- a/tests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java
@@ -25,7 +25,6 @@
 import static android.content.pm.ApplicationInfo.CATEGORY_VIDEO;
 import static android.content.pm.ApplicationInfo.FLAG_INSTALLED;
 
-import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -35,7 +34,6 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.when;
 
-import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Context;
@@ -53,6 +51,7 @@
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.WidgetUtils;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 
 import com.google.common.collect.ImmutableMap;
@@ -79,15 +78,15 @@
                     R.string.news_widget_recommendation_category_label, /*order=*/1);
     private static final WidgetRecommendationCategory SUGGESTED_FOR_YOU =
             new WidgetRecommendationCategory(
-                    R.string.others_widget_recommendation_category_label, /*order=*/4);
+                    R.string.others_widget_recommendation_category_label, /*order=*/2);
     private static final WidgetRecommendationCategory SOCIAL =
             new WidgetRecommendationCategory(
                     R.string.social_widget_recommendation_category_label,
-                    /*order=*/5);
+                    /*order=*/3);
     private static final WidgetRecommendationCategory ENTERTAINMENT =
             new WidgetRecommendationCategory(
                     R.string.entertainment_widget_recommendation_category_label,
-                    /*order=*/6);
+                    /*order=*/4);
 
     private final ApplicationInfo mTestAppInfo = ApplicationInfoBuilder.newBuilder().setPackageName(
             TEST_PACKAGE).setName(TEST_APP_NAME).build();
@@ -152,11 +151,8 @@
 
         doAnswer(invocation -> widgetLabel).when(mIconCache).getTitleNoCache(any());
 
-        AppWidgetProviderInfo providerInfo = AppWidgetManager.getInstance(getApplicationContext())
-                .getInstalledProvidersForPackage(
-                        getInstrumentation().getContext().getPackageName(), Process.myUserHandle())
-                .get(0);
-        providerInfo.provider = ComponentName.createRelative(TEST_PACKAGE, widgetClassName);
+        AppWidgetProviderInfo providerInfo = WidgetUtils.createAppWidgetProviderInfo(ComponentName
+                .createRelative(TEST_PACKAGE, widgetClassName));
 
         LauncherAppWidgetProviderInfo launcherAppWidgetProviderInfo =
                 LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, providerInfo);
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderAccessibilityTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetsListHeaderAccessibilityTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderAccessibilityTest.java
rename to tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetsListHeaderAccessibilityTest.java
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
rename to tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
rename to tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
diff --git a/tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
rename to tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
diff --git a/tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
similarity index 98%
rename from tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
rename to tests/multivalentTests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
index 9c03ccf..0370a6b 100644
--- a/tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.widget.picker.search;
 
 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo;
@@ -163,7 +164,7 @@
                 .when(mDataProvider)
                 .getAllWidgets();
         mSimpleWidgetsSearchAlgorithm.doSearch("Ca", mSearchCallback);
-        MAIN_EXECUTOR.submit(() -> { }).get();
+        getInstrumentation().waitForIdleSync();
         verify(mSearchCallback).onSearchResult(
                 matches("Ca"), argThat(a -> a != null && !a.isEmpty()));
     }
diff --git a/tests/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSizesTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSizesTest.kt
similarity index 93%
rename from tests/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSizesTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSizesTest.kt
index 040fbf5..7b629bf 100644
--- a/tests/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSizesTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSizesTest.kt
@@ -43,6 +43,7 @@
     private lateinit var context: Context
     private lateinit var deviceProfile: DeviceProfile
     private lateinit var testInvariantProfile: InvariantDeviceProfile
+    private lateinit var widgetItemInvariantProfile: InvariantDeviceProfile
 
     @Mock private lateinit var iconCache: IconCache
 
@@ -51,6 +52,11 @@
         MockitoAnnotations.initMocks(this)
         context = ActivityContextWrapper(ApplicationProvider.getApplicationContext())
         testInvariantProfile = LauncherAppState.getIDP(context)
+        widgetItemInvariantProfile =
+            InvariantDeviceProfile().apply {
+                numRows = TEST_GRID_SIZE
+                numColumns = TEST_GRID_SIZE
+            }
         deviceProfile = testInvariantProfile.getDeviceProfile(context).copy(context)
     }
 
@@ -60,7 +66,8 @@
         val expectedPreviewContainers = testSizes.values.toList()
 
         for ((index, widgetSize) in testSizes.keys.withIndex()) {
-            val widgetItem = createWidgetItem(widgetSize, context, testInvariantProfile, iconCache)
+            val widgetItem =
+                createWidgetItem(widgetSize, context, widgetItemInvariantProfile, iconCache)
 
             assertWithMessage("size for $widgetSize should be: ${expectedPreviewContainers[index]}")
                 .that(WidgetPreviewContainerSize.forItem(widgetItem, deviceProfile))
@@ -70,6 +77,7 @@
 
     companion object {
         private const val TEST_PACKAGE = "com.google.test"
+        private const val TEST_GRID_SIZE = 6
 
         private val HANDHELD_TEST_SIZES: Map<Point, WidgetPreviewContainerSize> =
             mapOf(
diff --git a/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
rename to tests/multivalentTests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
diff --git a/tests/src/com/android/launcher3/AppFilterTest.kt b/tests/src/com/android/launcher3/AppFilterTest.kt
new file mode 100644
index 0000000..f2150a2
--- /dev/null
+++ b/tests/src/com/android/launcher3/AppFilterTest.kt
@@ -0,0 +1,59 @@
+/*
+ * 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
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.res.Resources
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnitRunner
+
+@RunWith(MockitoJUnitRunner::class)
+class AppFilterTest {
+
+    @Mock private lateinit var mockContext: Context
+
+    @Mock // Mock the Resources object as well
+    private lateinit var mockResources: Resources
+
+    private lateinit var appFilter: AppFilter
+
+    @Before
+    fun setUp() {
+        `when`(mockContext.resources).thenReturn(mockResources) // Link the context and resources
+        `when`(mockResources.getStringArray(R.array.filtered_components))
+            .thenReturn(arrayOf("com.example.app1/Activity1"))
+        appFilter = AppFilter(mockContext)
+    }
+
+    @Test
+    fun shouldShowApp_notFiltered_returnsTrue() {
+        val appToShow = ComponentName("com.example.app2", "Activity2")
+        assertThat(appFilter.shouldShowApp(appToShow)).isTrue()
+    }
+
+    @Test
+    fun shouldShowApp_filtered_returnsFalse() {
+        val appToHide = ComponentName("com.example.app1", "Activity1")
+        assertThat(appFilter.shouldShowApp(appToHide)).isFalse()
+    }
+}
diff --git a/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt b/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt
index 13d7499..0538870 100644
--- a/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt
+++ b/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt
@@ -46,7 +46,7 @@
 @IgnoreLimit(ignoreLimit = BuildConfig.IS_STUDIO_BUILD)
 abstract class FakeInvariantDeviceProfileTest {
 
-    protected var context: Context? = null
+    protected lateinit var context: Context
     protected var inv: InvariantDeviceProfile? = null
     protected val info: Info = mock()
     protected var windowBounds: WindowBounds? = null
@@ -257,10 +257,10 @@
     }
 
     protected fun initializeVarsForTwoPanel(
-            isLandscape: Boolean = false,
-            isGestureMode: Boolean = true,
-            rows: Int = 4,
-            cols: Int = 4,
+        isLandscape: Boolean = false,
+        isGestureMode: Boolean = true,
+        rows: Int = 4,
+        cols: Int = 4,
     ) {
         val (x, y) = if (isLandscape) Pair(2208, 1840) else Pair(1840, 2208)
 
diff --git a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
index b6b2261..4cd2a07 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
@@ -202,6 +202,7 @@
     }
 
     @Test
+    @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/339109319
     public void openPrivateSpaceSettings_triggersCorrectIntent() {
         Intent expectedIntent = ApiWrapper.INSTANCE.get(mContext).getPrivateSpaceSettingsIntent();
         ArgumentCaptor<Intent> acIntent = ArgumentCaptor.forClass(Intent.class);
diff --git a/tests/src/com/android/launcher3/allapps/TaplKeyboardFocusTest.java b/tests/src/com/android/launcher3/allapps/TaplKeyboardFocusTest.java
index 20684eb..4e627a9 100644
--- a/tests/src/com/android/launcher3/allapps/TaplKeyboardFocusTest.java
+++ b/tests/src/com/android/launcher3/allapps/TaplKeyboardFocusTest.java
@@ -75,7 +75,6 @@
     }
 
     @Test
-    @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/311410127
     public void testAllAppsExitSearchAndFocusSearchResults() {
         final HomeAllApps allApps = mLauncher.goHome().switchToAllApps();
         assertTrue("Launcher internal state is not All Apps",
diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
index 56ac960..b4945d7 100644
--- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -79,6 +79,7 @@
 
     private LauncherModelHelper mModelHelper;
     private LauncherAppState mApp;
+    private PackageManagerHelper mPmHelper;
 
     private MatrixCursor mCursor;
     private InvariantDeviceProfile mIDP;
@@ -92,6 +93,7 @@
         mContext = mModelHelper.sandboxContext;
         mIDP = InvariantDeviceProfile.INSTANCE.get(mContext);
         mApp = LauncherAppState.getInstance(mContext);
+        mPmHelper = PackageManagerHelper.INSTANCE.get(mContext);
 
         mCursor = new MatrixCursor(new String[] {
                 ICON, TITLE, _ID, CONTAINER, ITEM_TYPE,
@@ -101,7 +103,7 @@
         });
 
         UserManagerState ums = new UserManagerState();
-        mLoaderCursor = new LoaderCursor(mCursor, mApp, ums, null);
+        mLoaderCursor = new LoaderCursor(mCursor, mApp, ums, mPmHelper, null);
         ums.allUsers.put(0, Process.myUserHandle());
     }
 
diff --git a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
index 6bbcf85..6cf3b19 100644
--- a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
+++ b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
@@ -23,6 +23,7 @@
 import android.content.pm.LauncherApps
 import android.content.pm.PackageInstaller
 import android.content.pm.ShortcutInfo
+import android.os.Process
 import android.os.UserHandle
 import android.platform.test.annotations.EnableFlags
 import android.util.LongSparseArray
@@ -429,6 +430,7 @@
                 whenever(disabledMessage).thenReturn("")
                 whenever(disabledReason).thenReturn(0)
                 whenever(persons).thenReturn(EMPTY_PERSON_ARRAY)
+                whenever(userHandle).thenReturn(Process.myUserHandle())
             }
         mIconRequestInfos = mutableListOf()
         // Make sure shortcuts map has expected key from expected package
diff --git a/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt b/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
index 9409ac1..60385a7 100644
--- a/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
+++ b/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
@@ -15,137 +15,105 @@
  */
 package com.android.launcher3.nonquickstep
 
-import android.content.Context
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import androidx.test.platform.app.InstrumentationRegistry
 import com.android.launcher3.AbstractDeviceProfileTest
 import com.android.launcher3.DeviceProfile
+import com.android.launcher3.Flags
 import com.android.launcher3.InvariantDeviceProfile
-import com.google.common.truth.Truth.assertThat
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
 
 /** Tests for DeviceProfile. */
 @SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(Parameterized::class)
 class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
     private val folderName: String = "DeviceProfileDumpTest"
-    @Test
-    fun phonePortrait3Button() {
-        initializeVarsForPhone(deviceSpecs["phone"]!!, isGestureMode = false)
-        val dp = getDeviceProfileForGrid("5_by_5")
 
-        assertDump(dp, "phonePortrait3Button")
+    @Parameterized.Parameter lateinit var instance: TestCase
+
+    @Before
+    fun setUp() {
+        if (instance.decoupleDepth) {
+            setFlagsRule.enableFlags(Flags.FLAG_ENABLE_SCALING_REVEAL_HOME_ANIMATION)
+        } else {
+            setFlagsRule.disableFlags(Flags.FLAG_ENABLE_SCALING_REVEAL_HOME_ANIMATION)
+        }
     }
 
     @Test
-    fun phonePortrait() {
-        initializeVarsForPhone(deviceSpecs["phone"]!!)
-        val dp = getDeviceProfileForGrid("5_by_5")
+    fun dumpPortraitGesture() {
+        initializeDevice(instance.deviceName, isGestureMode = true, isLandscape = false)
+        val dp = getDeviceProfileForGrid(instance.gridName)
+        dp.isTaskbarPresentInApps = instance.isTaskbarPresentInApps
 
-        assertDump(dp, "phonePortrait")
+        assertDump(dp, instance.filename("Portrait"))
     }
 
     @Test
-    fun phoneVerticalBar3Button() {
-        initializeVarsForPhone(deviceSpecs["phone"]!!, isVerticalBar = true, isGestureMode = false)
-        val dp = getDeviceProfileForGrid("5_by_5")
+    fun dumpPortrait3Button() {
+        initializeDevice(instance.deviceName, isGestureMode = false, isLandscape = false)
+        val dp = getDeviceProfileForGrid(instance.gridName)
+        dp.isTaskbarPresentInApps = instance.isTaskbarPresentInApps
 
-        assertDump(dp, "phoneVerticalBar3Button")
+        assertDump(dp, instance.filename("Portrait3Button"))
     }
 
     @Test
-    fun phoneVerticalBar() {
-        initializeVarsForPhone(deviceSpecs["phone"]!!, isVerticalBar = true)
-        val dp = getDeviceProfileForGrid("5_by_5")
+    fun dumpLandscapeGesture() {
+        initializeDevice(instance.deviceName, isGestureMode = true, isLandscape = true)
+        val dp = getDeviceProfileForGrid(instance.gridName)
+        dp.isTaskbarPresentInApps = instance.isTaskbarPresentInApps
 
-        assertDump(dp, "phoneVerticalBar")
+        val testName =
+            if (instance.deviceName == "phone") {
+                "VerticalBar"
+            } else {
+                "Landscape"
+            }
+        assertDump(dp, instance.filename(testName))
     }
 
     @Test
-    fun tabletLandscape3Button() {
-        initializeVarsForTablet(deviceSpecs["tablet"]!!, isLandscape = true, isGestureMode = false)
-        val dp = getDeviceProfileForGrid("6_by_5")
-        dp.isTaskbarPresentInApps = true
+    fun dumpLandscape3Button() {
+        initializeDevice(instance.deviceName, isGestureMode = false, isLandscape = true)
+        val dp = getDeviceProfileForGrid(instance.gridName)
+        dp.isTaskbarPresentInApps = instance.isTaskbarPresentInApps
 
-        assertDump(dp, "tabletLandscape3Button")
+        val testName =
+            if (instance.deviceName == "phone") {
+                "VerticalBar3Button"
+            } else {
+                "Landscape3Button"
+            }
+        assertDump(dp, instance.filename(testName))
     }
 
-    @Test
-    fun tabletLandscape() {
-        initializeVarsForTablet(deviceSpecs["tablet"]!!, isLandscape = true)
-        val dp = getDeviceProfileForGrid("6_by_5")
-        dp.isTaskbarPresentInApps = true
-
-        assertDump(dp, "tabletLandscape")
-    }
-
-    @Test
-    fun tabletPortrait3Button() {
-        initializeVarsForTablet(deviceSpecs["tablet"]!!, isGestureMode = false)
-        val dp = getDeviceProfileForGrid("6_by_5")
-        dp.isTaskbarPresentInApps = true
-
-        assertDump(dp, "tabletPortrait3Button")
-    }
-
-    @Test
-    fun tabletPortrait() {
-        initializeVarsForTablet(deviceSpecs["tablet"]!!)
-        val dp = getDeviceProfileForGrid("6_by_5")
-        dp.isTaskbarPresentInApps = true
-
-        assertDump(dp, "tabletPortrait")
-    }
-
-    @Test
-    fun twoPanelLandscape3Button() {
-        initializeVarsForTwoPanel(
-            deviceSpecs["twopanel-tablet"]!!,
-            deviceSpecs["twopanel-phone"]!!,
-            isLandscape = true,
-            isGestureMode = false
-        )
-        val dp = getDeviceProfileForGrid("4_by_4")
-        dp.isTaskbarPresentInApps = true
-
-        assertDump(dp, "twoPanelLandscape3Button")
-    }
-
-    @Test
-    fun twoPanelLandscape() {
-        initializeVarsForTwoPanel(
-            deviceSpecs["twopanel-tablet"]!!,
-            deviceSpecs["twopanel-phone"]!!,
-            isLandscape = true
-        )
-        val dp = getDeviceProfileForGrid("4_by_4")
-        dp.isTaskbarPresentInApps = true
-
-        assertDump(dp, "twoPanelLandscape")
-    }
-
-    @Test
-    fun twoPanelPortrait3Button() {
-        initializeVarsForTwoPanel(
-            deviceSpecs["twopanel-tablet"]!!,
-            deviceSpecs["twopanel-phone"]!!,
-            isGestureMode = false
-        )
-        val dp = getDeviceProfileForGrid("4_by_4")
-        dp.isTaskbarPresentInApps = true
-
-        assertDump(dp, "twoPanelPortrait3Button")
-    }
-
-    @Test
-    fun twoPanelPortrait() {
-        initializeVarsForTwoPanel(deviceSpecs["twopanel-tablet"]!!, deviceSpecs["twopanel-phone"]!!)
-        val dp = getDeviceProfileForGrid("4_by_4")
-        dp.isTaskbarPresentInApps = true
-
-        assertDump(dp, "twoPanelPortrait")
+    private fun initializeDevice(deviceName: String, isGestureMode: Boolean, isLandscape: Boolean) {
+        val deviceSpec = deviceSpecs[instance.deviceName]!!
+        when (deviceName) {
+            "twopanel-phone",
+            "twopanel-tablet" ->
+                initializeVarsForTwoPanel(
+                    deviceSpecUnfolded = deviceSpecs["twopanel-tablet"]!!,
+                    deviceSpecFolded = deviceSpecs["twopanel-phone"]!!,
+                    isLandscape = isLandscape,
+                    isGestureMode = isGestureMode,
+                )
+            "tablet" ->
+                initializeVarsForTablet(
+                    deviceSpec = deviceSpec,
+                    isLandscape = isLandscape,
+                    isGestureMode = isGestureMode
+                )
+            else ->
+                initializeVarsForPhone(
+                    deviceSpec = deviceSpec,
+                    isVerticalBar = isLandscape,
+                    isGestureMode = isGestureMode
+                )
+        }
     }
 
     private fun getDeviceProfileForGrid(gridName: String): DeviceProfile {
@@ -153,6 +121,48 @@
     }
 
     private fun assertDump(dp: DeviceProfile, filename: String) {
-        assertDump(dp, folderName, filename);
+        assertDump(dp, folderName, filename)
+    }
+
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getInstances(): List<TestCase> {
+            return listOf(
+                TestCase("phone", gridName = "5_by_5"),
+                TestCase("tablet", gridName = "6_by_5", isTaskbarPresentInApps = true),
+                TestCase("twopanel-tablet", gridName = "4_by_4", isTaskbarPresentInApps = true),
+                TestCase(
+                    "twopanel-tablet",
+                    gridName = "4_by_4",
+                    isTaskbarPresentInApps = true,
+                    decoupleDepth = true
+                ),
+            )
+        }
+
+        data class TestCase(
+            val deviceName: String,
+            val gridName: String,
+            val isTaskbarPresentInApps: Boolean = false,
+            val decoupleDepth: Boolean = false
+        ) {
+            fun filename(testName: String = ""): String {
+                val device =
+                    when (deviceName) {
+                        "tablet" -> "tablet"
+                        "twopanel-tablet" -> "twoPanel"
+                        "twopanel-phone" -> "twoPanelFolded"
+                        else -> "phone"
+                    }
+                val depth =
+                    if (decoupleDepth) {
+                        "_decoupleDepth"
+                    } else {
+                        ""
+                    }
+                return "$device$testName$depth"
+            }
+        }
     }
 }
diff --git a/tests/src/com/android/launcher3/popup/SystemShortcutTest.java b/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
index e459956..c663be0 100644
--- a/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
+++ b/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
@@ -30,6 +30,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -56,6 +57,7 @@
 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.TestSandboxModelContextWrapper;
@@ -87,6 +89,7 @@
     private PopupDataProvider mPopupDataProvider;
     private AppInfo mAppInfo;
     @Mock UserCache mUserCache;
+    @Mock ApiWrapper mApiWrapper;
     @Mock BaseDragLayer mBaseDragLayer;
     @Mock UserIconInfo mUserIconInfo;
     @Mock LauncherActivityInfo mLauncherActivityInfo;
@@ -97,6 +100,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mSandboxContext.putObject(UserCache.INSTANCE, mUserCache);
+        mSandboxContext.putObject(ApiWrapper.INSTANCE, mApiWrapper);
         mTestContext = new TestSandboxModelContextWrapper(mSandboxContext);
         mView = new View(mSandboxContext);
         spyOn(mTestContext);
@@ -244,7 +248,6 @@
         SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
                 .getShortcut(mTestContext, mAppInfo, mView);
 
-        verify(mPrivateProfileManager, times(2)).getProfileUser();
         assertNull(systemShortcut);
     }
 
@@ -266,8 +269,7 @@
         SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
                 .getShortcut(mTestContext, mAppInfo, mView);
 
-        verify(mPrivateProfileManager, times(3)).getProfileUser();
-        verify(mPrivateProfileManager).isEnabled();
+        verify(mPrivateProfileManager, atLeast(1)).isEnabled();
         assertNotNull(systemShortcut);
     }
 
diff --git a/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java b/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
index 70a3dd0..69b42cb 100644
--- a/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
@@ -76,7 +76,6 @@
         String output =
                 mDevice.executeShellCommand(
                         "pm create-user --profileOf 0 --managed TestProfile");
-        // b/203817455
         updateWorkProfileSetupSuccessful("pm create-user", output);
 
         String[] tokens = output.split("\\s+");
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
index 9dbd866..9b184ae 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
@@ -32,7 +32,6 @@
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
 import com.android.launcher3.ui.TestViewHelpers;
-import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.launcher3.util.rule.ShellCommandRule;
 import com.android.launcher3.util.rule.TestStabilityRule.Stability;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
@@ -54,7 +53,6 @@
 
     @Test
     @PortraitLandscape
-    @ScreenRecord // b/316910614
     public void testDragIcon() throws Throwable {
         mLauncher.enableDebugTracing(); // b/289161193
         commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext));
@@ -107,7 +105,6 @@
     @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/316910614
     @PlatinumTest(focusArea = "launcher")
     @Test
-    @ScreenRecord // b/316910614
     public void testResizeWidget() throws Throwable {
         commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext));
 
diff --git a/tests/src/com/android/launcher3/util/PackageManagerHelperTest.java b/tests/src/com/android/launcher3/util/PackageManagerHelperTest.java
index d1da5f4..b5e797e 100644
--- a/tests/src/com/android/launcher3/util/PackageManagerHelperTest.java
+++ b/tests/src/com/android/launcher3/util/PackageManagerHelperTest.java
@@ -34,6 +34,7 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -63,6 +64,8 @@
         mContext = mock(Context.class);
         mLauncherApps = mock(LauncherApps.class);
         when(mContext.getSystemService(eq(LauncherApps.class))).thenReturn(mLauncherApps);
+        when(mContext.getResources()).thenReturn(
+                InstrumentationRegistry.getInstrumentation().getTargetContext().getResources());
         mPackageManagerHelper = new PackageManagerHelper(mContext);
     }
 
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 68829e0..2e3944d 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -359,6 +359,21 @@
     }
 
     /**
+     * Gets Overview Actions specific to grouped tasks.
+     *
+     * @return The Overview group actions bar
+     */
+    @NonNull
+    public OverviewActions getOverviewGroupActions() {
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to get overview group actions")) {
+            verifyActiveContainer();
+            UiObject2 groupActions = mLauncher.waitForOverviewObject("group_action_buttons");
+            return new OverviewActions(groupActions, mLauncher);
+        }
+    }
+
+    /**
      * Returns if clear all button is visible.
      */
     public boolean isClearAllVisible() {
@@ -449,10 +464,18 @@
     private void verifyActionsViewVisibility() {
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "want to assert overview actions view visibility")) {
+            boolean isTablet = mLauncher.isTablet();
+            OverviewTask task = isTablet ? getFocusedTaskForTablet() : getCurrentTask();
+
             if (isActionsViewVisible()) {
-                mLauncher.waitForOverviewObject("action_buttons");
+                if (task.isTaskSplit()) {
+                    mLauncher.waitForOverviewObject("group_action_buttons");
+                } else {
+                    mLauncher.waitForOverviewObject("action_buttons");
+                }
             } else {
                 mLauncher.waitUntilOverviewObjectGone("action_buttons");
+                mLauncher.waitUntilOverviewObjectGone("group_action_buttons");
             }
         }
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index aa8d339..68b0a36 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -108,7 +108,7 @@
 public final class LauncherInstrumentation {
 
     private static final String TAG = "Tapl";
-    private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 15;
+    private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 5;
     private static final int GESTURE_STEP_MS = 16;
 
     static final Pattern EVENT_PILFER_POINTERS = Pattern.compile("pilferPointers");
diff --git a/tests/tapl/com/android/launcher3/tapl/PrivateSpaceContainer.java b/tests/tapl/com/android/launcher3/tapl/PrivateSpaceContainer.java
index c21bae3..a2814f0 100644
--- a/tests/tapl/com/android/launcher3/tapl/PrivateSpaceContainer.java
+++ b/tests/tapl/com/android/launcher3/tapl/PrivateSpaceContainer.java
@@ -25,7 +25,7 @@
  */
 public class PrivateSpaceContainer {
     private static final String PS_HEADER_RES_ID = "ps_header_layout";
-    private static final String INSTALL_APP_TITLE = "Install apps";
+    private static final String INSTALL_APP_TITLE = "Install";
     private static final String DIVIDER_RES_ID = "private_space_divider";
 
     private final LauncherInstrumentation mLauncher;
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
index 8d3a631..4be46ab 100644
--- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
 
+import android.text.TextUtils;
 import android.widget.TextView;
 
 import androidx.test.uiautomator.By;
@@ -117,4 +118,28 @@
     public SearchResultFromQsb getSearchResultForInput() {
         return this;
     }
+
+    /** Verify a tile is present by checking its title and subtitle. */
+    public void verifyTileIsPresent(String title, String subtitle) {
+        ArrayList<UiObject2> searchResults =
+                new ArrayList<>(mLauncher.waitForObjectsInContainer(
+                        mLauncher.waitForSystemLauncherObject(SEARCH_CONTAINER_RES_ID),
+                        By.clazz(TextView.class)));
+        boolean foundTitle = false;
+        boolean foundSubtitle = false;
+        for (UiObject2 uiObject: searchResults) {
+            String currentString = uiObject.getText();
+            if (TextUtils.equals(currentString, title)) {
+                foundTitle = true;
+            } else if (TextUtils.equals(currentString, subtitle)) {
+                foundSubtitle = true;
+            }
+        }
+        if (!foundTitle) {
+            mLauncher.fail("Tile not found for title: " + title);
+        }
+        if (!foundSubtitle) {
+            mLauncher.fail("Tile not found for subtitle: " + subtitle);
+        }
+    }
 }