Merge "Check for isAttachedToWindow before dispatching touch event" into main
diff --git a/Android.bp b/Android.bp
index a4a058f..85632b2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -25,6 +25,7 @@
     name: "launcher-non-platform-apis-defaults",
     static_libs: [
         "android.os.flags-aconfig-java",
+        "android.multiuser.flags-aconfig-java",
         "android.appwidget.flags-aconfig-java",
         "com.android.window.flags.window-aconfig-java",
     ],
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index 4f5b1a0..9f505a4 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -235,7 +235,7 @@
 
 flag {
     name: "enable_fallback_overview_in_window"
-    namespace: "launcher"
+    namespace: "lse_desktop_experience"
     description: "Enables fallback recents opening inside of a window instead of an activity."
     bug: "292269949"
 }
@@ -450,14 +450,14 @@
 
 flag {
     name: "enable_recents_window_proto_log"
-    namespace: "launcher"
+    namespace: "lse_desktop_experience"
     description: "Enables tracking recents window logs in ProtoLog"
     bug: "292269949"
 }
 
 flag {
     name: "enable_state_manager_proto_log"
-    namespace: "launcher"
+    namespace: "lse_desktop_experience"
     description: "Enables tracking state manager logs in ProtoLog"
     bug: "292269949"
 }
@@ -488,7 +488,7 @@
 
 flag {
     name: "enable_launcher_overview_in_window"
-    namespace: "launcher"
+    namespace: "lse_desktop_experience"
     description: "Enables launcher recents opening inside of a window instead of being hosted in launcher activity."
     bug: "292269949"
 }
@@ -518,13 +518,6 @@
 }
 
 flag {
-    name: "taskbar_recents_layout_transition"
-    namespace: "launcher"
-    description: "Enable Taskbar LayoutTransition for Recent Apps"
-    bug: "343521765"
-}
-
-flag {
     name: "enable_pinning_app_with_context_menu"
     namespace: "launcher"
     description: "Add options to pin/unpin to taskbar to app context menus."
@@ -629,3 +622,47 @@
   description: "Enable Alt + Tab KQS view to show apps in flattened structure"
   bug: "382769617"
 }
+
+flag {
+  name: "enable_gesture_nav_on_connected_displays"
+  namespace: "lse_desktop_experience"
+  description: "Enables gesture navigation handling on connected displays"
+  bug: "382130680"
+}
+
+flag {
+  name: "enable_taskbar_behind_shade"
+  namespace: "lse_desktop_experience"
+  description: "Keeps taskbar behind notification shade when its pulled down"
+  bug: "343194358"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "enable_scalability_for_desktop_experience"
+  namespace: "launcher"
+  description: "Enable more grid scale options on the launcher for desktop experience"
+  bug: "375491272"
+}
+
+flag {
+  name: "enable_gesture_nav_horizontal_touch_slop"
+  namespace: "launcher"
+  description: "Enables horizontal touch slop checking in non-vertical fling navigation gestures"
+  bug: "394364217"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "sync_app_launch_with_taskbar_stash"
+  namespace: "launcher"
+  description: "Syncs the two animations (app launch, taskbar stash) so they play at the same time."
+  bug: "319162553"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/aconfig/launcher_growth.aconfig b/aconfig/launcher_growth.aconfig
new file mode 100644
index 0000000..a880538
--- /dev/null
+++ b/aconfig/launcher_growth.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.launcher3"
+container: "system_ext"
+
+flag {
+    name: "enable_growth_nudge"
+    namespace: "desktop_oobe"
+    description: "Add growth nudge in launcher"
+    bug: "396165728"
+}
diff --git a/aconfig/launcher_overview.aconfig b/aconfig/launcher_overview.aconfig
index 5749c51..d27a214 100644
--- a/aconfig/launcher_overview.aconfig
+++ b/aconfig/launcher_overview.aconfig
@@ -5,14 +5,14 @@
     name: "enable_grid_only_overview"
     namespace: "launcher_overview"
     description: "Enable a grid-only overview without a focused task."
-    bug: "257950105"
+    bug: "360204325"
 }
 
 flag {
     name: "enable_overview_icon_menu"
     namespace: "launcher_overview"
     description: "Enable updated overview icon and menu within task."
-    bug: "257950105"
+    bug: "360205084"
 }
 
 flag {
@@ -110,3 +110,13 @@
     description: "Enable wallpaper background for desktop tasks in overview."
     bug: "363257721"
 }
+
+flag {
+    name: "enable_show_enabled_shortcuts_in_accessibility_menu"
+    namespace: "launcher_overview"
+    description: "Enables showing the same shortcuts in the Task menu as well as the accessibility actions menu"
+    bug: "383662632"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/go/quickstep/src/com/android/launcher3/util/MainThreadInitializedObject.java b/go/quickstep/src/com/android/launcher3/util/MainThreadInitializedObject.java
new file mode 100644
index 0000000..e1f3508
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 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 static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.content.Context;
+import android.os.Looper;
+
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Utility class for defining singletons which are initiated on main thread.
+ */
+public class MainThreadInitializedObject<T extends SafeCloseable> {
+
+    private final ObjectProvider<T> mProvider;
+    private T mValue;
+
+    public MainThreadInitializedObject(ObjectProvider<T> provider) {
+        mProvider = provider;
+    }
+
+    public T get(Context context) {
+        Context app = context.getApplicationContext();
+        if (app instanceof ObjectSandbox sc) {
+            return sc.getObject(this);
+        }
+
+        if (mValue == null) {
+            if (Looper.myLooper() == Looper.getMainLooper()) {
+                mValue = TraceHelper.allowIpcs("main.thread.object", () -> mProvider.get(app));
+            } else {
+                try {
+                    return MAIN_EXECUTOR.submit(() -> get(context)).get();
+                } catch (InterruptedException | ExecutionException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+        return mValue;
+    }
+
+    public interface ObjectProvider<T> {
+
+        T get(Context context);
+    }
+
+    /** Sandbox for isolating {@link MainThreadInitializedObject} instances from Launcher. */
+    public interface ObjectSandbox {
+
+        /**
+         * Find a cached object from mObjectMap if we have already created one. If not, generate
+         * an object using the provider.
+         */
+        <T extends SafeCloseable> T getObject(MainThreadInitializedObject<T> object);
+    }
+}
diff --git a/quickstep/res/color/app_chip_menu_item_color_fg.xml b/quickstep/res/color/app_chip_menu_item_color_fg.xml
new file mode 100644
index 0000000..fa1dc34
--- /dev/null
+++ b/quickstep/res/color/app_chip_menu_item_color_fg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2025 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.
+  -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:alpha="0.15" android:color="@color/materialColorOnSurface" android:state_enabled="true" android:state_pressed="true" />
+    <item android:alpha="0.11" android:color="@color/materialColorOnSurface" android:state_enabled="true" android:state_hovered="true" />
+    <item android:color="@android:color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/quickstep/res/color/app_chip_state_color_fg.xml b/quickstep/res/color/app_chip_state_color_fg.xml
new file mode 100644
index 0000000..58dfee0
--- /dev/null
+++ b/quickstep/res/color/app_chip_state_color_fg.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 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.
+  -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:alpha="0.15" android:color="@color/materialColorOnSurface" android:state_enabled="true" android:state_pressed="true" />
+    <item android:alpha="0.11" android:color="@color/materialColorOnSurface" android:state_enabled="true" android:state_hovered="true" />
+    <item android:color="@android:color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/quickstep/res/color/keyboard_quick_switch_scroll_button_bg.xml b/quickstep/res/color/keyboard_quick_switch_scroll_button_bg.xml
new file mode 100644
index 0000000..1592055
--- /dev/null
+++ b/quickstep/res/color/keyboard_quick_switch_scroll_button_bg.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+        android:color="@color/materialColorPrimary"
+        android:alpha="0.38"/>
+    <item android:color="@color/materialColorPrimary"/>
+</selector>
\ No newline at end of file
diff --git a/quickstep/res/color/keyboard_quick_switch_scroll_button_fg.xml b/quickstep/res/color/keyboard_quick_switch_scroll_button_fg.xml
new file mode 100644
index 0000000..051c18f
--- /dev/null
+++ b/quickstep/res/color/keyboard_quick_switch_scroll_button_fg.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="true"
+        android:state_pressed="true"
+        android:color="@color/materialColorOnPrimary"
+        android:alpha="0.15"/>
+    <item android:state_enabled="true"
+        android:state_hovered="true"
+        android:color="@color/materialColorOnPrimary"
+        android:alpha="0.11" />
+    <item android:color="@android:color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/quickstep/res/color/keyboard_quick_switch_scroll_button_icon.xml b/quickstep/res/color/keyboard_quick_switch_scroll_button_icon.xml
new file mode 100644
index 0000000..74df84b
--- /dev/null
+++ b/quickstep/res/color/keyboard_quick_switch_scroll_button_icon.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2025 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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+        android:color="@color/materialColorOnPrimary"
+        android:alpha="0.38"/>
+    <item android:color="@color/materialColorOnPrimary"/>
+</selector>
diff --git a/quickstep/res/drawable/app_chip_fg.xml b/quickstep/res/drawable/app_chip_fg.xml
new file mode 100644
index 0000000..7b19c9e
--- /dev/null
+++ b/quickstep/res/drawable/app_chip_fg.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@color/app_chip_state_color_fg" />
+</shape>
\ No newline at end of file
diff --git a/quickstep/res/drawable/app_chip_menu_bg.xml b/quickstep/res/drawable/app_chip_menu_bg.xml
new file mode 100644
index 0000000..499056e
--- /dev/null
+++ b/quickstep/res/drawable/app_chip_menu_bg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@color/materialColorSurfaceBright"/>
+    <corners android:radius="@dimen/task_menu_corner_radius"/>
+</shape>
diff --git a/quickstep/res/drawable/app_chip_menu_item_bg.xml b/quickstep/res/drawable/app_chip_menu_item_bg.xml
new file mode 100644
index 0000000..39e88d2
--- /dev/null
+++ b/quickstep/res/drawable/app_chip_menu_item_bg.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@color/materialColorSurfaceBright" />
+</shape>
diff --git a/quickstep/res/drawable/app_chip_menu_item_fg.xml b/quickstep/res/drawable/app_chip_menu_item_fg.xml
new file mode 100644
index 0000000..96d067d
--- /dev/null
+++ b/quickstep/res/drawable/app_chip_menu_item_fg.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@color/app_chip_menu_item_color_fg" />
+    <corners android:radius="@dimen/task_menu_item_corner_radius" />
+</shape>
diff --git a/quickstep/res/drawable/bg_keyboard_quick_switch_scroll_button.xml b/quickstep/res/drawable/bg_keyboard_quick_switch_scroll_button.xml
new file mode 100644
index 0000000..7067f13
--- /dev/null
+++ b/quickstep/res/drawable/bg_keyboard_quick_switch_scroll_button.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <solid android:color="@color/keyboard_quick_switch_scroll_button_bg" />
+    <corners android:radius="@dimen/keyboard_quick_switch_scroll_button_corner_radius" />
+</shape>
diff --git a/quickstep/res/drawable/fg_keyboard_quick_switch_scroll_button.xml b/quickstep/res/drawable/fg_keyboard_quick_switch_scroll_button.xml
new file mode 100644
index 0000000..dd63f54
--- /dev/null
+++ b/quickstep/res/drawable/fg_keyboard_quick_switch_scroll_button.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <solid android:color="@color/keyboard_quick_switch_scroll_button_fg" />
+    <corners android:radius="@dimen/keyboard_quick_switch_scroll_button_corner_radius" />
+</shape>
diff --git a/quickstep/res/drawable/ic_chevron_end.xml b/quickstep/res/drawable/ic_chevron_end.xml
new file mode 100644
index 0000000..9ca4f3a
--- /dev/null
+++ b/quickstep/res/drawable/ic_chevron_end.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:autoMirrored="true"
+    android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M504,480L320,296L376,240L616,480L376,720L320,664L504,480Z" />
+</vector>
diff --git a/quickstep/res/drawable/ic_chevron_start.xml b/quickstep/res/drawable/ic_chevron_start.xml
new file mode 100644
index 0000000..913da02
--- /dev/null
+++ b/quickstep/res/drawable/ic_chevron_start.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:autoMirrored="true"
+    android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M560,720L320,480L560,240L616,296L432,480L616,664L560,720Z" />
+</vector>
diff --git a/quickstep/res/layout/gesture_tutorial_fragment.xml b/quickstep/res/layout/gesture_tutorial_fragment.xml
index 64ad1f7..6e7ff86 100644
--- a/quickstep/res/layout/gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/gesture_tutorial_fragment.xml
@@ -153,16 +153,6 @@
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_fragment_feedback_title"/>
 
-        <com.android.quickstep.interaction.TutorialStepIndicator
-            android:id="@+id/gesture_tutorial_fragment_feedback_tutorial_step"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintTop_toTopOf="@id/gesture_tutorial_fragment_action_button"
-            app:layout_constraintBottom_toBottomOf="@id/gesture_tutorial_fragment_action_button"/>
-
         <Button
             android:id="@+id/gesture_tutorial_fragment_action_button"
             style="@style/TextAppearance.GestureTutorial.ButtonLabel"
diff --git a/quickstep/res/layout/icon_app_chip_view.xml b/quickstep/res/layout/icon_app_chip_view.xml
index 0972be1..de05d59 100644
--- a/quickstep/res/layout/icon_app_chip_view.xml
+++ b/quickstep/res/layout/icon_app_chip_view.xml
@@ -26,6 +26,7 @@
     android:importantForAccessibility="no"
     android:autoMirrored="true"
     android:elevation="@dimen/task_thumbnail_icon_menu_elevation"
+    android:foreground="@drawable/app_chip_fg"
     android:background="@color/materialColorSurfaceBright">
 
     <!-- ignoring warning because the user of the anchor is a Rect where RTL is not needed -->
@@ -77,4 +78,5 @@
         android:background="@drawable/icon_menu_arrow_background"
         android:src="@drawable/ic_chevron_down"
         android:importantForAccessibility="no" />
+
 </com.android.quickstep.views.IconAppChipView>
\ No newline at end of file
diff --git a/quickstep/res/layout/keyboard_quick_switch_view.xml b/quickstep/res/layout/keyboard_quick_switch_view.xml
index 345b97c..2dea79c 100644
--- a/quickstep/res/layout/keyboard_quick_switch_view.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_view.xml
@@ -18,6 +18,8 @@
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/keyboard_quick_switch_view"
+    android:contentDescription="@string/quick_switch_content_description"
+    android:accessibilityPaneTitle="@string/quick_switch_pane_title"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_marginTop="@dimen/keyboard_quick_switch_margin_top"
@@ -67,6 +69,24 @@
 
     </androidx.constraintlayout.widget.ConstraintLayout>
 
+    <ImageButton
+        android:id="@+id/scroll_button_start"
+        android:src="@drawable/ic_chevron_start"
+        android:contentDescription="@string/quick_switch_scroll_arrow_left"
+        android:background="@drawable/bg_keyboard_quick_switch_scroll_button"
+        android:foreground="@drawable/fg_keyboard_quick_switch_scroll_button"
+        android:tint="@color/keyboard_quick_switch_scroll_button_icon"
+        android:layout_width="@dimen/keyboard_quick_switch_scroll_button_width"
+        android:layout_height="@dimen/keyboard_quick_switch_scroll_button_height"
+        android:paddingHorizontal="@dimen/keyboard_quick_switch_scroll_button_horizontal_padding"
+        android:paddingVertical="@dimen/keyboard_quick_switch_scroll_button_vertical_padding"
+        android:layout_marginStart="@dimen/keyboard_quick_switch_view_spacing"
+        android:visibility="gone"
+
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"/>
+
     <HorizontalScrollView
         android:id="@+id/scroll_view"
         android:layout_width="wrap_content"
@@ -76,9 +96,12 @@
         android:alpha="0"
         android:visibility="gone"
 
+        app:layout_constraintHorizontal_bias="0"
+        app:layout_constrainedWidth="true"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintStart_toStartOf="parent">
+        app:layout_constraintStart_toEndOf="@id/scroll_button_start"
+        app:layout_constraintEnd_toStartOf="@id/scroll_button_end">
 
         <androidx.constraintlayout.widget.ConstraintLayout
             android:id="@+id/content"
@@ -89,4 +112,22 @@
 
     </HorizontalScrollView>
 
+    <ImageButton
+        android:id="@+id/scroll_button_end"
+        android:src="@drawable/ic_chevron_end"
+        android:contentDescription="@string/quick_switch_scroll_arrow_right"
+        android:background="@drawable/bg_keyboard_quick_switch_scroll_button"
+        android:foreground="@drawable/fg_keyboard_quick_switch_scroll_button"
+        android:tint="@color/keyboard_quick_switch_scroll_button_icon"
+        android:layout_width="@dimen/keyboard_quick_switch_scroll_button_width"
+        android:layout_height="@dimen/keyboard_quick_switch_scroll_button_height"
+        android:paddingHorizontal="@dimen/keyboard_quick_switch_scroll_button_horizontal_padding"
+        android:paddingVertical="@dimen/keyboard_quick_switch_scroll_button_vertical_padding"
+        android:layout_marginEnd="@dimen/keyboard_quick_switch_view_spacing"
+        android:visibility="gone"
+
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintEnd_toEndOf="parent" />
+
 </com.android.launcher3.taskbar.KeyboardQuickSwitchView>
diff --git a/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml b/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml
index 7530c28..55fe2b8 100644
--- a/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml
@@ -137,6 +137,7 @@
         android:layout_above="@id/gesture_tutorial_fragment_action_button"
         android:layout_centerHorizontal="true"
         android:background="@android:color/transparent"
+        android:screenReaderFocusable="true"
         android:paddingTop="24dp"
         android:paddingHorizontal="24dp"
         android:layout_marginBottom="16dp">
@@ -146,8 +147,6 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginTop="104dp"
-            android:accessibilityHeading="true"
-            android:accessibilityTraversalBefore="@id/gesture_tutorial_fragment_feedback_subtitle"
             android:gravity="top"
             android:lineSpacingExtra="-1sp"
             android:textAppearance="@style/TextAppearance.GestureTutorial.MainTitle"
@@ -162,22 +161,10 @@
             android:layout_marginTop="24dp"
             android:lineSpacingExtra="4sp"
             android:textAppearance="@style/TextAppearance.GestureTutorial.MainSubtitle"
-            android:accessibilityTraversalAfter="@id/gesture_tutorial_fragment_feedback_title"
-            android:accessibilityTraversalBefore="@id/gesture_tutorial_fragment_action_button"
 
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_fragment_feedback_title" />
 
-        <com.android.quickstep.interaction.TutorialStepIndicator
-            android:id="@+id/gesture_tutorial_fragment_feedback_tutorial_step"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toTopOf="parent" />
-
         <Button
             android:id="@+id/gesture_tutorial_fragment_close_button"
             style="@style/TextAppearance.GestureTutorial.Feedback.Subtext"
@@ -228,7 +215,6 @@
         android:layout_alignParentBottom="true"
         android:layout_alignParentEnd="true"
         android:clickable="true"
-        android:focusableInTouchMode="true"
         android:accessibilityTraversalAfter="@id/gesture_tutorial_fragment_feedback_subtitle"
         android:contentDescription="@string/gesture_tutorial_action_button_label"
         android:background="@drawable/gesture_tutorial_action_button_background"
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index 3aac1b6..4abfbbe 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -29,8 +29,8 @@
     launcher:hoverBorderColor="@color/materialColorPrimary">
 
     <ViewStub
-        android:id="@+id/snapshot"
-        android:inflatedId="@id/snapshot"
+        android:id="@+id/task_content_view"
+        android:inflatedId="@id/task_content_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
 
diff --git a/quickstep/res/layout/task_content_view.xml b/quickstep/res/layout/task_content_view.xml
new file mode 100644
index 0000000..9055ccd
--- /dev/null
+++ b/quickstep/res/layout/task_content_view.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 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.task.thumbnail.TaskContentView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/task_content_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" />
\ No newline at end of file
diff --git a/quickstep/res/layout/task_grouped.xml b/quickstep/res/layout/task_grouped.xml
index 3e6f5ed..a7c4856 100644
--- a/quickstep/res/layout/task_grouped.xml
+++ b/quickstep/res/layout/task_grouped.xml
@@ -34,14 +34,14 @@
     launcher:hoverBorderColor="@color/materialColorPrimary">
 
     <ViewStub
-        android:id="@+id/snapshot"
-        android:inflatedId="@id/snapshot"
+        android:id="@+id/task_content_view"
+        android:inflatedId="@id/task_content_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
 
     <ViewStub
-        android:id="@+id/bottomright_snapshot"
-        android:inflatedId="@id/bottomright_snapshot"
+        android:id="@+id/bottomright_task_content_view"
+        android:inflatedId="@id/bottomright_task_content_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
 
diff --git a/quickstep/res/layout/task_thumbnail_view_header.xml b/quickstep/res/layout/task_header_view.xml
similarity index 78%
rename from quickstep/res/layout/task_thumbnail_view_header.xml
rename to quickstep/res/layout/task_header_view.xml
index 70e4a42..ea5c24e 100644
--- a/quickstep/res/layout/task_thumbnail_view_header.xml
+++ b/quickstep/res/layout/task_header_view.xml
@@ -13,12 +13,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.quickstep.views.TaskThumbnailViewHeader
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.quickstep.views.TaskHeaderView 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:id="@+id/task_thumbnail_view_header"
+    android:id="@+id/task_header_view"
     android:background="@drawable/task_thumbnail_header_bg">
 
     <androidx.constraintlayout.widget.ConstraintLayout
@@ -38,22 +37,17 @@
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintEnd_toStartOf="@id/header_app_title"
             app:layout_constraintTop_toTopOf="parent"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintHorizontal_bias="0"
-            app:layout_constraintVertical_bias="0.5"
-            app:layout_constraintHorizontal_chainStyle="spread_inside" />
+            app:layout_constraintBottom_toBottomOf="parent" />
         <TextView
             android:id="@+id/header_app_title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/task_thumbnail_header_margin_between_views"
-            android:layout_marginEnd="@dimen/task_thumbnail_header_margin_between_views"
+            android:maxLines="1"
             android:text="@string/header_default_app_title"
-            app:layout_constraintStart_toEndOf="@id/header_app_icon"
-            app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintHorizontal_bias="0"
-            app:layout_constraintVertical_bias="0.5" />
+            app:layout_constraintStart_toEndOf="@id/header_app_icon"
+            app:layout_constraintTop_toTopOf="parent" />
         <ImageButton
             android:id="@+id/header_close_button"
             android:contentDescription="@string/header_close_icon_description"
@@ -66,7 +60,7 @@
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintHorizontal_bias="1"
-            app:layout_constraintVertical_bias="0.5" />
+            app:layout_constraintStart_toEndOf="@id/header_app_title"
+            app:layout_constraintHorizontal_bias="1" />
     </androidx.constraintlayout.widget.ConstraintLayout>
-</com.android.quickstep.views.TaskThumbnailViewHeader>
+</com.android.quickstep.views.TaskHeaderView>
diff --git a/quickstep/res/layout/task_menu.xml b/quickstep/res/layout/task_menu.xml
index b6d8786..abc728a 100644
--- a/quickstep/res/layout/task_menu.xml
+++ b/quickstep/res/layout/task_menu.xml
@@ -43,8 +43,7 @@
             android:id="@+id/menu_option_layout"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:orientation="horizontal"
-            android:showDividers="middle" />
+            android:orientation="horizontal" />
 
     </ScrollView>
 
diff --git a/quickstep/res/layout/task_thumbnail.xml b/quickstep/res/layout/task_thumbnail.xml
index 3b96615..8280e13 100644
--- a/quickstep/res/layout/task_thumbnail.xml
+++ b/quickstep/res/layout/task_thumbnail.xml
@@ -17,7 +17,8 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/snapshot"
     android:layout_width="match_parent"
-    android:layout_height="match_parent" >
+    android:layout_height="0dp"
+    android:layout_weight="1" >
 
     <com.android.quickstep.views.FixedSizeImageView
         android:id="@+id/task_thumbnail"
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index c922238..7a6435b 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Taakbalkoorloop"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Skuif na links bo"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Skuif na regs onder"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{meer app}other{meer apps}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Werkskerm"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> en <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Appikoon"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Apptitel"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Maak Toe-knoppie"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index 97b0ac7..7286f3f 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"የተግባር አሞሌ ትርፍ ፍሰት"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ወደ ላይ/ግራ ይውሰዱ"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ወደ ታች/ቀኝ ይውሰዱ"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ተጨማሪ መተግበሪያ}one{ተጨማሪ መተግበሪያ}other{ተጨማሪ መተግበሪያዎች}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"ዴስክቶፕ"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> እና <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"የመተግበሪያ አዶ"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"የመተግበሪያ ርዕስ"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"የዝጋ አዝራር"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index 5b61fa7..e673ac6 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"القائمة الكاملة لشريط التطبيقات"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"الانتقال إلى يمين الشاشة أو أعلاها"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"الانتقال إلى يسار الشاشة أو أسفلها"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{تطبيق واحد آخر}zero{تطبيق آخر}two{تطبيقان آخران}few{تطبيقات أخرى}many{تطبيقًا آخر}other{تطبيق آخر}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"وضع الكمبيوتر المكتبي"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"\"<xliff:g id="APP_NAME_1">%1$s</xliff:g>\" و\"<xliff:g id="APP_NAME_2">%2$s</xliff:g>\""</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"رمز التطبيق"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"عنوان التطبيق"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"زر الإغلاق"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index d7cb836..4f3c22d 100644
--- a/quickstep/res/values-as/strings.xml
+++ b/quickstep/res/values-as/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"টাস্কবাৰ অ’ভাৰফ্ল"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ওপৰৰ বাঁওফাললৈ নিয়ক"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"তলৰ সোঁফাললৈ নিয়ক"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{অধিক এপ্‌}one{অধিক এপ্‌}other{অধিক এপ্‌}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"ডেস্কটপ"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> আৰু <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"এপৰ আইকন"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"এপৰ শিৰোনাম"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"বন্ধ কৰা বুটাম"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index dab4070..f7f22e2 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Tapşırıqlar Paneli üzrə əlavə menyu"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Yuxarı/sola köçürün"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Aşağı/sağa köçürün"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{əlavə tətbiq}other{əlavə tətbiq}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Masaüstü"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> və <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Tətbiq ikonası"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Tətbiq başlığı"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Qapatma düyməsi"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index 58a9c6b..67ecbcd 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Preklopna traka zadataka"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premesti gore levo"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premesti dole desno"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{dodatna aplikacija}one{dodatna aplikacija}few{dodatne aplikacije}other{dodatnih aplikacija}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Računar"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Ikona aplikacije"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Naziv aplikacije"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Dugme Zatvori"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index 5ea5761..436ed3f 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -90,8 +90,8 @@
     <string name="allset_title" msgid="5021126669778966707">"Гатова!"</string>
     <string name="allset_hint" msgid="459504134589971527">"Каб перайсці на галоўны экран, правядзіце пальцам уверх"</string>
     <string name="allset_button_hint" msgid="2395219947744706291">"Каб перайсці на галоўны экран, націсніце кнопку галоўнага экрана"</string>
-    <string name="allset_description_generic" msgid="5385500062202019855">"Вы можаце пачаць карыстанне прыладай \"<xliff:g id="DEVICE">%1$s</xliff:g>\""</string>
-    <string name="default_device_name" msgid="6660656727127422487">"прылада"</string>
+    <string name="allset_description_generic" msgid="5385500062202019855">"Вы можаце пачаць карыстацца <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+    <string name="default_device_name" msgid="6660656727127422487">"прыладай"</string>
     <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>
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Меню з пашырэннем панэлі задач"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Перамясціць уверх/улева"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Перамясціць уніз/управа"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{даступная праграма}one{даступная праграма}few{даступныя праграмы}many{даступных праграм}other{даступнай праграмы}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Працоўны стол"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> і <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Значок праграмы"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Назва праграмы"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Кнопка \"Закрыць\""</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index 5c01d29..922a473 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Меню при препълване на лентата на задачите"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Преместване горе/вляво"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Преместване долу/вдясно"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{допълнително приложение}other{допълнителни приложения}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Режим за настолни компютри"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> и <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Икона на приложението"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Име на приложението"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Бутон за затваряне"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index 01954cf..974994a 100644
--- a/quickstep/res/values-bn/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"টাস্কবার ওভারফ্লো"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"উপরে/বাঁদিকে সরান"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"নিচে/ডানদিকে সরান"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{আরও অ্যাপ}one{আরও অ্যাপ}other{আরও অ্যাপ}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"ডেস্কটপ"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ও <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"অ্যাপ আইকন"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"অ্যাপের শিরোনাম"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"বন্ধ করার বোতাম"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index 18c178b..c89f84a 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Preklopni meni trake zadataka"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premjesti gore lijevo"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premjesti dolje desno"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{dodatna aplikacija}one{dodatna aplikacija}few{dodatne aplikacije}other{dodatnih aplikacija}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Računar"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Ikona aplikacije"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Naslov aplikacije"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Dugme za zatvaranje"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index 23cd439..87591da 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Menú addicional de la barra de tasques"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mou a la part superior o a l\'esquerra"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mou a la part inferior o a la dreta"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplicació més}other{aplicacions més}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Escriptori"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Icona de l\'aplicació"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Títol de l\'aplicació"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Botó Tanca"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index d809cbe..6d53ca8 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Přetečení panelu aplikací"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Přesunout doleva nahoru"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Přesunout doprava dolů"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{další aplikace}few{další aplikace}many{další aplikace}other{dalších aplikací}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Počítač"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> a <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Ikona aplikace"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Název aplikace"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Tlačítko Zavřít"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index 4bae930..debea45 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Prikmenu på proceslinjen"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flyt til toppen eller venstre side"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flyt til bunden eller højre side"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{yderligere app}one{yderligere app}other{yderligere apps}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Computertilstand"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> og <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Appikon"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Apptitel"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Knappen Luk"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index 7986a55..b4d5ac1 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Dreipunkt-Menü der Taskleiste"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Nach oben / Nach links verschieben"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Nach unten / Nach rechts verschieben"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{weitere App}other{weitere Apps}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Desktopmodus"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> und <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"App-Symbol"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Titel der App"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Schaltfläche „Schließen“"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index 438dcf8..5dde857 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Υπερχείλιση γραμμής εργαλείων"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Μετακίνηση επάνω/αριστερά"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Μετακίνηση κάτω/δεξιά"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ακόμη εφαρμογή}other{ακόμη εφαρμογές}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Υπολογιστής"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> και <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Εικονίδιο εφαρμογής"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Τίτλος εφαρμογής"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Κουμπί κλεισίματος"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index 954db96..eef812c 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Taskbar overflow"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{more app}other{more apps}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> and <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"App icon"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"App title"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Close button"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml
index 4efd369..d1319ce 100644
--- a/quickstep/res/values-en-rCA/strings.xml
+++ b/quickstep/res/values-en-rCA/strings.xml
@@ -139,6 +139,7 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Taskbar Overflow"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
+    <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Open app as a bubble"</string>
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{more app}other{more apps}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> and <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +156,6 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"App icon"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"App title"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Close button"</string>
+    <string name="pin_to_taskbar" msgid="6607778046321626950">"Pin to taskbar"</string>
+    <string name="unpin_from_taskbar" msgid="2178811773165572676">"Unpin from taskbar"</string>
 </resources>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index 954db96..eef812c 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Taskbar overflow"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{more app}other{more apps}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> and <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"App icon"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"App title"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Close button"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index 954db96..eef812c 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Taskbar overflow"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{more app}other{more apps}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> and <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"App icon"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"App title"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Close button"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index 6cd544c..17e3a7d 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -139,6 +139,7 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Barra de tareas ampliada"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover a la parte superior o izquierda"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover a la parte inferior o derecha"</string>
+    <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Abrir app como burbuja"</string>
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{app más}other{apps más}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Escritorio"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> y <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +156,6 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Ícono de la app"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Título de la app"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Botón de cerrar"</string>
+    <string name="pin_to_taskbar" msgid="6607778046321626950">"Fijar a la barra"</string>
+    <string name="unpin_from_taskbar" msgid="2178811773165572676">"No fijar a la barra"</string>
 </resources>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index 7f7b433..69628cf 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Barra de tareas ampliada"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover arriba/a la izquierda"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover abajo/a la derecha"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{app más}other{apps más}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Ordenador"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> y <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Icono de la aplicación"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Título de la aplicación"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Botón de cerrar"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index d4e041a..41cb357 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Tegumiriba ületäide"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Teisalda üles/vasakule"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Teisalda alla/paremale"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{rakendus veel}other{rakendust veel}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Lauaarvuti"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ja <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Rakenduse ikoon"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Rakenduse pealkiri"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Sulgemisnupp"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index 0a912d8..15c4742 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Zereginen barraren luzapena"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Eraman gora, ezkerretara"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Eraman behera, eskuinetara"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplikazio gehiago}other{aplikazio gehiago}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Ordenagailua"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> eta <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Aplikazioaren ikonoa"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Aplikazioaren izena"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Ixteko botoia"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index 7dcdb7f..810632b 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"سرریز نوار وظیفه"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"انتقال به بالا/ چپ"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"انتقال به پایین/ راست"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{برنامه دیگر}one{برنامه دیگر}other{برنامه دیگر}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"رایانه"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> و <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"نماد برنامه"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"عنوان برنامه"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"دکمه بستن"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index adeaa34..e23ae30 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Tehtäväpalkin ylivuotu"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Siirrä ylös tai vasemmalle"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Siirrä alas tai oikealle"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{muu sovellus}other{muuta sovellusta}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Tietokone"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ja <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Sovelluskuvake"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Sovelluksen nimi"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Sulje-painike"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index 3201886..448abf5 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -90,7 +90,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Tout est prêt!"</string>
     <string name="allset_hint" msgid="459504134589971527">"Balayez l\'écran vers le haut pour accéder à l\'écran d\'accueil"</string>
     <string name="allset_button_hint" msgid="2395219947744706291">"Toucher le bouton d\'accueil pour passer sur votre écran d\'accueil"</string>
-    <string name="allset_description_generic" msgid="5385500062202019855">"Vous êtes maintenant prêt à utiliser votre <xliff:g id="DEVICE">%1$s</xliff:g>."</string>
+    <string name="allset_description_generic" msgid="5385500062202019855">"Vous êtes maintenant prêt à utiliser votre <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
     <string name="default_device_name" msgid="6660656727127422487">"appareil"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Paramètres de navigation du système"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Partager"</string>
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Barre des tâches à développer"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Déplacer vers le coin supérieur gauche de l\'écran"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Déplacer vers le coin inférieur droit de l\'écran"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{autre appli}one{autre appli}other{autres applis}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Bureau"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> et <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Icône de l\'appli"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Nom de l\'appli"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Bouton Fermer"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index 9982b25..59310e2 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Développement de la barre des tâches"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Déplacer en haut ou à gauche"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Déplacer en bas ou à droite"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{autre application}one{autre application}other{autres applications}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Mode ordinateur"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> et <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Icône de l\'application"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Titre de l\'application"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Bouton \"Fermer\""</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index df665ed..994b5fd 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Menú adicional da barra de tarefas"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover á parte superior ou á esquerda"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover á parte inferior ou á dereita"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplicación máis}other{aplicacións máis}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Escritorio"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> e <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Icona da aplicación"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Título da aplicación"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Botón Pechar"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index 7145c6c9..03fbd63 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"ટાસ્કબાર ઓવરફ્લો"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"સૌથી ઉપર ડાબી બાજુએ ખસેડો"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"સૌથી નીચે જમણી બાજુએ ખસેડો"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{વધુ ઍપ}one{વધુ ઍપ}other{વધુ ઍપ}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"ડેસ્કટૉપ"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> અને <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"ઍપનું આઇકન"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"ઍપનું શીર્ષક"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"\'બંધ કરો\' બટન"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index 99b7b71..89648d4 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"टास्कबार ओवरफ़्लो आइकॉन"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ऊपर/बाईं तरफ़ ले जाएं"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"नीचे/दाईं तरफ़ ले जाएं"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ज़्यादा ऐप्लिकेशन}one{ज़्यादा ऐप्लिकेशन}other{ज़्यादा ऐप्लिकेशन}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"डेस्कटॉप"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> और <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"ऐप्लिकेशन आइकॉन"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"ऐप्लिकेशन का नाम"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"\'बंद करें\' बटन"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index 67fe240..e4a0f7c 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Dodatni izbornik trake sa zadacima"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premjesti gore/lijevo"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premjesti dolje/desno"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{dodatna aplikacija}one{dodatna aplikacija}few{dodatne aplikacije}other{dodatnih aplikacija}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Računalo"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Ikona aplikacije"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Naziv aplikacije"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Gumb Zatvori"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index f825032..ac97372 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Feladatsáv túlcsordulása"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mozgatás felülre vagy a bal oldalra"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mozgatás alulra vagy a jobb oldalra"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{további alkalmazás}other{további alkalmazás}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Asztali"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> és <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Alkalmazásikon"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Alkalmazás neve"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Bezárás gomb"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index 6a7653f..27ac837 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Հավելվածների վահանակի լրացուցիչ ընտրացանկ"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Տեղափոխել վերևի ձախ անկյուն"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Տեղափոխել ներքևի աջ անկյուն"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{լրացուցիչ հավելված}one{լրացուցիչ հավելված}other{լրացուցիչ հավելված}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Համակարգիչ"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> և <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Հավելվածի պատկերակ"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Հավելվածի անվանում"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"«Փակել» կոճակ"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index 5b5796c..69a2a82 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Tambahan Taskbar"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Pindahkan ke atas/kiri"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pindahkan ke bawah/kanan"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplikasi lainnya}other{aplikasi lainnya}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> dan <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Ikon aplikasi"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Judul aplikasi"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Tombol tutup"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index 12541f7..a8c2770 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Yfirflæði á forritastiku"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Færa efst/til vinstri"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Færa neðst/til hægri"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{forrit til viðbótar}one{forrit til viðbótar}other{forrit til viðbótar}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Skjáborð"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> og <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Forritstákn"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Titil forrits"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Hnappur til að loka"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index 731184b..7d0bcd8 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Overflow barra delle app"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Sposta in alto/a sinistra"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sposta in basso/a destra"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{altra app}other{altre app}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> e <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Icona dell\'app"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Titolo dell\'app"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Pulsante Chiudi"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index 0fff001..55295cf 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"אפשרויות נוספות בסרגל האפליקציות"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"העברה לפינה השמאלית/העליונה"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"העברה לפינה הימנית/התחתונה"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{אפליקציה נוספת}one{אפליקציות נוספות}two{אפליקציות נוספות}other{אפליקציות נוספות}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"מחשב"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ו-<xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"סמל האפליקציה"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"שם האפליקציה"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"כפתור הסגירה"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index ded498a..b66126f 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -139,6 +139,7 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"タスクバーのオーバフロー"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"上 / 左に移動"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"下 / 右に移動"</string>
+    <string name="open_app_as_a_bubble" msgid="6642626287247807473">"アプリをバブルとして開く"</string>
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{個のその他のアプリ}other{個のその他のアプリ}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"デスクトップ"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> と <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +156,6 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"アプリのアイコン"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"アプリのタイトル"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"閉じるボタン"</string>
+    <string name="pin_to_taskbar" msgid="6607778046321626950">"タスクバーに固定"</string>
+    <string name="unpin_from_taskbar" msgid="2178811773165572676">"タスクバーの固定解除"</string>
 </resources>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index 1604495..0608631 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -139,6 +139,7 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"ამოცანათა ზოლის გადავსება"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ზემოთ/მარცხნივ გადატანა"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ქვემოთ/მარჯვნივ გადატანა"</string>
+    <string name="open_app_as_a_bubble" msgid="6642626287247807473">"აპის გახსნა ბუშტის სახით"</string>
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{სხვა აპი}other{სხვა აპი}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"დესკტოპი"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> და <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +156,6 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"აპის ხატულა"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"აპის სათაური"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"დახურვის ღილაკი"</string>
+    <string name="pin_to_taskbar" msgid="6607778046321626950">"ამოც. ზოლში ჩანიშვნა"</string>
+    <string name="unpin_from_taskbar" msgid="2178811773165572676">"ამოც. ზოლიდან მოხსნა"</string>
 </resources>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index 76a6199..8e18fab 100644
--- a/quickstep/res/values-kk/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"\"Тапсырмалар жолағы\" қосымша мәзірі"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Жоғары/солға жылжыту"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Төмен/оңға жылжыту"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{қосымша қолданба}other{қосымша қолданба}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Компьютер"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> және <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Қолданба белгішесі"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Қолданба атауы"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"\"Жабу\" түймесі"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 1fdcde9..44f7fe4 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"ម៉ឺនុយបន្ថែមរបារកិច្ចការ"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ផ្លាស់ទីទៅខាងលើ/ឆ្វេង"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ផ្លាស់ទីទៅខាងក្រោម/ស្ដាំ"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{កម្មវិធីច្រើនទៀត}other{កម្មវិធីច្រើនទៀត}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"អេក្រង់ដើម"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> និង <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"រូបកម្មវិធី"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"ចំណងជើងកម្មវិធី"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"ប៊ូតុងបិទ"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index c43a3d1..2187634 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -139,6 +139,7 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"ಟಾಸ್ಕ್ ಬಾರ್ ಓವರ್‌ಫ್ಲೋ"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ಮೇಲಿನ/ಎಡಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ಕೆಳಗಿನ/ಬಲಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
+    <string name="open_app_as_a_bubble" msgid="6642626287247807473">"ಆ್ಯಪ್ ಅನ್ನು ಬಬಲ್ ಆಗಿ ತೆರೆಯಿರಿ"</string>
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ಹೆಚ್ಚಿನ ಆ್ಯಪ್‌}one{ಹೆಚ್ಚಿನ ಆ್ಯಪ್‌ಗಳು}other{ಹೆಚ್ಚಿನ ಆ್ಯಪ್‌ಗಳು}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"ಡೆಸ್ಕ್‌ಟಾಪ್"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ಮತ್ತು <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +156,6 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"ಆ್ಯಪ್ ಐಕಾನ್"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"ಆ್ಯಪ್ ಶೀರ್ಷಿಕೆ"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"ಮುಚ್ಚುವ ಬಟನ್"</string>
+    <string name="pin_to_taskbar" msgid="6607778046321626950">"ಟಾಸ್ಕ್‌ಬಾರ್‌ಗೆ ಪಿನ್ ಮಾಡಿ"</string>
+    <string name="unpin_from_taskbar" msgid="2178811773165572676">"ಟಾಸ್ಕ್‌ಬಾರ್‌ನಿಂದ ಅನ್‌ಪಿನ್"</string>
 </resources>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index 4105f97..e5e5359 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"태스크 바 오버플로"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"상단/왼쪽으로 이동"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"하단/오른쪽으로 이동"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{추가 앱}other{추가 앱}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"데스크톱"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> 및 <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"앱 아이콘"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"앱 제목"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"닫기 버튼"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index 3109069..d23ee7e 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"\"Тапшырмалар панели\" кошумча менюсу"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Жогорку/сол бурчка жылдыруу"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Төмөнкү/оң бурчка жылдыруу"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{колдонмо бар}other{колдонмо бар}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Компьютер"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> жана <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Колдонмонун сүрөтчөсү"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Колдонмонун аталышы"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Жабуу баскычы"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index 3df475f..8407311 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"ສ່ວນເພີ່ມເຕີມຂອງແຖບໜ້າວຽກ"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ຍ້າຍໄປຊ້າຍ/ເທິງ"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ຍ້າຍໄປຂວາ/ລຸ່ມ"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ແອັບເພີ່ມເຕີມ}other{ແອັບເພີ່ມເຕີມ}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"ເດັສທັອບ"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ແລະ <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"ໄອຄອນແອັບ"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"ຊື່ແອັບ"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"ປຸ່ມປິດ"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index ec16b11..268eeb7 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -139,6 +139,7 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Užduočių juostos perpildymas"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Perkelti aukštyn, kairėn"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Perkelti žemyn, dešinėn"</string>
+    <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Atidaryti programą kaip burbulą"</string>
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{papildoma programa}one{papildoma programa}few{papildomos programos}many{papildomos programos}other{papildomų programų}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Stalinis kompiuteris"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"„<xliff:g id="APP_NAME_1">%1$s</xliff:g>“ ir „<xliff:g id="APP_NAME_2">%2$s</xliff:g>“"</string>
@@ -155,4 +156,6 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Programos piktograma"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Programos pavadinimas"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Mygtukas „Uždaryti“"</string>
+    <string name="pin_to_taskbar" msgid="6607778046321626950">"Priseg. prie užd. j."</string>
+    <string name="unpin_from_taskbar" msgid="2178811773165572676">"Atsegti nuo užd. j."</string>
 </resources>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index 0f15a23..ad83c24 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Uzdevumu joslas pārpilde"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Pārvietot uz augšējo/kreiso stūri"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pārvietot uz apakšējo/labo stūri"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{papildu lietotne}zero{papildu lietotņu}one{papildu lietotne}other{papildu lietotnes}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Darbvirsma"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"“<xliff:g id="APP_NAME_1">%1$s</xliff:g>” un “<xliff:g id="APP_NAME_2">%2$s</xliff:g>”"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Lietotnes ikona"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Lietotnes nosaukums"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Poga Aizvērt"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index 8fd0cc5..96edee6 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Проширено балонче на „Лента со задачи“"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Премести горе лево"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Премести долу десно"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{дополнителна апликација}one{дополнителна апликација}other{дополнителни апликации}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Режим за компјутер"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> и <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Икона за апликацијата"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Наслов на апликацијата"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Копче за затворање"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index 849315b..628acd1 100644
--- a/quickstep/res/values-ml/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"ടാസ്‌ക്ബാർ ഓവർഫ്ലോ"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"മുകളിലേക്കോ ഇടത്തേക്കോ നീക്കുക"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"താഴേക്കോ വലത്തേക്കോ നീക്കുക"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{കൂടുതൽ ആപ്പ്}other{കൂടുതൽ ആപ്പുകൾ}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"ഡെസ്‌ക്ടോപ്പ്"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g>, <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"ആപ്പ് ഐക്കൺ"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"ആപ്പിന്റെ പേര്"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"അടയ്ക്കുക ബട്ടൺ"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index f086314..ac3c40e 100644
--- a/quickstep/res/values-mn/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Ажлын хэсгийн урт цэс"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Зүүн дээд хэсэг рүү зөөх"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Баруун доод хэсэг рүү зөөх"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{бусад апп}other{бусад апп}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Дэлгэц"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> болон <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Aппын дүрс тэмдэг"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Аппын нэр"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Хаах товч"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index 75c42ad..6f82343 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"टास्कबार ओव्हरफ्लो"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"सर्वात वरती/डावीकडे हलवा"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"तळाशी/उजवीकडे हलवा"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{आणखी अ‍ॅप}other{आणखी अ‍ॅप्स}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"डेस्कटॉप"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> आणि <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"अ‍ॅपचा आयकन"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"अ‍ॅपचे शीर्षक"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"बंद करा बटण"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index b039666..b85b62c 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -88,7 +88,7 @@
     <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Bagus!"</string>
     <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="allset_title" msgid="5021126669778966707">"Siap!"</string>
-    <string name="allset_hint" msgid="459504134589971527">"Leret ke atas untuk ke laman utama"</string>
+    <string name="allset_hint" msgid="459504134589971527">"Leret ke atas untuk ke skrin utama"</string>
     <string name="allset_button_hint" msgid="2395219947744706291">"Ketik butang skrin utama untuk pergi ke skrin utama anda"</string>
     <string name="allset_description_generic" msgid="5385500062202019855">"Anda sudah sedia untuk mula menggunakan <xliff:g id="DEVICE">%1$s</xliff:g> anda"</string>
     <string name="default_device_name" msgid="6660656727127422487">"peranti"</string>
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Limpahan Bar Tugas"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Alihkan ke atas/kiri"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Alihkan ke bawah/kanan"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{apl lagi}other{apl lagi}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> dan <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Ikon apl"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Tajuk apl"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Butang tutup"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index 81a10b5..467ef26 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Taskbar မီနူးအပို"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"အပေါ်/ဘယ်ဘက်သို့ ရွှေ့ရန်"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"အောက်ခြေ/ညာဘက်သို့ ရွှေ့ရန်"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{နောက်ထပ်အက်ပ်}other{နောက်ထပ်အက်ပ်များ}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"ဒက်စ်တော့"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> နှင့် <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"အက်ပ်သင်္ကေတ"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"အက်ပ်ခေါင်းစဉ်"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"အပိတ် ခလုတ်"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index 4c1aaaa..2690a61 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Overflyt for oppgavelinjen"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flytt til øverst/venstre"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flytt til nederst/høyre"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{app til}other{apper til}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Skrivebord"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> og <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Appikon"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Apptittel"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Lukkeknapp"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index 47b2532..d03fcce 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"टास्कबार ओभरफ्लो"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"सिरान/बायाँतिर सार्नुहोस्"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"फेद/दायाँतिर सार्नुहोस्"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{थप एप}other{थप एपहरू}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"डेस्कटप"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> र <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"एप जनाउने आइकन"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"एपको शीर्षक"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"\"बन्द गर्नुहोस्\" बटन"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index 6cf9683..d265f96 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Taakbalkoverloop"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Naar boven/links verplaatsen"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Naar beneden/rechts verplaatsen"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{extra app}other{extra apps}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> en <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Icoon van app"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Titel van app"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Knop Sluiten"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index 248d4cc..e19fee5 100644
--- a/quickstep/res/values-or/strings.xml
+++ b/quickstep/res/values-or/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"ଟାସ୍କବାର ଓଭରଫ୍ଲୋ"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ଶୀର୍ଷ/ବାମକୁ ମୁଭ କରନ୍ତୁ"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ନିମ୍ନ/ଡାହାଣକୁ ମୁଭ କରନ୍ତୁ"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ଅଧିକ ଆପ}other{ଅଧିକ ଆପ୍ସ}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"ଡେସ୍କଟପ"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ଏବଂ <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"ଆପ ଆଇକନ"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"ଆପ ଟାଇଟେଲ"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"\"ବନ୍ଦ କରନ୍ତୁ\" ବଟନ"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index 8489fd1..4a015e4 100644
--- a/quickstep/res/values-pa/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"ਟਾਸਕਬਾਰ ਓਵਰਫ਼ਲੋ"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ਸਿਖਰਲੇ/ਖੱਬੇ ਪਾਸੇ ਲੈ ਕੇ ਜਾਓ"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ਹੇਠਾਂ/ਸੱਜੇ ਪਾਸੇ ਲੈ ਕੇ ਜਾਓ"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ਹੋਰ ਐਪ}one{ਹੋਰ ਐਪ}other{ਹੋਰ ਐਪਾਂ}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"ਡੈਸਕਟਾਪ"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ਅਤੇ <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"ਐਪ ਪ੍ਰਤੀਕ"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"ਐਪ ਸਿਰਲੇਖ"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"\'ਬੰਦ ਕਰੋ\' ਬਟਨ"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index 5c76187..89297a2 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Rozwijany pasek aplikacji"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Przesuń w górny lewy róg"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Przesuń w dolny prawy róg"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{inna aplikacja}few{inne aplikacje}many{innych aplikacji}other{innej aplikacji}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Pulpit"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Ikona aplikacji"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Tytuł aplikacji"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Przycisk Zamknij"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index 84a4f58..8c38182 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -139,6 +139,7 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Menu adicional da Barra de tarefas"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover para a parte superior esquerda"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover para a part superior direita"</string>
+    <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Abrir app como um balão"</string>
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{outra app}other{outras apps}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Computador"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> e <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +156,6 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Ícone da app"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Título da app"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Botão Fechar"</string>
+    <string name="pin_to_taskbar" msgid="6607778046321626950">"Afixar na barra tar."</string>
+    <string name="unpin_from_taskbar" msgid="2178811773165572676">"Desaf. da barra tar."</string>
 </resources>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index fad3164..5ea0194 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Barra de tarefas flutuante"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover para cima/para a esquerda"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover para baixo/para a direita"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{outro app}one{outro app}other{outros apps}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Computador"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> e <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Ícone do app"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Título do app"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Botão \"Fechar\""</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index a89ccdc..08e4081 100644
--- a/quickstep/res/values-ro/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Meniu suplimentar pentru bara de activități"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mută în stânga sus"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mută în dreapta jos"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplicație suplimentară}few{mai multe aplicații}other{mai multe aplicații}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Computer"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> și <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Pictograma aplicației"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Titlul aplicației"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Buton de închidere"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index 9ebbc02..65af36f 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Дополнительное меню панели задач"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Переместить вверх или влево"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Переместить вниз или вправо"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{дополнительное приложение}one{дополнительное приложение}few{дополнительных приложения}many{дополнительных приложений}other{дополнительного приложения}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Режим компьютера"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> и <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Значок приложения"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Название приложения"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Кнопка \"Закрыть\""</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index de23b6e..7e00beb 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"කාර්ය තීරුව පිටාර යාම"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ඉහළ/වම වෙත ගෙන යන්න"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"පහළ/දකුණ වෙත ගෙන යන්න"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{තව යෙදුම}one{තවත් යෙදුම්}other{තවත් යෙදුම්}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"ඩෙස්ක්ටොපය"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> සහ <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"යෙදුම් නිරූපකය"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"යෙදුම් මාතෘකාව"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"වැසීමේ බොත්තම"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index bf9adff..1170e71 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Rozšírená ponuka panela aplikácií"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Presunúť hore alebo doľava"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Presunúť dole alebo doprava"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ďalšia aplikácia}few{ďalšie aplikácie}many{ďalšie aplikácie}other{ďalšie aplikácie}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Počítač"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> a <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Ikona aplikácie"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Názov aplikácie"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Tlačidlo Zavrieť"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index 440c0f1..9ed661c 100644
--- a/quickstep/res/values-sl/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -139,6 +139,7 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Oblaček opravilne vrstice z dodatnimi elementi"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premakni na vrh/levo"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premakni na dno/desno"</string>
+    <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Odpri aplikacijo kot oblaček"</string>
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{dodatna aplikacija}one{dodatna aplikacija}two{dodatni aplikaciji}few{dodatne aplikacije}other{dodatnih aplikacij}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Namizni računalnik"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> in <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +156,6 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Ikona aplikacije"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Ime aplikacije"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Gumb za zapiranje"</string>
+    <string name="pin_to_taskbar" msgid="6607778046321626950">"Pripni v opravilno vrstico"</string>
+    <string name="unpin_from_taskbar" msgid="2178811773165572676">"Odpni iz opravilne vrstice"</string>
 </resources>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index 9aedee5..965699d 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Tejkalimi i shiritit të detyrave"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Lëviz në krye/majtas"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Lëviz në fund/djathtas"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplikacion tjetër}other{aplikacione të tjera}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> dhe <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Ikona e aplikacionit"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Titulli i aplikacionit"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Butoni i mbylljes"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index e810d5a..c827cda 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Преклопна трака задатака"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Премести горе лево"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Премести доле десно"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{додатна апликација}one{додатна апликација}few{додатне апликације}other{додатних апликација}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Рачунар"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> и <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Икона апликације"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Назив апликације"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Дугме Затвори"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index ab9f10e..5713036 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Fler alternativ för aktivitetsfältet"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flytta högst upp/till vänster"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flytta längst ned/till höger"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{app till}other{appar till}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Skrivbordsläge"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> och <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Appikon"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Apptitel"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Knappen Stäng"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index 5f696c5..f1dbcbc 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Upauzana wa Vipengele vya Ziada"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Sogeza juu/kushoto"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sogeza chini/kulia"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{programu nyingine}other{programu zingine}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Kompyuta ya Mezani"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> na <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Aikoni ya programu"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Kichwa cha programu"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Kitufe cha kufunga"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index 4f22a04..04dfc30 100644
--- a/quickstep/res/values-ta/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"செயல் பட்டிக்கான கூடுதல் விருப்பங்கள்"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"மேலே/இடதுபுறம் நகர்த்தும்"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"கீழே/வலதுபுறம் நகர்த்தும்"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{கூடுதல் ஆப்ஸ்}other{கூடுதல் ஆப்ஸ்}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"டெஸ்க்டாப்"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> மற்றும் <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"ஆப்ஸ் ஐகான்"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"ஆப்ஸ் தலைப்பு"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"மூடுவதற்கான பட்டன்"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index 429d2d4..516c2f7 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -139,6 +139,7 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"టాస్క్‌బార్ ఓవర్‌ఫ్లో"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ఎగువ/ఎడమ వైపునకు తరలించండి"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"దిగువ/కుడి వైపునకు తరలించండి"</string>
+    <string name="open_app_as_a_bubble" msgid="6642626287247807473">"యాప్‌ను బబుల్‌లాగా తెరవండి"</string>
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{మరో యాప్‌}other{మరిన్ని యాప్‌లు}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"డెస్క్‌టాప్"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g>, <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +156,6 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"యాప్ చిహ్నం"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"యాప్ టైటిల్"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"\'మూసివేయండి\' బటన్"</string>
+    <string name="pin_to_taskbar" msgid="6607778046321626950">"టాస్క్‌బార్‌కు పిన్"</string>
+    <string name="unpin_from_taskbar" msgid="2178811773165572676">"టాస్క్‌బార్ అన్‌పిన్"</string>
 </resources>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index 03852ff..04eac38 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"การดำเนินการเพิ่มเติมของแถบงาน"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ย้ายไปที่ด้านบนหรือด้านซ้าย"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ย้ายไปที่ด้านล่างหรือด้านขวา"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{แอปเพิ่มเติม}other{แอปเพิ่มเติม}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"เดสก์ท็อป"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> และ <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"ไอคอนแอป"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"ชื่อแอป"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"ปุ่มปิด"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index 1b3696e..89ead25 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -139,6 +139,7 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Taskbar Overflow"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Ilipat sa itaas/kaliwa"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Ilipat sa ibaba/kanan"</string>
+    <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Buksan ang app bilang bubble"</string>
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{pang app}one{pang app}other{pang app}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> at <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +156,6 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Icon ng app"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Pamagat ng app"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Button na isara"</string>
+    <string name="pin_to_taskbar" msgid="6607778046321626950">"I-pin sa taskbar"</string>
+    <string name="unpin_from_taskbar" msgid="2178811773165572676">"I-unpin sa taskbar"</string>
 </resources>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index e213b40..f2c42d0 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Görev Çubuğu Taşması"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Sol üste taşı"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sağ alta taşı"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{uygulama daha}other{uygulama daha}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Masaüstü"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ve <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Uygulama simgesi"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Uygulama başlığı"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Kapat düğmesi"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 2313a07..82d8a60 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Додаткове меню панелі завдань"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Перемістити вгору або вліво"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Перемістити вниз або вправо"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{інший додаток}one{інший додаток}few{інші додатки}many{інших додатків}other{іншого додатка}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Комп’ютер"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> та <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Значок додатка"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Назва додатка"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Кнопка \"Закрити\""</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index 0a3e122..65436ae 100644
--- a/quickstep/res/values-ur/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"ٹاسک بار اوورفلو"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"اوپر/بائیں طرف منتقل کریں"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"نیچے/دائیں طرف منتقل کریں"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{مزید ایپ}other{مزید ایپس}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"ڈیسک ٹاپ"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> اور <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"ایپ آئیکن"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"ایپ کا عنوان"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"\'بند کریں\' بٹن"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index 06ca97a..b761b5d 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Vazifalar panelini kengaytirish"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Yuqoriga yoki chapga oʻtkazish"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pastga yoki oʻngga oʻtkazish"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{boshqa ilova}other{boshqa ilovalar}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Kompyuter"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> va <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Ilova belgisi"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Ilova nomi"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Yopish tugmasi"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index bf50e05..383f915 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Trình đơn mục bổ sung trên thanh tác vụ"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Chuyển lên trên cùng/sang bên trái"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Chuyển xuống dưới cùng/sang bên phải"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ứng dụng khác}other{ứng dụng khác}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Máy tính"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> và <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Biểu tượng ứng dụng"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Tên ứng dụng"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Nút đóng"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 374a4b6..17d022e 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"任务栏溢出图标"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移到顶部/左侧"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右侧"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{多个应用}other{多个应用}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"桌面模式"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g>和<xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"应用图标"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"应用名称"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"“关闭”按钮"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index 42e2e37..47bb6d4 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"工作列溢位"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移至上方/左側"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移至底部/右側"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{個其他應用程式}other{個其他應用程式}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"桌面"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"「<xliff:g id="APP_NAME_1">%1$s</xliff:g>」和「<xliff:g id="APP_NAME_2">%2$s</xliff:g>」"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"應用程式圖示"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"應用程式名稱"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"關閉按鈕"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index d9d1fb5..58c7bb5 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"工作列溢位"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移到上方/左側"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右側"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{個其他應用程式}other{個其他應用程式}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"電腦模式"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"「<xliff:g id="APP_NAME_1">%1$s</xliff:g>」和「<xliff:g id="APP_NAME_2">%2$s</xliff:g>」"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"應用程式圖示"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"應用程式標題"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"關閉按鈕"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index 660ef3e..8d0a6db 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -139,6 +139,8 @@
     <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Ukuphuphuma Kwetaskbar"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Hamba phezulu/kwesokunxele"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Hamba phansi/kwesokudla"</string>
+    <!-- no translation found for open_app_as_a_bubble (6642626287247807473) -->
+    <skip />
     <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{i-app eyengeziwe}one{ama-app engeziwe}other{ama-app engeziwe}}"</string>
     <string name="quick_switch_desktop" msgid="8393802056024499749">"Ideskithophu"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"I-<xliff:g id="APP_NAME_1">%1$s</xliff:g> ne-<xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
@@ -155,4 +157,8 @@
     <string name="header_app_icon_description" msgid="2184625881433608027">"Isithonjana se-app"</string>
     <string name="header_default_app_title" msgid="8308052350689531566">"Isihloko se-app"</string>
     <string name="header_close_icon_description" msgid="5400033616675911319">"Inkinobho yokuvala"</string>
+    <!-- no translation found for pin_to_taskbar (6607778046321626950) -->
+    <skip />
+    <!-- no translation found for unpin_from_taskbar (2178811773165572676) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index d699cdf..e69fa4d 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -56,6 +56,7 @@
     <!-- Accessibility actions -->
     <item type="id" name="action_move_to_top_or_left" />
     <item type="id" name="action_move_to_bottom_or_right" />
+    <item type="id" name="action_create_application_bubble" />
 
     <!-- The max scale for the wallpaper when it's zoomed in -->
     <item name="config_wallpaperMaxScale" format="float" type="dimen">
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 52ebdae..36e6902 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -371,7 +371,6 @@
     <dimen name="taskbar_running_app_indicator_height">2dp</dimen>
     <dimen name="taskbar_running_app_indicator_width">12dp</dimen>
     <dimen name="taskbar_running_app_indicator_top_margin">4dp</dimen>
-    <dimen name="taskbar_minimized_app_indicator_width">6dp</dimen>
 
     <!-- Transient taskbar -->
     <dimen name="transient_taskbar_padding">12dp</dimen>
@@ -530,6 +529,11 @@
     <dimen name="keyboard_quick_switch_text_button_radius">360dp</dimen>
     <dimen name="keyboard_quick_switch_text_button_horizontal_padding">16dp</dimen>
     <dimen name="keyboard_quick_switch_text_button_fade_edge_length">20dp</dimen>
+    <dimen name="keyboard_quick_switch_scroll_button_width">36dp</dimen>
+    <dimen name="keyboard_quick_switch_scroll_button_height">56dp</dimen>
+    <dimen name="keyboard_quick_switch_scroll_button_horizontal_padding">12dp</dimen>
+    <dimen name="keyboard_quick_switch_scroll_button_vertical_padding">32dp</dimen>
+    <dimen name="keyboard_quick_switch_scroll_button_corner_radius">18dp</dimen>
 
     <!-- Digital Wellbeing -->
     <dimen name="digital_wellbeing_toast_height">48dp</dimen>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 8e70a2b..7578bd5 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -322,6 +322,14 @@
     <string name="move_drop_target_top_or_left">Move to top&#47;left</string>
     <!-- Label for moving drop target to the bottom or right side of the screen, depending on orientation (from the Taskbar only). -->
     <string name="move_drop_target_bottom_or_right">Move to bottom&#47;right</string>
+    <!-- Label for creating an application bubble (from the Taskbar only). -->
+    <string name="open_app_as_a_bubble">Open app as a bubble</string>
+
+    <!-- Accessibility pane title for quick switch view, which lists apps opened by the user, ordered by how recently the app was opened. -->
+    <string name="quick_switch_pane_title">Recent apps</string>
+
+    <!-- Content description for the quick switch view, which lists apps opened by the user, ordered by how recently the app was opened. -->
+    <string name="quick_switch_content_description">Recent app list</string>
 
     <!-- Label for quick switch tile showing how many more apps are available. The number will be displayed above this text. [CHAR LIMIT=NONE] -->
     <string name="quick_switch_overflow">{count, plural,
@@ -334,6 +342,15 @@
 
     <!-- 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>
+    <!-- Accessibility label for quick switch tiles that include information about the tile's position in the parent list [CHAR LIMIT=NONE] -->
+    <string name="quick_switch_task_with_position_in_parent"><xliff:g id="task_description" example="Chrome">%1$s</xliff:g>, item <xliff:g id="index_in_parent" example="1">%2$d</xliff:g> of <xliff:g id="total_tasks" example="5">%3$d</xliff:g></string>
+
+    <!-- Accessibility label for an arrow button within quick switch UI that scrolls the quick switch content left
+        TODO(b/397975686): Make these translatable when verified by UX. -->
+    <string name="quick_switch_scroll_arrow_left" translatable="false">Scroll left</string>
+    <!-- Accessibility label for an arrow button within quick switch UI that scrolls the quick switch content right
+        TODO(b/397975686): Make these translatable when verified by UX. -->
+    <string name="quick_switch_scroll_arrow_right" translatable="false">Scroll right</string>
 
     <!-- Strings for bubble bar -->
     <!-- Fallback name for a bubble if it does have a title [CHAR_LIMIT=none] -->
@@ -366,4 +383,9 @@
     <string name="header_default_app_title">App title</string>
     <!-- Content description for the header close button. [CHAR LIMIT=NONE] -->
     <string name="header_close_icon_description">Close button</string>
+
+    <!-- Label for pinning an item to the taskbar. [CHAR_LIMIT=20] -->
+    <string name="pin_to_taskbar">Pin to taskbar</string>
+    <!-- Label for unpinning an item from the taskbar. [CHAR_LIMIT=20] -->
+    <string name="unpin_from_taskbar">Unpin from taskbar</string>
 </resources>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 84ae0fe..aae8a56 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -176,6 +176,7 @@
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map.Entry;
 
 /**
  * Manages the opening and closing app transitions from Launcher
@@ -351,7 +352,7 @@
                 new RemoteAnimationAdapter(runner, duration, statusBarTransitionDelay),
                 new RemoteTransition(runner.toRemoteTransition(),
                         mLauncher.getIApplicationThread(), "QuickstepLaunch"));
-        IRemoteCallback endCallback = completeRunnableListCallback(onEndCallback);
+        IRemoteCallback endCallback = completeRunnableListCallback(onEndCallback, mLauncher);
         options.setOnAnimationAbortListener(endCallback);
         options.setOnAnimationFinishedListener(endCallback);
         options.setLaunchCookie(StableViewInfo.toLaunchCookie(itemInfo));
@@ -1342,9 +1343,9 @@
                 ? Collections.EMPTY_LIST
                 : runningTaskTarget.taskInfo.launchCookies;
 
-        return mLauncher.getFirstMatchForAppClose(
+        return mLauncher.getFirstVisibleElementForAppClose(
                 StableViewInfo.fromLaunchCookies(launchCookies), packageName,
-                UserHandle.of(runningTaskTarget.taskInfo.userId), true /* supportsAllAppsState */);
+                UserHandle.of(runningTaskTarget.taskInfo.userId));
     }
 
     private @NonNull RectF getDefaultWindowTargetRect() {
diff --git a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
index f992913..4d3e3be 100644
--- a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
+++ b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
@@ -25,7 +25,6 @@
 
 import static java.lang.Math.max;
 import static java.lang.Math.min;
-import static java.util.Collections.emptyList;
 
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
@@ -48,7 +47,6 @@
 import com.android.launcher3.model.StringCache;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.model.WidgetPredictionsRequester;
-import com.android.launcher3.model.WidgetsFilterDataProvider;
 import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.PackageItemInfo;
@@ -70,7 +68,8 @@
 import java.util.regex.Pattern;
 
 /** An Activity that can host Launcher's widget picker. */
-public class WidgetPickerActivity extends BaseActivity {
+public class WidgetPickerActivity extends BaseActivity implements
+        WidgetPredictionsRequester.WidgetPredictionsListener {
     private static final String TAG = "WidgetPickerActivity";
     /**
      * Name of the extra that indicates that a widget being dragged.
@@ -121,9 +120,7 @@
     private LauncherAppState mApp;
     private StringCache mStringCache;
     private WidgetPredictionsRequester mWidgetPredictionsRequester;
-    private final WidgetPickerDataProvider mWidgetPickerDataProvider =
-            new WidgetPickerDataProvider();
-    private WidgetsFilterDataProvider mWidgetsFilterDataProvider;
+    private WidgetPickerDataProvider mWidgetPickerDataProvider;
 
     private int mDesiredWidgetWidth;
     private int mDesiredWidgetHeight;
@@ -170,7 +167,7 @@
         InvariantDeviceProfile idp = mApp.getInvariantDeviceProfile();
         mDeviceProfile = idp.getDeviceProfile(this);
         mModel = new WidgetsModel();
-        mWidgetsFilterDataProvider = WidgetsFilterDataProvider.Companion.newInstance(this);
+        mWidgetPickerDataProvider = new WidgetPickerDataProvider(this);
 
         setContentView(R.layout.widget_picker_activity);
         mDragLayer = findViewById(R.id.drag_layer);
@@ -313,24 +310,20 @@
     private void refreshAndBindWidgets() {
         MODEL_EXECUTOR.execute(() -> {
             LauncherAppState app = LauncherAppState.getInstance(this);
-            // Don't have to setup filters - its setup when launcher loads
-            // Just refresh filters with available cached info.
-            mModel.updateWidgetFilters(mWidgetsFilterDataProvider);
             mModel.update(app, null);
 
             StringCache stringCache = new StringCache();
             stringCache.loadStrings(this);
 
             bindStringCache(stringCache);
-            bindWidgets(mModel.getWidgetsByPackageItemForPicker(),
-                    mModel.getDefaultWidgetsFilter());
+            bindWidgets(mModel.getWidgetsByPackageItemForPicker());
             // Open sheet once widgets are available, so that it doesn't interrupt the open
             // animation.
             openWidgetsSheet();
             if (mUiSurface != null) {
                 mWidgetPredictionsRequester = new WidgetPredictionsRequester(app.getContext(),
                         mUiSurface, mModel.getWidgetsByComponentKeyForPicker());
-                mWidgetPredictionsRequester.request(mAddedWidgets, this::bindRecommendedWidgets);
+                mWidgetPredictionsRequester.request(mAddedWidgets, /*listener=*/ this);
             }
         });
     }
@@ -339,26 +332,19 @@
         MAIN_EXECUTOR.execute(() -> mStringCache = stringCache);
     }
 
-    private void bindWidgets(Map<PackageItemInfo, List<WidgetItem>> widgets,
-            @Nullable Predicate<WidgetItem> defaultWidgetsFilter) {
+    private void bindWidgets(Map<PackageItemInfo, List<WidgetItem>> widgets) {
         WidgetsListBaseEntriesBuilder builder = new WidgetsListBaseEntriesBuilder(
                 mApp.getContext());
-
         final List<WidgetsListBaseEntry> allWidgets = builder.build(widgets, mNoShortcutsFilter);
 
-        // Default list is shown if either defaultWidgetsFilter exists or host has additionally
-        // enforced size filtering.
+        // Default list is shown if host has additionally enforced size filtering.
         @Nullable Predicate<WidgetItem> defaultListFilter =
                 hasHostSizeFilters() ? mHostSizeAndNoShortcutsFilter : null;
-        if (defaultWidgetsFilter != null) {
-            defaultListFilter = defaultListFilter != null ? defaultListFilter.and(
-                    defaultWidgetsFilter) : defaultWidgetsFilter;
-        }
-        final List<WidgetsListBaseEntry> defaultWidgets = defaultListFilter != null ? builder.build(
-                widgets, defaultListFilter) : emptyList();
 
-        MAIN_EXECUTOR.execute(
-                () -> mWidgetPickerDataProvider.setWidgets(allWidgets, defaultWidgets));
+        MAIN_EXECUTOR.execute(() -> {
+            mWidgetPickerDataProvider.setHostSpecifiedDefaultWidgetsFilter(defaultListFilter);
+            mWidgetPickerDataProvider.setWidgets(allWidgets);
+        });
     }
 
     private void openWidgetsSheet() {
@@ -370,7 +356,8 @@
         });
     }
 
-    private void bindRecommendedWidgets(List<ItemInfo> recommendedWidgets) {
+    @Override
+    public void onPredictionsAvailable(List<ItemInfo> recommendedWidgets) {
         // Bind recommendations once picker has finished open animation.
         MAIN_EXECUTOR.getHandler().postDelayed(
                 () -> mWidgetPickerDataProvider.setWidgetRecommendations(recommendedWidgets),
@@ -380,7 +367,7 @@
     @Override
     protected void onDestroy() {
         super.onDestroy();
-        MODEL_EXECUTOR.execute(() -> mWidgetsFilterDataProvider.destroy());
+        mWidgetPickerDataProvider.destroy();
         if (mWidgetPredictionsRequester != null) {
             mWidgetPredictionsRequester.clear();
         }
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt
index 688018b..1438edf 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt
@@ -64,18 +64,13 @@
 
     fun createAnimators(info: TransitionInfo, finishCallback: (Animator) -> Unit): List<Animator> {
         val launchChange = getLaunchChange(info)
-        requireNotNull(launchChange) {
-            val changesString =
+        if (launchChange == null) {
+            val tasksInfo =
                 info.changes.joinToString(", ") { change ->
-                    "Change: mode=${change.mode}, " +
-                        "taskId=${change.taskInfo?.id}, " +
-                        "isFreeform=${change.taskInfo?.isFreeform}"
+                    "${change.taskInfo?.taskId}:${change.taskInfo?.isFreeform}"
                 }
-            Log.e(
-                TAG,
-                "No launch change found: Transition type=${info.type}, changes=$changesString",
-            )
-            "expected an app launch Change"
+            Log.e(TAG, "No launch change found: Transition info=$info, tasks state=$tasksInfo")
+            return emptyList()
         }
 
         val transaction = transactionSupplier.get()
@@ -105,10 +100,14 @@
     }
 
     private fun getLaunchChange(info: TransitionInfo): Change? =
-        info.changes.firstOrNull { change -> change.mode in LAUNCH_CHANGE_MODES }
+        info.changes.firstOrNull { change ->
+            change.mode in LAUNCH_CHANGE_MODES && change.taskInfo?.isFreeform == true
+        }
 
     private fun getMinimizeChange(info: TransitionInfo): Change? =
-        info.changes.firstOrNull { change -> change.mode == TRANSIT_TO_BACK }
+        info.changes.firstOrNull { change ->
+            change.mode == TRANSIT_TO_BACK && change.taskInfo?.isFreeform == true
+        }
 
     private fun getTrampolineCloseChange(info: TransitionInfo): Change? {
         if (
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
index 79072a6..5a8934b 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
@@ -84,6 +84,10 @@
             if (animators.isEmpty()) finishedCallback.run()
         }
         animators += animatorHelper.createAnimators(info, animatorFinishedCallback)
+        if (animators.isEmpty()) {
+            finishedCallback.run()
+            return
+        }
         animators.forEach { it.start() }
     }
 
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
index 40cfe92..a01846d 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
@@ -30,6 +30,7 @@
 import com.android.launcher3.util.Executors.MAIN_EXECUTOR
 import com.android.quickstep.SystemUiProxy
 import com.android.quickstep.TaskViewUtils
+import com.android.quickstep.util.DesksUtils.Companion.areMultiDesksFlagsEnabled
 import com.android.quickstep.views.DesktopTaskView
 import com.android.quickstep.views.TaskContainer
 import com.android.quickstep.views.TaskView
@@ -60,7 +61,11 @@
                 callback,
             )
         val transition = RemoteTransition(animRunner, appThread, "RecentsToDesktop")
-        systemUiProxy.showDesktopApps(desktopTaskView.displayId, transition)
+        if (areMultiDesksFlagsEnabled()) {
+            systemUiProxy.activateDesk(desktopTaskView.deskId, transition)
+        } else {
+            systemUiProxy.showDesktopApps(desktopTaskView.displayId, transition)
+        }
     }
 
     /** Launch desktop tasks from recents view */
diff --git a/quickstep/src/com/android/launcher3/model/WidgetPredictionsRequester.java b/quickstep/src/com/android/launcher3/model/WidgetPredictionsRequester.java
index d3ac975..ada7301 100644
--- a/quickstep/src/com/android/launcher3/model/WidgetPredictionsRequester.java
+++ b/quickstep/src/com/android/launcher3/model/WidgetPredictionsRequester.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3.model;
 
-import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -48,7 +47,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
@@ -56,7 +54,7 @@
  * Works with app predictor to fetch and process widget predictions displayed in a standalone
  * widget picker activity for a UI surface.
  */
-public class WidgetPredictionsRequester {
+public class WidgetPredictionsRequester implements AppPredictor.Callback {
     private static final int NUM_OF_RECOMMENDED_WIDGETS_PREDICATION = 20;
     private static final String BUNDLE_KEY_ADDED_APP_WIDGETS = "added_app_widgets";
     // container/screenid/[positionx,positiony]/[spanx,spany]
@@ -71,6 +69,9 @@
     @NonNull
     private final String mUiSurface;
     private boolean mPredictionsAvailable;
+    @Nullable
+    private WidgetPredictionsListener mPredictionsListener = null;
+    @Nullable Predicate<WidgetItem> mFilter = null;
     @NonNull
     private final Map<ComponentKey, WidgetItem> mAllWidgets;
 
@@ -81,36 +82,49 @@
         mAllWidgets = Collections.unmodifiableMap(allWidgets);
     }
 
+    // AppPredictor.Callback -> onTargetsAvailable
+    @Override
+    @WorkerThread
+    public void onTargetsAvailable(List<AppTarget> targets) {
+        List<WidgetItem> filteredPredictions = filterPredictions(targets, mAllWidgets, mFilter);
+        List<ItemInfo> mappedPredictions = mapWidgetItemsToItemInfo(filteredPredictions);
+
+        if (!mPredictionsAvailable && mPredictionsListener != null) {
+            mPredictionsAvailable = true;
+            MAIN_EXECUTOR.execute(
+                    () -> mPredictionsListener.onPredictionsAvailable(mappedPredictions));
+        }
+    }
+
     /**
      * Requests one time predictions from the app predictions manager and invokes provided callback
-     * once predictions are available.
+     * once predictions are available. Any previous requests may be cancelled.
      *
      * @param existingWidgets widgets that are currently added to the surface;
-     * @param callback        consumer of prediction results to be called when predictions are
-     *                        available
+     * @param listener        consumer of prediction results to be called when predictions are
+     *                        available; any previous listener will no longer receive updates.
      */
+    @WorkerThread // e.g. MODEL_EXECUTOR
     public void request(List<AppWidgetProviderInfo> existingWidgets,
-            Consumer<List<ItemInfo>> callback) {
+            WidgetPredictionsListener listener) {
+        clear();
+        mPredictionsListener = listener;
+        mFilter = notOnUiSurfaceFilter(existingWidgets);
+
+        AppPredictionManager apm = mContext.getSystemService(AppPredictionManager.class);
+        if (apm == null) {
+            return;
+        }
+
         Bundle bundle = buildBundleForPredictionSession(existingWidgets);
-        Predicate<WidgetItem> filter = notOnUiSurfaceFilter(existingWidgets);
-
-        MODEL_EXECUTOR.execute(() -> {
-            clear();
-            AppPredictionManager apm = mContext.getSystemService(AppPredictionManager.class);
-            if (apm == null) {
-                return;
-            }
-
-            mAppPredictor = apm.createAppPredictionSession(
-                    new AppPredictionContext.Builder(mContext)
-                            .setUiSurface(mUiSurface)
-                            .setExtras(bundle)
-                            .setPredictedTargetCount(NUM_OF_RECOMMENDED_WIDGETS_PREDICATION)
-                            .build());
-            mAppPredictor.registerPredictionUpdates(MODEL_EXECUTOR,
-                    targets -> bindPredictions(targets, filter, callback));
-            mAppPredictor.requestPredictionUpdate();
-        });
+        mAppPredictor = apm.createAppPredictionSession(
+                new AppPredictionContext.Builder(mContext)
+                        .setUiSurface(mUiSurface)
+                        .setExtras(bundle)
+                        .setPredictedTargetCount(NUM_OF_RECOMMENDED_WIDGETS_PREDICATION)
+                        .build());
+        mAppPredictor.registerPredictionUpdates(MODEL_EXECUTOR, /*callback=*/ this);
+        mAppPredictor.requestPredictionUpdate();
     }
 
     /**
@@ -158,27 +172,14 @@
         return widgetItem -> !existingComponentKeys.contains(widgetItem);
     }
 
-    /** Provides the predictions returned by the predictor to the registered callback. */
-    @WorkerThread
-    private void bindPredictions(List<AppTarget> targets, Predicate<WidgetItem> filter,
-            Consumer<List<ItemInfo>> callback) {
-        if (!mPredictionsAvailable) {
-            mPredictionsAvailable = true;
-            List<WidgetItem> filteredPredictions = filterPredictions(targets, mAllWidgets, filter);
-            List<ItemInfo> mappedPredictions = mapWidgetItemsToItemInfo(filteredPredictions);
-
-            MAIN_EXECUTOR.execute(() -> callback.accept(mappedPredictions));
-            MODEL_EXECUTOR.execute(this::clear);
-        }
-    }
-
     /**
      * Applies the provided filter (e.g. widgets not on workspace) on the predictions returned by
      * the predictor.
      */
     @VisibleForTesting
     static List<WidgetItem> filterPredictions(List<AppTarget> predictions,
-            Map<ComponentKey, WidgetItem> allWidgets, Predicate<WidgetItem> filter) {
+            @NonNull Map<ComponentKey, WidgetItem> allWidgets,
+            @Nullable Predicate<WidgetItem> filter) {
         List<WidgetItem> servicePredictedItems = new ArrayList<>();
 
         for (AppTarget prediction : predictions) {
@@ -187,7 +188,7 @@
                 WidgetItem widgetItem = allWidgets.get(
                         new ComponentKey(new ComponentName(prediction.getPackageName(), className),
                                 prediction.getUser()));
-                if (widgetItem != null && filter.test(widgetItem)) {
+                if (widgetItem != null && (filter == null || filter.test(widgetItem))) {
                     servicePredictedItems.add(widgetItem);
                 }
             }
@@ -200,27 +201,34 @@
      * Converts the list of {@link WidgetItem}s to the list of {@link ItemInfo}s.
      */
     private List<ItemInfo> mapWidgetItemsToItemInfo(List<WidgetItem> widgetItems) {
-        List<ItemInfo> items;
-        if (enableCategorizedWidgetSuggestions()) {
-            WidgetRecommendationCategoryProvider categoryProvider =
-                    new WidgetRecommendationCategoryProvider();
-            items = widgetItems.stream()
-                    .map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION,
-                            categoryProvider.getWidgetRecommendationCategory(mContext, it)))
-                    .collect(Collectors.toList());
-        } else {
-            items = widgetItems.stream().map(it -> new PendingAddWidgetInfo(it.widgetInfo,
-                    CONTAINER_WIDGETS_PREDICTION)).collect(Collectors.toList());
-        }
-        return items;
+        WidgetRecommendationCategoryProvider categoryProvider =
+                new WidgetRecommendationCategoryProvider();
+        return widgetItems.stream()
+                .map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION,
+                        categoryProvider.getWidgetRecommendationCategory(mContext, it)))
+                .collect(Collectors.toList());
     }
 
     /** Cleans up any open prediction sessions. */
     public void clear() {
         if (mAppPredictor != null) {
+            mAppPredictor.unregisterPredictionUpdates(this);
             mAppPredictor.destroy();
             mAppPredictor = null;
         }
+        mPredictionsListener = null;
         mPredictionsAvailable = false;
+        mFilter = null;
+    }
+
+    /**
+     * Listener class to listen to updates from the {@link WidgetPredictionsRequester}
+     */
+    public interface WidgetPredictionsListener {
+        /**
+         * Callback method that is called when the predicted widgets are available.
+         * @param predictions list of predicted widgets {@link PendingAddWidgetInfo}
+         */
+        void onPredictionsAvailable(List<ItemInfo> predictions);
     }
 }
diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
index 8bd2ba8..0a4b7c8 100644
--- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.model;
 
-import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
 import static com.android.launcher3.Flags.enableTieredWidgetsByDefaultInPicker;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
 import static com.android.launcher3.model.ModelUtils.WIDGET_FILTER;
@@ -45,7 +44,6 @@
 import java.util.Map;
 import java.util.Random;
 import java.util.Set;
-import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 /** Task to update model as a result of predicted widgets update */
@@ -68,8 +66,6 @@
     @Override
     public void execute(@NonNull ModelTaskController taskController, @NonNull BgDataModel dataModel,
             @NonNull AllAppsList apps) {
-        Predicate<WidgetItem> predictedWidgetsFilter = enableTieredWidgetsByDefaultInPicker()
-                ? dataModel.widgetsModel.getPredictedWidgetsFilter() : null;
         Set<ComponentKey> widgetsInWorkspace = dataModel.itemsIdMap
                 .stream()
                 .filter(WIDGET_FILTER)
@@ -84,8 +80,6 @@
                         .stream()
                         .filter(entry -> entry.getValue().widgetInfo != null
                                 && !widgetsInWorkspace.contains(entry.getValue())
-                                && (predictedWidgetsFilter == null
-                                || predictedWidgetsFilter.test(entry.getValue()))
                         ).collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
 
         Context context = taskController.getApp().getContext();
@@ -134,20 +128,12 @@
             }
         }
 
-        List<ItemInfo> items;
-        if (enableCategorizedWidgetSuggestions()) {
-            WidgetRecommendationCategoryProvider categoryProvider =
-                    new WidgetRecommendationCategoryProvider();
-            items = servicePredictedItems.stream()
-                    .map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION,
-                            categoryProvider.getWidgetRecommendationCategory(context, it)))
-                    .collect(Collectors.toList());
-        } else {
-            items = servicePredictedItems.stream()
-                    .map(it -> new PendingAddWidgetInfo(it.widgetInfo,
-                            CONTAINER_WIDGETS_PREDICTION)).collect(
-                            Collectors.toList());
-        }
+        WidgetRecommendationCategoryProvider categoryProvider =
+                new WidgetRecommendationCategoryProvider();
+        List<ItemInfo> items = servicePredictedItems.stream()
+                .map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION,
+                        categoryProvider.getWidgetRecommendationCategory(context, it)))
+                .collect(Collectors.toList());
         FixedContainerItems fixedContainerItems =
                 new FixedContainerItems(mPredictorState.containerId, items);
 
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
index eb24df1..2402a28 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
@@ -401,6 +401,39 @@
         DisplayController.INSTANCE.get(context).notifyConfigChange()
     }
 
+    private fun notifyOnDeskAdded(displayId: Int, deskId: Int) {
+        if (DEBUG) {
+            Log.d(TAG, "notifyOnDeskAdded: displayId=$displayId, deskId=$deskId")
+        }
+
+        for (listener in desktopVisibilityListeners) {
+            listener.onDeskAdded(displayId, deskId)
+        }
+    }
+
+    private fun notifyOnDeskRemoved(displayId: Int, deskId: Int) {
+        if (DEBUG) {
+            Log.d(TAG, "notifyOnDeskRemoved: displayId=$displayId, deskId=$deskId")
+        }
+
+        for (listener in desktopVisibilityListeners) {
+            listener.onDeskRemoved(displayId, deskId)
+        }
+    }
+
+    private fun notifyOnActiveDeskChanged(displayId: Int, newActiveDesk: Int, oldActiveDesk: Int) {
+        if (DEBUG) {
+            Log.d(
+                TAG,
+                "notifyOnActiveDeskChanged: displayId=$displayId, newActiveDesk=$newActiveDesk, oldActiveDesk=$oldActiveDesk",
+            )
+        }
+
+        for (listener in desktopVisibilityListeners) {
+            listener.onActiveDeskChanged(displayId, newActiveDesk, oldActiveDesk)
+        }
+    }
+
     /** TODO: b/333533253 - Remove after flag rollout */
     private fun setBackgroundStateEnabled(backgroundStateEnabled: Boolean) {
         if (DEBUG) {
@@ -511,6 +544,8 @@
                 "Found a duplicate desk Id: $deskId on display: $displayId"
             }
         }
+
+        notifyOnDeskAdded(displayId, deskId)
     }
 
     private fun onDeskRemoved(displayId: Int, deskId: Int) {
@@ -526,6 +561,8 @@
                 it.activeDeskId = INACTIVE_DESK_ID
             }
         }
+
+        notifyOnDeskRemoved(displayId, deskId)
     }
 
     private fun onActiveDeskChanged(displayId: Int, newActiveDesk: Int, oldActiveDesk: Int) {
@@ -539,12 +576,16 @@
             check(oldActiveDesk == it.activeDeskId) {
                 "Mismatch between the Shell's oldActiveDesk: $oldActiveDesk, and Launcher's: ${it.activeDeskId}"
             }
-            check(it.deskIds.contains(newActiveDesk)) {
+            check(newActiveDesk == INACTIVE_DESK_ID || it.deskIds.contains(newActiveDesk)) {
                 "newActiveDesk: $newActiveDesk was never added to display: $displayId"
             }
             it.activeDeskId = newActiveDesk
         }
 
+        if (newActiveDesk != oldActiveDesk) {
+            notifyOnActiveDeskChanged(displayId, newActiveDesk, oldActiveDesk)
+        }
+
         if (wasInDesktopMode != isInDesktopModeAndNotInOverview(displayId)) {
             notifyIsInDesktopModeChanged(displayId, !wasInDesktopMode)
         }
@@ -718,6 +759,6 @@
         private const val TAG = "DesktopVisController"
         private const val DEBUG = false
 
-        public const val INACTIVE_DESK_ID = -1
+        const val INACTIVE_DESK_ID = -1
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
index b33fd38..aa3feb7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
@@ -19,29 +19,34 @@
 import android.content.Intent;
 import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
-import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 
-import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.util.BaseContext;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.Themes;
-import com.android.launcher3.views.ActivityContext;
 import com.android.quickstep.SystemUiProxy;
 
-import java.util.ArrayList;
-import java.util.List;
-
 // TODO(b/218912746): Share more behavior to avoid all apps context depending directly on taskbar.
 /** Base for common behavior between taskbar window contexts. */
-public abstract class BaseTaskbarContext extends ContextThemeWrapper implements ActivityContext,
-        SystemShortcut.BubbleActivityStarter {
+public abstract class BaseTaskbarContext extends BaseContext
+        implements SystemShortcut.BubbleActivityStarter {
 
     protected final LayoutInflater mLayoutInflater;
-    private final List<OnDeviceProfileChangeListener> mDPChangeListeners = new ArrayList<>();
+    private final boolean mIsPrimaryDisplay;
 
-    public BaseTaskbarContext(Context windowContext) {
+    public BaseTaskbarContext(Context windowContext, boolean isPrimaryDisplay) {
         super(windowContext, Themes.getActivityThemeRes(windowContext));
         mLayoutInflater = LayoutInflater.from(this).cloneInContext(this);
+        mIsPrimaryDisplay = isPrimaryDisplay;
+    }
+
+    public boolean isTransientTaskbar() {
+        return DisplayController.isTransientTaskbar(this) && mIsPrimaryDisplay;
+    }
+
+    public boolean isPrimaryDisplay() {
+        return mIsPrimaryDisplay;
     }
 
     @Override
@@ -50,11 +55,6 @@
     }
 
     @Override
-    public final List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners() {
-        return mDPChangeListeners;
-    }
-
-    @Override
     public void showShortcutBubble(ShortcutInfo info) {
         if (info == null) return;
         SystemUiProxy.INSTANCE.get(this).showShortcutBubble(info);
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index 5afc5ed..1698050 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.Flags.enableAltTabKqsOnConnectedDisplays;
 
+import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
 import android.view.MotionEvent;
@@ -164,12 +165,12 @@
                         processLoadedTasks(tasks, taskIdsToExclude);
                         mQuickSwitchViewController.updateQuickSwitchView(
                                 mTasks,
-                                mNumHiddenTasks,
+                                wasOpenedFromTaskbar ? 0 : mNumHiddenTasks,
                                 currentFocusIndexOverride,
                                 mHasDesktopTask,
                                 mWasDesktopTaskFilteredOut);
                     }, shouldShowDesktopTasks ? RecentsFilterState.EMPTY_FILTER
-                            : RecentsFilterState.getEmptyDesktopTaskFilter());
+                            : RecentsFilterState.getDesktopTaskFilter());
                 }
 
                 mQuickSwitchViewController.updateLayoutForSurface(wasOpenedFromTaskbar,
@@ -204,7 +205,7 @@
             // running. If not, focus that first task.
             mQuickSwitchViewController.openQuickSwitchView(
                     mTasks,
-                    mNumHiddenTasks,
+                    wasOpenedFromTaskbar ? 0 : mNumHiddenTasks,
                     /* updateTasks= */ false,
                     currentFocusedIndex == -1 && !mControllerCallbacks.isFirstTaskRunning()
                             ? 0 : currentFocusedIndex,
@@ -222,7 +223,7 @@
             // the correct index.
             mQuickSwitchViewController.openQuickSwitchView(
                     mTasks,
-                    mNumHiddenTasks,
+                    wasOpenedFromTaskbar ? 0 : mNumHiddenTasks,
                     /* updateTasks= */ true,
                     currentFocusedIndex == -1 && !mControllerCallbacks.isFirstTaskRunning()
                             ? 0 : currentFocusedIndex,
@@ -231,7 +232,7 @@
                     mWasDesktopTaskFilteredOut,
                     wasOpenedFromTaskbar);
         }, shouldShowDesktopTasks ? RecentsFilterState.EMPTY_FILTER
-                : RecentsFilterState.getEmptyDesktopTaskFilter());
+                : RecentsFilterState.getDesktopTaskFilter());
     }
 
     private boolean shouldExcludeTask(GroupTask task, Set<Integer> taskIdsToExclude) {
@@ -354,6 +355,27 @@
         }
     }
 
+    @VisibleForTesting
+    boolean isShownFromTaskbar() {
+        return isShown() && mQuickSwitchViewController.wasOpenedFromTaskbar();
+    }
+
+    @VisibleForTesting
+    boolean isShown() {
+        return mQuickSwitchViewController != null
+                && !mQuickSwitchViewController.isCloseAnimationRunning();
+    }
+
+    @VisibleForTesting
+    List<Integer> shownTaskIds() {
+        if (!isShown()) {
+            return Collections.emptyList();
+        }
+
+        return mTasks.stream().flatMap(
+                groupTask -> groupTask.getTasks().stream().map(task -> task.key.id)).toList();
+    }
+
     @Override
     public void dumpLogs(String prefix, PrintWriter pw) {
         pw.println(prefix + "KeyboardQuickSwitchController:");
@@ -423,7 +445,13 @@
             if (task == null) {
                 return false;
             }
-            int runningTaskId = ActivityManagerWrapper.getInstance().getRunningTask().taskId;
+            ActivityManager.RunningTaskInfo runningTaskInfo =
+                    ActivityManagerWrapper.getInstance().getRunningTask();
+            if (runningTaskInfo == null) {
+                return false;
+            }
+
+            int runningTaskId = runningTaskInfo.taskId;
             return task.containsTask(runningTaskId);
         }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
index f80dc90..15be03a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
@@ -40,6 +40,8 @@
 import com.android.quickstep.util.BorderAnimator;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
 
 import kotlin.Unit;
 
@@ -64,6 +66,11 @@
     @Nullable private ImageView mIcon2;
     @Nullable private View mContent;
 
+    // Describe the task position in the parent container. Used to add information about the task's
+    // position in a task list to the task view's content description.
+    private int mIndexInParent = -1;
+    private int mTotalTasksInParent = -1;
+
     public KeyboardQuickSwitchTaskView(@NonNull Context context) {
         this(context, null);
     }
@@ -108,11 +115,11 @@
 
         TypefaceUtils.setTypeface(
                 mContent.findViewById(R.id.large_text),
-                TypefaceUtils.FONT_FAMILY_HEADLINE_LARGE_EMPHASIZED
+                FontFamily.GSF_HEADLINE_LARGE_EMPHASIZED
         );
         TypefaceUtils.setTypeface(
                 mContent.findViewById(R.id.small_text),
-                TypefaceUtils.FONT_FAMILY_LABEL_LARGE_BASELINE
+                FontFamily.GSF_LABEL_LARGE
         );
 
         Resources resources = mContext.getResources();
@@ -153,36 +160,51 @@
         applyThumbnail(mThumbnailView1, task1, thumbnailUpdateFunction);
         applyThumbnail(mThumbnailView2, task2, thumbnailUpdateFunction);
 
+        // Update content description, even in cases task icons, and content descriptions need to be
+        // loaded asynchronously to ensure that the task has non empty description (assuming task
+        // position information was set), as KeyboardQuickSwitch view may request accessibility
+        // focus to be moved to the task when the quick switch UI gets shown. The description will
+        // be updated once the task metadata has been loaded - the delay should be very short, and
+        // the content description when task titles are not available still gives some useful
+        // information to the user (the task's position in the list).
+        updateContentDesctiptionForTasks(task1, task2);
+
         if (iconUpdateFunction == null) {
             applyIcon(mIcon1, task1);
             applyIcon(mIcon2, task2);
-            setContentDescription(task2 == null
-                    ? task1.titleDescription
-                    : getContext().getString(
-                            R.string.quick_switch_split_task,
-                            task1.titleDescription,
-                            task2.titleDescription));
             return;
         }
+
         iconUpdateFunction.updateIconInBackground(task1, t -> {
             applyIcon(mIcon1, task1);
             if (task2 != null) {
                 return;
             }
-            setContentDescription(task1.titleDescription);
+            updateContentDesctiptionForTasks(task1, null);
         });
+
         if (task2 == null) {
             return;
         }
         iconUpdateFunction.updateIconInBackground(task2, t -> {
             applyIcon(mIcon2, task2);
-            setContentDescription(getContext().getString(
-                    R.string.quick_switch_split_task,
-                    task1.titleDescription,
-                    task2.titleDescription));
+            updateContentDesctiptionForTasks(task1, task2);
         });
     }
 
+    /**
+     * Initializes information about the task's position within the parent container context - used
+     * to add position information to the view's content description.
+     * Should be called before associating the view with tasks.
+     *
+     * @param index The view's 0-based index within the parent task container.
+     * @param totalTasks The total number of tasks in the parent task container.
+     */
+    protected void setPositionInformation(int index, int totalTasks) {
+        mIndexInParent = index;
+        mTotalTasksInParent = totalTasks;
+    }
+
     protected void setThumbnailsForSplitTasks(
             @NonNull Task task1,
             @Nullable Task task2,
@@ -281,6 +303,28 @@
                 constantState.newDrawable(getResources(), getContext().getTheme()));
     }
 
+    /**
+     * Updates the task view's content description to reflect tasks represented by the view.
+     */
+    private void updateContentDesctiptionForTasks(@NonNull Task task1, @Nullable Task task2) {
+        String tasksDescription = task1.titleDescription == null || task2 == null
+                ? task1.titleDescription
+                : getContext().getString(
+                        R.string.quick_switch_split_task,
+                        task1.titleDescription,
+                        task2.titleDescription);
+        if (mIndexInParent < 0) {
+            setContentDescription(tasksDescription);
+            return;
+        }
+
+        setContentDescription(
+                getContext().getString(R.string.quick_switch_task_with_position_in_parent,
+                        tasksDescription != null ? tasksDescription : "",
+                        mIndexInParent + 1,
+                        mTotalTasksInParent));
+    }
+
     protected interface ThumbnailUpdateFunction {
 
         void updateThumbnailInBackground(Task task, Consumer<ThumbnailData> callback);
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
index 4b4d68d..ab147bb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
@@ -30,10 +30,12 @@
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
 import android.view.ViewTreeObserver;
 import android.view.animation.Interpolator;
 import android.widget.HorizontalScrollView;
+import android.widget.ImageButton;
 import android.widget.TextView;
 import android.window.OnBackInvokedDispatcher;
 import android.window.WindowOnBackInvokedDispatcher;
@@ -45,6 +47,7 @@
 
 import com.android.app.animation.Interpolators;
 import com.android.internal.jank.Cuj;
+import com.android.launcher3.Flags;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatedFloat;
@@ -55,6 +58,8 @@
 import com.android.quickstep.util.SplitTask;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
+import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
 
 import java.util.HashMap;
 import java.util.List;
@@ -102,8 +107,12 @@
     private HorizontalScrollView mScrollView;
     private ConstraintLayout mContent;
 
-    private int mTaskViewWidth;
-    private int mTaskViewHeight;
+    private boolean mSupportsScrollArrows = false;
+    private ImageButton mStartScrollArrow;
+    private ImageButton mEndScrollArrow;
+
+    private int mTaskViewBorderWidth;
+    private int mTaskViewRadius;
     private int mSpacing;
     private int mSmallSpacing;
     private int mOutlineRadius;
@@ -112,11 +121,13 @@
     private int mOverviewTaskIndex = -1;
     private int mDesktopTaskIndex = -1;
 
-    @Nullable private AnimatorSet mOpenAnimation;
+    @Nullable
+    private AnimatorSet mOpenAnimation;
 
     private boolean mIsBackCallbackRegistered = false;
 
-    @Nullable private KeyboardQuickSwitchViewController.ViewCallbacks mViewCallbacks;
+    @Nullable
+    private KeyboardQuickSwitchViewController.ViewCallbacks mViewCallbacks;
 
     public KeyboardQuickSwitchView(@NonNull Context context) {
         this(context, null);
@@ -152,21 +163,37 @@
         mNoRecentItemsPane = findViewById(R.id.no_recent_items_pane);
         mScrollView = findViewById(R.id.scroll_view);
         mContent = findViewById(R.id.content);
+        mStartScrollArrow = findViewById(R.id.scroll_button_start);
+        mEndScrollArrow = findViewById(R.id.scroll_button_end);
+
+        setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
 
         Resources resources = getResources();
-        mTaskViewWidth = resources.getDimensionPixelSize(
-                R.dimen.keyboard_quick_switch_taskview_width);
-        mTaskViewHeight = resources.getDimensionPixelSize(
-                R.dimen.keyboard_quick_switch_taskview_height);
         mSpacing = resources.getDimensionPixelSize(R.dimen.keyboard_quick_switch_view_spacing);
         mSmallSpacing = resources.getDimensionPixelSize(
                 R.dimen.keyboard_quick_switch_view_small_spacing);
         mOutlineRadius = resources.getDimensionPixelSize(R.dimen.keyboard_quick_switch_view_radius);
+        mTaskViewBorderWidth = resources.getDimensionPixelSize(
+                R.dimen.keyboard_quick_switch_border_width);
+        mTaskViewRadius = resources.getDimensionPixelSize(
+                R.dimen.keyboard_quick_switch_task_view_radius);
+
         mIsRtl = Utilities.isRtl(resources);
 
+        if (Flags.taskbarOverflow()) {
+            initializeScrollArrows();
+
+            if (mIsRtl) {
+                mStartScrollArrow.setContentDescription(
+                        resources.getString(R.string.quick_switch_scroll_arrow_right));
+                mEndScrollArrow.setContentDescription(
+                        resources.getString(R.string.quick_switch_scroll_arrow_left));
+            }
+        }
+
         TypefaceUtils.setTypeface(
                 mNoRecentItemsPane.findViewById(R.id.no_recent_items_text),
-                TypefaceUtils.FONT_FAMILY_LABEL_LARGE_BASELINE);
+                FontFamily.GSF_LABEL_LARGE);
     }
 
     private void registerOnBackInvokedCallback() {
@@ -274,6 +301,7 @@
                 continue;
             }
 
+            currentTaskView.setPositionInformation(i, tasksToDisplay);
             currentTaskView.setThumbnailsForSplitTasks(
                     task1,
                     task2,
@@ -331,6 +359,78 @@
                 });
     }
 
+    private void initializeScrollArrows() {
+        mSupportsScrollArrows = true;
+
+        mStartScrollArrow.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mIsRtl) {
+                    runScrollCommand(false, () -> {
+                        mScrollView.smoothScrollBy(mScrollView.getWidth(), 0);
+                    });
+                } else {
+                    runScrollCommand(false, () -> {
+                        mScrollView.smoothScrollBy(-mScrollView.getWidth(), 0);
+                    });
+                }
+            }
+        });
+
+        mEndScrollArrow.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mIsRtl) {
+                    runScrollCommand(false, () -> {
+                        mScrollView.smoothScrollBy(-mScrollView.getWidth(), 0);
+                    });
+                } else {
+                    runScrollCommand(false, () -> {
+                        mScrollView.smoothScrollBy(mScrollView.getWidth(), 0);
+                    });
+                }
+            }
+        });
+
+        // Add listeners to disable arrow buttons when the scroll view cannot be further scrolled in
+        // the associated direction.
+        mScrollView.setOnScrollChangeListener(new OnScrollChangeListener() {
+            @Override
+            public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX,
+                    int oldScrollY) {
+                updateArrowButtonsEnabledState();
+            }
+        });
+
+        // Update scroll view outline to clip its contents with rounded corners.
+        mScrollView.setClipToOutline(true);
+        mScrollView.setOutlineProvider(new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+                int spacingWithoutBorder = mSpacing - mTaskViewBorderWidth;
+                outline.setRoundRect(spacingWithoutBorder,
+                        spacingWithoutBorder, view.getWidth() - spacingWithoutBorder,
+                        view.getHeight() - spacingWithoutBorder,
+                        mTaskViewRadius);
+            }
+        });
+    }
+
+    private void updateArrowButtonsEnabledState() {
+        if (!mDisplayingRecentTasks) {
+            return;
+        }
+
+        int scrollX = mScrollView.getScrollX();
+        if (mIsRtl) {
+            mEndScrollArrow.setEnabled(scrollX > 0);
+            mStartScrollArrow.setEnabled(scrollX < mContent.getWidth() - mScrollView.getWidth());
+        } else {
+            mStartScrollArrow.setEnabled(scrollX > 0);
+            mEndScrollArrow.setEnabled(scrollX < mContent.getWidth() - mScrollView.getWidth());
+        }
+    }
+
     int getOverviewTaskIndex() {
         return mOverviewTaskIndex;
     }
@@ -346,6 +446,21 @@
         mViewCallbacks = null;
     }
 
+    private void animateDisplayedContentForClose(View view, AnimatorSet animator) {
+        Animator translationYAnimation = ObjectAnimator.ofFloat(
+                view,
+                TRANSLATION_Y,
+                0, -Utilities.dpToPx(CONTENT_START_TRANSLATION_Y_DP));
+        translationYAnimation.setDuration(CONTENT_TRANSLATION_Y_ANIMATION_DURATION_MS);
+        translationYAnimation.setInterpolator(CLOSE_TRANSLATION_Y_INTERPOLATOR);
+        animator.play(translationYAnimation);
+
+        Animator contentAlphaAnimation = ObjectAnimator.ofFloat(view, ALPHA, 1f, 0f);
+        contentAlphaAnimation.setDuration(CONTENT_ALPHA_ANIMATION_DURATION_MS);
+        animator.play(contentAlphaAnimation);
+
+    }
+
     protected Animator getCloseAnimation() {
         AnimatorSet closeAnimation = new AnimatorSet();
 
@@ -360,17 +475,11 @@
         closeAnimation.play(alphaAnimation);
 
         View displayedContent = mDisplayingRecentTasks ? mScrollView : mNoRecentItemsPane;
-        Animator translationYAnimation = ObjectAnimator.ofFloat(
-                displayedContent,
-                TRANSLATION_Y,
-                0, -Utilities.dpToPx(CONTENT_START_TRANSLATION_Y_DP));
-        translationYAnimation.setDuration(CONTENT_TRANSLATION_Y_ANIMATION_DURATION_MS);
-        translationYAnimation.setInterpolator(CLOSE_TRANSLATION_Y_INTERPOLATOR);
-        closeAnimation.play(translationYAnimation);
-
-        Animator contentAlphaAnimation = ObjectAnimator.ofFloat(displayedContent, ALPHA, 1f, 0f);
-        contentAlphaAnimation.setDuration(CONTENT_ALPHA_ANIMATION_DURATION_MS);
-        closeAnimation.play(contentAlphaAnimation);
+        animateDisplayedContentForClose(displayedContent, closeAnimation);
+        if (mSupportsScrollArrows) {
+            animateDisplayedContentForClose(mStartScrollArrow, closeAnimation);
+            animateDisplayedContentForClose(mEndScrollArrow, closeAnimation);
+        }
 
         closeAnimation.addListener(new AnimatorListenerAdapter() {
             @Override
@@ -385,6 +494,31 @@
         return closeAnimation;
     }
 
+    private void animateDisplayedContentForOpen(View view, AnimatorSet animator) {
+        Animator translationXAnimation = ObjectAnimator.ofFloat(
+                view,
+                TRANSLATION_X,
+                -Utilities.dpToPx(CONTENT_START_TRANSLATION_X_DP), 0);
+        translationXAnimation.setDuration(CONTENT_TRANSLATION_X_ANIMATION_DURATION_MS);
+        translationXAnimation.setInterpolator(OPEN_TRANSLATION_X_INTERPOLATOR);
+        animator.play(translationXAnimation);
+
+        Animator translationYAnimation = ObjectAnimator.ofFloat(
+                view,
+                TRANSLATION_Y,
+                -Utilities.dpToPx(CONTENT_START_TRANSLATION_Y_DP), 0);
+        translationYAnimation.setDuration(CONTENT_TRANSLATION_Y_ANIMATION_DURATION_MS);
+        translationYAnimation.setInterpolator(OPEN_TRANSLATION_Y_INTERPOLATOR);
+        animator.play(translationYAnimation);
+
+        view.setAlpha(0.0f);
+        Animator contentAlphaAnimation = ObjectAnimator.ofFloat(view, ALPHA, 0f,
+                1f);
+        contentAlphaAnimation.setStartDelay(CONTENT_ALPHA_ANIMATION_START_DELAY_MS);
+        contentAlphaAnimation.setDuration(CONTENT_ALPHA_ANIMATION_DURATION_MS);
+        animator.play(contentAlphaAnimation);
+    }
+
     protected void animateOpen(int currentFocusIndexOverride) {
         if (mOpenAnimation != null) {
             // Restart animation since currentFocusIndexOverride can change the initial scroll.
@@ -407,28 +541,17 @@
         mOpenAnimation.play(alphaAnimation);
 
         View displayedContent = mDisplayingRecentTasks ? mScrollView : mNoRecentItemsPane;
-        Animator translationXAnimation = ObjectAnimator.ofFloat(
-                displayedContent,
-                TRANSLATION_X,
-                -Utilities.dpToPx(CONTENT_START_TRANSLATION_X_DP), 0);
-        translationXAnimation.setDuration(CONTENT_TRANSLATION_X_ANIMATION_DURATION_MS);
-        translationXAnimation.setInterpolator(OPEN_TRANSLATION_X_INTERPOLATOR);
-        mOpenAnimation.play(translationXAnimation);
+        animateDisplayedContentForOpen(displayedContent, mOpenAnimation);
+        if (mSupportsScrollArrows) {
+            animateDisplayedContentForOpen(mStartScrollArrow, mOpenAnimation);
+            animateDisplayedContentForOpen(mEndScrollArrow, mOpenAnimation);
+        }
 
-        Animator translationYAnimation = ObjectAnimator.ofFloat(
-                displayedContent,
-                TRANSLATION_Y,
-                -Utilities.dpToPx(CONTENT_START_TRANSLATION_Y_DP), 0);
-        translationYAnimation.setDuration(CONTENT_TRANSLATION_Y_ANIMATION_DURATION_MS);
-        translationYAnimation.setInterpolator(OPEN_TRANSLATION_Y_INTERPOLATOR);
-        mOpenAnimation.play(translationYAnimation);
-
-        Animator contentAlphaAnimation = ObjectAnimator.ofFloat(displayedContent, ALPHA, 0f, 1f);
-        contentAlphaAnimation.setStartDelay(CONTENT_ALPHA_ANIMATION_START_DELAY_MS);
-        contentAlphaAnimation.setDuration(CONTENT_ALPHA_ANIMATION_DURATION_MS);
-        mOpenAnimation.play(contentAlphaAnimation);
 
         ViewOutlineProvider outlineProvider = getOutlineProvider();
+        int defaultFocusedTaskIndex = Math.min(
+                getTaskCount() - 1,
+                currentFocusIndexOverride == -1 ? 1 : currentFocusIndexOverride);
         mOpenAnimation.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
@@ -461,9 +584,28 @@
                                         OPEN_OUTLINE_INTERPOLATOR));
                     }
                 });
-                animateFocusMove(-1, Math.min(
-                        getTaskCount() - 1,
-                        currentFocusIndexOverride == -1 ? 1 : currentFocusIndexOverride));
+
+                if (mSupportsScrollArrows) {
+                    mScrollView.getViewTreeObserver().addOnGlobalLayoutListener(
+                            new ViewTreeObserver.OnGlobalLayoutListener() {
+                                @Override
+                                public void onGlobalLayout() {
+                                    if (mScrollView.getWidth() == 0) {
+                                        return;
+                                    }
+
+                                    if (mContent.getWidth() > mScrollView.getWidth()) {
+                                        mStartScrollArrow.setVisibility(VISIBLE);
+                                        mEndScrollArrow.setVisibility(VISIBLE);
+                                        updateArrowButtonsEnabledState();
+                                    }
+                                    mScrollView.getViewTreeObserver().removeOnGlobalLayoutListener(
+                                            this);
+                                }
+                            });
+                }
+
+                animateFocusMove(-1, defaultFocusedTaskIndex);
                 displayedContent.setVisibility(VISIBLE);
                 setVisibility(VISIBLE);
                 requestFocus();
@@ -483,6 +625,11 @@
                 invalidateOutline();
                 mOpenAnimation = null;
                 InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_KEYBOARD_QUICK_SWITCH_OPEN);
+
+                View focusedTask = getTaskAt(defaultFocusedTaskIndex);
+                if (focusedTask != null) {
+                    focusedTask.requestAccessibilityFocus();
+                }
             }
         });
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index 5f7a026..b5f2532 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -40,7 +40,6 @@
 import com.android.launcher3.desktop.DesktopAppLaunchTransition;
 import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
 import com.android.launcher3.taskbar.overlay.TaskbarOverlayDragLayer;
-import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.util.GroupTask;
@@ -106,8 +105,7 @@
             boolean hasDesktopTask,
             boolean wasDesktopTaskFilteredOut,
             boolean wasOpenedFromTaskbar) {
-        final boolean isTransientTaskBar = DisplayController.isTransientTaskbar(
-                mControllers.taskbarActivityContext);
+        final boolean isTransientTaskBar = mControllers.taskbarActivityContext.isTransientTaskbar();
         positionView(wasOpenedFromTaskbar, isTransientTaskBar);
 
         // Keep the taskbar unstashed if the KQS is opened.
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 3f3700b..e998388 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -135,11 +135,11 @@
     @Override
     protected void onDestroy() {
         onLauncherVisibilityChanged(false /* isVisible */, true /* fromInitOrDestroy */);
+        mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
         super.onDestroy();
         mTaskbarLauncherStateController.onDestroy();
 
         mLauncher.setTaskbarUIController(null);
-        mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
         mHomeState.removeListener(mVisibilityChangeListener);
     }
 
@@ -225,9 +225,8 @@
         if (isVisible || isPinnedTaskbar) {
             return getTaskbarToHomeDuration(shouldOverrideToFastAnimation, isPinnedTaskbar);
         } else {
-            return DisplayController.isTransientTaskbar(mLauncher)
-                    ? TRANSIENT_TASKBAR_TRANSITION_DURATION
-                    : TASKBAR_TO_APP_DURATION;
+            return mControllers.taskbarActivityContext.isTransientTaskbar()
+                    ? TRANSIENT_TASKBAR_TRANSITION_DURATION : TASKBAR_TO_APP_DURATION;
         }
     }
 
@@ -279,7 +278,10 @@
     private void postAdjustHotseatForBubbleBar() {
         Hotseat hotseat = mLauncher.getHotseat();
         if (hotseat == null || !isBubbleBarVisible()) return;
-        hotseat.post(() -> adjustHotseatForBubbleBar(isBubbleBarVisible()));
+        hotseat.post(() -> {
+            if (mControllers == null) return;
+            adjustHotseatForBubbleBar(isBubbleBarVisible());
+        });
     }
 
     private boolean isBubbleBarVisible() {
@@ -334,7 +336,7 @@
         }
 
         // Persistent features EDU tooltip.
-        if (!DisplayController.isTransientTaskbar(mLauncher)) {
+        if (!mControllers.taskbarActivityContext.isTransientTaskbar()) {
             mControllers.taskbarEduTooltipController.maybeShowFeaturesEdu();
             return;
         }
@@ -357,7 +359,7 @@
         }
 
         // Persistent features EDU tooltip.
-        if (!DisplayController.isTransientTaskbar(mLauncher)) {
+        if (!mControllers.taskbarActivityContext.isTransientTaskbar()) {
             return !OnboardingPrefs.TASKBAR_EDU_TOOLTIP_STEP.hasReachedMax(mLauncher);
         }
 
@@ -467,10 +469,15 @@
     }
 
     @Override
-    protected boolean canToggleHomeAllApps() {
-        return mLauncher.isResumed()
+    protected void toggleAllApps(boolean focusSearch) {
+        boolean canToggleHomeAllApps = mLauncher.isResumed()
                 && !mTaskbarLauncherStateController.isInOverviewUi()
                 && !mLauncher.areDesktopTasksVisible();
+        if (canToggleHomeAllApps) {
+            mLauncher.toggleAllApps(focusSearch);
+            return;
+        }
+        super.toggleAllApps(focusSearch);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index ee5b8d1..27b38e5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -870,6 +870,9 @@
         if (predictiveBackThreeButtonNav() && buttonType == BUTTON_BACK) {
             // set up special touch listener for back button to support predictive back
             setBackButtonTouchListener(buttonView, navButtonController);
+            // Set this View clickable, so that NearestTouchFrame.java forwards closeby touches to
+            // this View
+            buttonView.setClickable(true);
         } else {
             buttonView.setOnClickListener(view ->
                     navButtonController.onButtonClick(buttonType, view));
diff --git a/quickstep/src/com/android/launcher3/taskbar/PinToTaskbarShortcut.kt b/quickstep/src/com/android/launcher3/taskbar/PinToTaskbarShortcut.kt
new file mode 100644
index 0000000..da6932f
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/PinToTaskbarShortcut.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar
+
+import android.content.Context
+import android.util.SparseArray
+import android.view.View
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.LauncherAppState
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
+import com.android.launcher3.R
+import com.android.launcher3.model.BgDataModel
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.popup.SystemShortcut
+import com.android.launcher3.views.ActivityContext
+
+/**
+ * A single menu item shortcut to allow users to pin an item to the taskbar and unpin an item from
+ * the taskbar.
+ */
+class PinToTaskbarShortcut<T>(
+    target: T,
+    itemInfo: ItemInfo?,
+    originalView: View,
+    private val mIsPin: Boolean,
+    private val mPinnedInfoList: SparseArray<ItemInfo?>,
+) :
+    SystemShortcut<T>(
+        if (mIsPin) R.drawable.ic_pin else R.drawable.ic_unpin,
+        if (mIsPin) R.string.pin_to_taskbar else R.string.unpin_from_taskbar,
+        target,
+        itemInfo,
+        originalView,
+    ) where T : Context?, T : ActivityContext? {
+
+    override fun onClick(v: View?) {
+        dismissTaskMenuView()
+        // Create a placeholder callbacks for the writer to notify other launcher model callbacks
+        // after update.
+        val callbacks: BgDataModel.Callbacks = object : BgDataModel.Callbacks {}
+
+        val writer =
+            LauncherAppState.getInstance(mOriginalView.context)
+                .model
+                .getWriter(true, mTarget!!.cellPosMapper, callbacks)
+
+        if (!mIsPin) {
+            writer.deleteItemFromDatabase(mItemInfo, "item unpinned through long-press menu")
+            return
+        }
+
+        val newInfo =
+            if (mItemInfo is com.android.launcher3.model.data.AppInfo) {
+                mItemInfo.makeWorkspaceItem(mOriginalView.context)
+            } else if (mItemInfo is WorkspaceItemInfo) {
+                mItemInfo.clone()
+            } else {
+                return
+            }
+
+        val dp: DeviceProfile = mTarget.deviceProfile
+        var targetIdx = -1
+
+        for (i in 0 until dp.numShownHotseatIcons) {
+            if (mPinnedInfoList[i] == null) {
+                targetIdx = i
+                break
+            }
+        }
+
+        val cellX = if (dp.isVerticalBarLayout()) 0 else targetIdx
+        val cellY = if (dp.isVerticalBarLayout()) (dp.numShownHotseatIcons - (targetIdx + 1)) else 0
+
+        writer.addItemToDatabase(newInfo, CONTAINER_HOTSEAT, mItemInfo.screenId, cellX, cellY)
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index 2e5bebc..6bd3d85 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -36,7 +36,6 @@
 import com.android.launcher3.anim.AnimatedFloat;
 import com.android.launcher3.anim.RevealOutlineAnimation;
 import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
-import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.MultiPropertyFactory;
 import com.android.launcher3.util.MultiValueAlpha;
@@ -217,7 +216,7 @@
                 .getTransientTaskbarIconLayoutBounds();
         float startRadius = mStashedHandleRadius;
 
-        if (DisplayController.isTransientTaskbar(mActivity)) {
+        if (mActivity.isTransientTaskbar()) {
             // Account for the full visual height of the transient taskbar.
             int heightDiff = (mTaskbarSize - visualBounds.height()) / 2;
             visualBounds.top -= heightDiff;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 57bcc14..e9d8209 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -71,7 +71,6 @@
 import android.os.Trace;
 import android.provider.Settings;
 import android.util.Log;
-import android.view.Display;
 import android.view.Gravity;
 import android.view.Surface;
 import android.view.View;
@@ -81,6 +80,7 @@
 import android.widget.Toast;
 import android.window.DesktopExperienceFlags;
 import android.window.DesktopModeFlags;
+import android.window.DesktopModeFlags.DesktopModeFlag;
 import android.window.RemoteTransition;
 
 import androidx.annotation.NonNull;
@@ -94,15 +94,17 @@
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.BubbleTextView.RunningAppState;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
 import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.R;
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.apppairs.AppPairIcon;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.desktop.DesktopAppLaunchTransition;
 import com.android.launcher3.desktop.DesktopAppLaunchTransition.AppLaunchType;
-import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.logger.LauncherAtom;
@@ -149,6 +151,7 @@
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.LauncherBindableItemsContainer;
 import com.android.launcher3.util.MultiPropertyFactory;
 import com.android.launcher3.util.NavigationMode;
 import com.android.launcher3.util.RunnableList;
@@ -156,7 +159,6 @@
 import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.util.VibratorWrapper;
-import com.android.launcher3.util.ViewCache;
 import com.android.launcher3.views.ActivityContext;
 import com.android.quickstep.NavHandle;
 import com.android.quickstep.RecentsModel;
@@ -196,6 +198,9 @@
 
     private static final String WINDOW_TITLE = "Taskbar";
 
+    private static final DesktopModeFlag ENABLE_TASKBAR_BEHIND_SHADE = new DesktopModeFlag(
+            Flags::enableTaskbarBehindShade, false);
+
     private final @Nullable Context mNavigationBarPanelContext;
 
     private final TaskbarDragLayer mDragLayer;
@@ -219,7 +224,6 @@
 
     private NavigationMode mNavMode;
     private boolean mImeDrawsImeNavBar;
-    private final ViewCache mViewCache = new ViewCache();
 
     private final boolean mIsSafeModeEnabled;
     private final boolean mIsUserSetupComplete;
@@ -229,7 +233,6 @@
     private boolean mIsDestroyed = false;
     // The flag to know if the window is excluded from magnification region computation.
     private boolean mIsExcludeFromMagnificationRegion = false;
-    private boolean mBindingItems = false;
     private boolean mAddedWindow = false;
 
     // The bounds of the taskbar items relative to TaskbarDragLayer
@@ -253,7 +256,7 @@
             TaskbarNavButtonController buttonController,
             ScopedUnfoldTransitionProgressProvider unfoldTransitionProgressProvider,
             boolean isPrimaryDisplay, SystemUiProxy sysUiProxy) {
-        super(windowContext);
+        super(windowContext, isPrimaryDisplay);
         mIsPrimaryDisplay = isPrimaryDisplay;
         mNavigationBarPanelContext = navigationBarPanelContext;
         mSysUiProxy = sysUiProxy;
@@ -280,7 +283,6 @@
         mIsNavBarForceVisible = mIsNavBarKidsMode;
 
         // Get display and corners first, as views might use them in constructor.
-        Display display = windowContext.getDisplay();
         Context c = getApplicationContext();
         mWindowManager = c.getSystemService(WindowManager.class);
 
@@ -326,11 +328,11 @@
                     new BubbleBarViewController(this, bubbleBarView, bubbleBarContainer),
                     bubbleStashController,
                     bubbleHandleController,
-                    new BubbleDragController(this),
+                    new BubbleDragController(this, mDragLayer),
                     new BubbleDismissController(this, mDragLayer),
-                    new BubbleBarPinController(this, mDragLayer,
+                    new BubbleBarPinController(this, bubbleBarContainer,
                             () -> DisplayController.INSTANCE.get(this).getInfo().currentSize),
-                    new BubblePinController(this, mDragLayer,
+                    new BubblePinController(this, bubbleBarContainer,
                             () -> DisplayController.INSTANCE.get(this).getInfo().currentSize),
                     bubbleBarSwipeController,
                     new BubbleCreator(this)
@@ -382,6 +384,7 @@
                         DesktopVisibilityController.INSTANCE.get(this)));
 
         mLauncherPrefs = LauncherPrefs.get(this);
+        onViewCreated();
     }
 
     /** Updates {@link DeviceProfile} instances for any Taskbar windows. */
@@ -402,7 +405,7 @@
 
     /** Returns whether current taskbar is transient. */
     public boolean isTransientTaskbar() {
-        return DisplayController.isTransientTaskbar(this) && !isPhoneMode();
+        return super.isTransientTaskbar() && !isPhoneMode();
     }
 
     /**
@@ -423,7 +426,7 @@
         mDeviceProfile = originDeviceProfile.toBuilder(this)
                 .withDimensionsOverride(overrideProvider).build();
 
-        if (DisplayController.isTransientTaskbar(this)) {
+        if (isTransientTaskbar()) {
             mTransientTaskbarDeviceProfile = mDeviceProfile;
             mPersistentTaskbarDeviceProfile = mDeviceProfile
                     .toBuilder(this)
@@ -441,7 +444,6 @@
         mNavMode = (DesktopExperienceFlags.ENABLE_TASKBAR_CONNECTED_DISPLAYS.isTrue()
                 && !mIsPrimaryDisplay) ? NavigationMode.THREE_BUTTONS
                 : DisplayController.getNavigationMode(this);
-
     }
 
     /** Called when the visibility of the bubble bar changed. */
@@ -608,16 +610,6 @@
         mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false);
     }
 
-    /** Toggles Taskbar All Apps overlay. */
-    public void toggleAllApps() {
-        mControllers.taskbarAllAppsController.toggle();
-    }
-
-    /** Toggles Taskbar All Apps overlay with keyboard ready for search. */
-    public void toggleAllAppsSearch() {
-        mControllers.taskbarAllAppsController.toggleSearch();
-    }
-
     @Override
     public DeviceProfile getDeviceProfile() {
         return mDeviceProfile;
@@ -662,8 +654,7 @@
         int windowFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                 | WindowManager.LayoutParams.FLAG_SLIPPERY
                 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
-        boolean watchOutside = DisplayController.isTransientTaskbar(this)
-                || isThreeButtonNav();
+        boolean watchOutside = isTransientTaskbar() || isThreeButtonNav();
         if (watchOutside && !isRunningInTestHarness()) {
             windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                     | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
@@ -806,11 +797,6 @@
     }
 
     @Override
-    public ViewCache getViewCache() {
-        return mViewCache;
-    }
-
-    @Override
     public View.OnClickListener getItemOnClickListener() {
         return this::onTaskbarIconClicked;
     }
@@ -878,32 +864,29 @@
         }
     }
 
-    @Override
-    public DotInfo getDotInfoForItem(ItemInfo info) {
-        return getPopupDataProvider().getDotInfoForItem(info);
-    }
-
     @NonNull
     @Override
     public PopupDataProvider getPopupDataProvider() {
         return mControllers.taskbarPopupController.getPopupDataProvider();
     }
 
+    @NonNull
+    @Override
+    public LauncherBindableItemsContainer getContent() {
+        return mControllers.taskbarViewController.getContent();
+    }
+
+    @Override
+    public ActivityAllAppsContainerView<?> getAppsView() {
+        return mControllers.taskbarAllAppsController.getAppsView();
+    }
+
     @Override
     public View.AccessibilityDelegate getAccessibilityDelegate() {
         return mAccessibilityDelegate;
     }
 
     @Override
-    public boolean isBindingItems() {
-        return mBindingItems;
-    }
-
-    public void setBindingItems(boolean bindingItems) {
-        mBindingItems = bindingItems;
-    }
-
-    @Override
     public void onDragStart() {
         setTaskbarWindowFullscreen(true);
     }
@@ -935,7 +918,7 @@
         options.setSplashScreenStyle(splashScreenStyle);
         options.setPendingIntentBackgroundActivityStartMode(
                 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
-        IRemoteCallback endCallback = completeRunnableListCallback(callbacks);
+        IRemoteCallback endCallback = completeRunnableListCallback(callbacks, this);
         options.setOnAnimationAbortListener(endCallback);
         options.setOnAnimationFinishedListener(endCallback);
 
@@ -1002,6 +985,7 @@
      * Called when this instance of taskbar is no longer needed
      */
     public void onDestroy() {
+        onViewDestroyed();
         mIsDestroyed = true;
         mTaskbarFeatureEvaluator.onDestroy();
         setUIController(TaskbarUIController.DEFAULT);
@@ -1231,8 +1215,8 @@
                 bubbleControllers.bubbleBarViewController.getBubbleBarWithFlyoutMaximumHeight()
         ).orElse(0);
         int taskbarWindowSize;
-        boolean shouldTreatAsTransient = DisplayController.isTransientTaskbar(this)
-                || (enableTaskbarPinning() && !isThreeButtonNav());
+        boolean shouldTreatAsTransient =
+                isTransientTaskbar() || (enableTaskbarPinning() && !isThreeButtonNav());
 
         int extraHeightForTaskbarTooltips = enableCursorHoverStates()
                 ? resources.getDimensionPixelSize(R.dimen.arrow_toast_arrow_height)
@@ -1305,7 +1289,7 @@
      * Applies forcibly show flag to taskbar window iff transient taskbar is unstashed.
      */
     public void applyForciblyShownFlagWhileTransientTaskbarUnstashed(boolean shouldForceShow) {
-        if (!DisplayController.isTransientTaskbar(this) || isPhoneMode()) {
+        if (!isTransientTaskbar() || isPhoneMode()) {
             return;
         }
         if (shouldForceShow) {
@@ -1409,14 +1393,9 @@
                             AppLaunchType.UNMINIMIZE, Cuj.CUJ_DESKTOP_MODE_APP_LAUNCH_FROM_ICON)
                     : null;
 
-            TaskView taskView = null;
-            if (recents != null) {
-                taskView = recents.getTaskViewByTaskId(info.getTaskId());
-            }
 
-            if (areDesktopTasksVisible() && taskView != null
-                    && mControllers.uiController.isInOverviewUi()) {
-                RunnableList runnableList = taskView.launchWithAnimation();
+            if (areDesktopTasksVisible() && mControllers.uiController.isInOverviewUi()) {
+                RunnableList runnableList = recents.launchRunningDesktopTaskView();
                 if (runnableList != null) {
                     runnableList.add(() ->
                             // wrapped it in runnable here since we need the post for DW to be
@@ -1587,6 +1566,7 @@
     public boolean canUnminimizeDesktopTask(int taskId) {
         BubbleTextView.RunningAppState runningAppState =
                 mControllers.taskbarRecentAppsController.getRunningAppState(taskId);
+        Log.d(TAG, "Task id=" + taskId + ", Running app state=" + runningAppState);
         return runningAppState == RunningAppState.MINIMIZED
                 && DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_ALTTAB_TRANSITIONS_BUGFIX.isTrue();
     }
@@ -1797,7 +1777,7 @@
             folder.animateOpen();
             getStatsLogManager().logger().withItemInfo(folder.mInfo).log(LAUNCHER_FOLDER_OPEN);
 
-            folder.iterateOverItems((itemInfo, itemView) -> {
+            folder.mapOverItems((itemInfo, itemView) -> {
                 mControllers.taskbarViewController
                         .setClickAndLongClickListenersForIcon(itemView);
                 // To play haptic when dragging, like other Taskbar items do.
@@ -1899,7 +1879,7 @@
      */
     @VisibleForTesting
     public void unstashTaskbarIfStashed() {
-        if (DisplayController.isTransientTaskbar(this)) {
+        if (isTransientTaskbar()) {
             mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false);
         }
     }
@@ -1962,6 +1942,8 @@
                 // Override the alpha updates in the icon alignment animation.
                 allAppsButton.setAlpha(0);
             });
+            alphaOverride.addListener(AnimatorListeners.forSuccessCallback(
+                    () -> allAppsButton.setAlpha(1f)));
             fullAnimation.play(alphaOverride);
         }
 
@@ -2049,8 +2031,6 @@
                 "%s\tmIsUserSetupComplete=%b", prefix, mIsUserSetupComplete));
         pw.println(String.format(
                 "%s\tmWindowLayoutParams.height=%dpx", prefix, mWindowLayoutParams.height));
-        pw.println(String.format(
-                "%s\tmBindInProgress=%b", prefix, mBindingItems));
         mControllers.dumpLogs(prefix + "\t", pw);
         mDeviceProfile.dump(this, prefix, pw);
     }
@@ -2075,10 +2055,6 @@
         mControllers.keyboardQuickSwitchController.closeQuickSwitchView(false);
     }
 
-    boolean canToggleHomeAllApps() {
-        return mControllers.uiController.canToggleHomeAllApps();
-    }
-
     boolean isIconAlignedWithHotseat() {
         return mControllers.uiController.isIconAlignedWithHotseat();
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index 89cc991..abbcd6d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -30,7 +30,6 @@
 import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound
 import com.android.launcher3.taskbar.TaskbarPinningController.Companion.PINNING_PERSISTENT
 import com.android.launcher3.taskbar.TaskbarPinningController.Companion.PINNING_TRANSIENT
-import com.android.launcher3.util.DisplayController
 import kotlin.math.min
 
 /** Helps draw the taskbar background, made up of a rectangle plus two inverted rounded corners. */
@@ -43,7 +42,7 @@
     private val maxPersistentTaskbarHeight =
         context.persistentTaskbarDeviceProfile.taskbarHeight.toFloat()
     var backgroundProgress =
-        if (DisplayController.isTransientTaskbar(context)) {
+        if (context.isTransientTaskbar) {
             PINNING_TRANSIENT
         } else {
             PINNING_PERSISTENT
@@ -124,7 +123,7 @@
      * @param cornerRoundness 0 has no round corner, 1 has complete round corner.
      */
     fun setCornerRoundness(cornerRoundness: Float) {
-        if (DisplayController.isTransientTaskbar(context) && !transientBackgroundBounds.isEmpty) {
+        if (context.isTransientTaskbar && !transientBackgroundBounds.isEmpty) {
             return
         }
 
@@ -146,7 +145,7 @@
     /** Draws the background with the given paint and height, on the provided canvas. */
     fun draw(canvas: Canvas) {
         if (isInSetup) return
-        val isTransientTaskbar = DisplayController.isTransientTaskbar(context)
+        val isTransientTaskbar = context.isTransientTaskbar
         canvas.save()
         if (!isTransientTaskbar || transientBackgroundBounds.isEmpty || isAnimatingPinning) {
             drawPersistentBackground(canvas)
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index 6ca9385..58606de 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -195,8 +195,11 @@
                 voiceInteractionWindowController
         };
 
+        // TODO(b/401061748): get primary status from
+        //  TaskbarDesktopModeController/DesktopVisibilityController.
         if (taskbarDesktopModeController.isInDesktopModeAndNotInOverview(
-                taskbarActivityContext.getDisplayId())) {
+                taskbarActivityContext.getDisplayId())
+                || !taskbarActivityContext.isPrimaryDisplay()) {
             mCornerRoundness.value = taskbarDesktopModeController.getTaskbarCornerRoundness(
                     mSharedState.showCornerRadiusInDesktopMode);
         } else {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
index 3f6ebe2..e3e7499 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
@@ -35,7 +35,6 @@
 import com.android.launcher3.R
 import com.android.launcher3.popup.ArrowPopup
 import com.android.launcher3.popup.RoundedArrowDrawable
-import com.android.launcher3.util.DisplayController
 import com.android.launcher3.util.Themes
 import com.android.launcher3.views.ActivityContext
 import kotlin.math.max
@@ -64,12 +63,17 @@
                     false,
                 ) as TaskbarDividerPopupView<*>
 
-            return taskMenuViewWithArrow.populateForView(view, horizontalPosition)
+            return taskMenuViewWithArrow.populateForView(
+                view,
+                horizontalPosition,
+                taskbarActivityContext,
+            )
         }
     }
 
     private lateinit var dividerView: View
     private var horizontalPosition = 0.0f
+    private lateinit var taskbarActivityContext: TaskbarActivityContext
 
     private val popupCornerRadius = Themes.getDialogCornerRadius(context)
     private val arrowWidth = resources.getDimension(R.dimen.popup_arrow_width)
@@ -78,7 +82,7 @@
     private val minPaddingFromScreenEdge =
         resources.getDimension(R.dimen.taskbar_pinning_popup_menu_min_padding_from_screen_edge)
 
-    private var alwaysShowTaskbarOn = !DisplayController.isTransientTaskbar(context)
+    private var alwaysShowTaskbarOn = !taskbarActivityContext.isTransientTaskbar
     private var didPreferenceChange = false
     private var verticalOffsetForPopupView =
         resources.getDimensionPixelSize(R.dimen.taskbar_pinning_popup_menu_vertical_margin)
@@ -175,8 +179,13 @@
         return false
     }
 
-    private fun populateForView(view: View, horizontalPosition: Float): TaskbarDividerPopupView<*> {
+    private fun populateForView(
+        view: View,
+        horizontalPosition: Float,
+        taskbar: TaskbarActivityContext,
+    ): TaskbarDividerPopupView<*> {
         dividerView = view
+        taskbarActivityContext = taskbar
         this@TaskbarDividerPopupView.horizontalPosition = horizontalPosition
         tryUpdateBackground()
         return this
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index d531e2c..c884d39 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -68,6 +68,7 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.folder.Folder;
 import com.android.launcher3.graphics.DragPreviewProvider;
 import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
 import com.android.launcher3.logging.StatsLogManager;
@@ -79,7 +80,6 @@
 import com.android.launcher3.taskbar.bubbles.BubbleBarViewController;
 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;
@@ -116,6 +116,7 @@
     private int mRegistrationY;
 
     private boolean mIsSystemDragInProgress;
+    private boolean mIsDropHandledByDropTarget;
 
     // Animation for the drag shadow back into position after an unsuccessful drag
     private ValueAnimator mReturnAnimator;
@@ -252,7 +253,8 @@
                 /* originalView = */ btv,
                 dragLayerX + dragOffset.x,
                 dragLayerY + dragOffset.y,
-                (View target, DropTarget.DragObject d, boolean success) -> {} /* DragSource */,
+                (View target, DropTarget.DragObject d, boolean success) ->
+                        mIsDropHandledByDropTarget = success /* DragSource */,
                 btv.getTag() instanceof ItemInfo itemInfo ? itemInfo : null,
                 dragRect,
                 scale * iconScale,
@@ -460,7 +462,7 @@
             com.android.launcher3.logging.InstanceId launcherInstanceId = instanceIds.second;
 
             intent.putExtra(ClipDescription.EXTRA_LOGGING_INSTANCE_ID, internalInstanceId);
-            if (DisplayController.isTransientTaskbar(mActivity)) {
+            if (mActivity.isTransientTaskbar()) {
                 // Tell WM Shell to ignore drag events in the provided transient taskbar region.
                 TaskbarDragLayer dragLayer = mControllers.taskbarActivityContext.getDragLayer();
                 int[] locationOnScreen = dragLayer.getLocationOnScreen();
@@ -561,7 +563,7 @@
 
     @Override
     protected void endDrag() {
-        if (mDisallowGlobalDrag) {
+        if (mDisallowGlobalDrag && !mIsDropHandledByDropTarget) {
             // We need to explicitly set deferDragViewCleanupPostAnimation to true here so the
             // super call doesn't remove it from the drag layer before the animation completes.
             // This variable gets set in to false in super.dispatchDropComplete() because it
@@ -765,8 +767,11 @@
 
     @Override
     public void addDropTarget(DropTarget target) {
-        // No-op as Taskbar currently doesn't support any drop targets internally.
-        // Note: if we do add internal DropTargets, we'll still need to ignore Folder.
+        if (target instanceof Folder) {
+            // we need to ignore Folder.
+            return;
+        }
+        super.addDropTarget(target);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 55ecc37..1e193f6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -34,7 +34,6 @@
 import com.android.launcher3.anim.AnimatedFloat;
 import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.util.DimensionUtils;
-import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
 import com.android.launcher3.util.TouchController;
 
@@ -108,19 +107,18 @@
 
         if (startAnimation != null) {
             // set taskbar background render animation boolean
-            if (DisplayController.isTransientTaskbar(mActivity)) {
+            if (mActivity.isTransientTaskbar()) {
                 mTaskbarDragLayer.setIsAnimatingTransientTaskbarBackground(true);
             } else {
                 mTaskbarDragLayer.setIsAnimatingPersistentTaskbarBackground(true);
             }
 
-            float desiredValue = DisplayController.isTransientTaskbar(mActivity)
+            float desiredValue = mActivity.isTransientTaskbar()
                     ? PINNING_TRANSIENT
                     : PINNING_PERSISTENT;
 
-            float nonDesiredvalue = !DisplayController.isTransientTaskbar(mActivity)
-                    ? PINNING_TRANSIENT
-                    : PINNING_PERSISTENT;
+            float nonDesiredvalue =
+                    !mActivity.isTransientTaskbar() ? PINNING_TRANSIENT : PINNING_PERSISTENT;
 
             ObjectAnimator objectAnimator = mTaskbarBackgroundProgress.animateToValue(
                     nonDesiredvalue, desiredValue);
@@ -133,9 +131,8 @@
             }));
 
         } else {
-            mTaskbarBackgroundProgress.updateValue(DisplayController.isTransientTaskbar(mActivity)
-                    ? PINNING_TRANSIENT
-                    : PINNING_PERSISTENT);
+            mTaskbarBackgroundProgress.updateValue(
+                    mActivity.isTransientTaskbar() ? PINNING_TRANSIENT : PINNING_PERSISTENT);
         }
 
         mBgTaskbar.value = 1;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
index 5d1288c..7a23006 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
@@ -52,6 +52,8 @@
 import com.android.launcher3.views.BaseDragLayer
 import com.android.quickstep.util.ContextualSearchInvoker
 import com.android.quickstep.util.LottieAnimationColorUtils
+import com.android.wm.shell.shared.TypefaceUtils
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily
 import java.io.PrintWriter
 
 /** First EDU step for swiping up to show transient Taskbar. */
@@ -153,7 +155,7 @@
     fun maybeShowSwipeEdu() {
         if (
             !isTooltipEnabled ||
-                !DisplayController.isTransientTaskbar(activityContext) ||
+                !activityContext.isTransientTaskbar ||
                 tooltipStep > TOOLTIP_STEP_SWIPE
         ) {
             return
@@ -164,7 +166,7 @@
         tooltip?.run {
             TypefaceUtils.setTypeface(
                 requireViewById(R.id.taskbar_edu_title),
-                TypefaceUtils.FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED,
+                FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED,
             )
             val swipeAnimation = requireViewById<LottieAnimationView>(R.id.swipe_animation)
             swipeAnimation.supportLightTheme()
@@ -198,7 +200,7 @@
             suggestionsAnim.supportLightTheme()
             pinningAnim.supportLightTheme()
             handleEduAnimations(listOf(splitscreenAnim, suggestionsAnim, pinningAnim))
-            if (DisplayController.isTransientTaskbar(activityContext)) {
+            if (activityContext.isTransientTaskbar) {
                 splitscreenAnim.setAnimation(R.raw.taskbar_edu_splitscreen_transient)
                 suggestionsAnim.setAnimation(R.raw.taskbar_edu_suggestions_transient)
                 pinningEdu.visibility = if (enableTaskbarPinning()) VISIBLE else GONE
@@ -210,25 +212,25 @@
 
             TypefaceUtils.setTypeface(
                 requireViewById(R.id.taskbar_edu_title),
-                TypefaceUtils.FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED,
+                FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED,
             )
             TypefaceUtils.setTypeface(
                 requireViewById(R.id.splitscreen_text),
-                TypefaceUtils.FONT_FAMILY_BODY_MEDIUM_BASELINE,
+                FontFamily.GSF_BODY_MEDIUM,
             )
             TypefaceUtils.setTypeface(
                 requireViewById(R.id.suggestions_text),
-                TypefaceUtils.FONT_FAMILY_BODY_MEDIUM_BASELINE,
+                FontFamily.GSF_BODY_MEDIUM,
             )
             TypefaceUtils.setTypeface(
                 requireViewById(R.id.pinning_text),
-                TypefaceUtils.FONT_FAMILY_BODY_MEDIUM_BASELINE,
+                FontFamily.GSF_BODY_MEDIUM,
             )
 
             // Set up layout parameters.
             content.updateLayoutParams { width = MATCH_PARENT }
             updateLayoutParams<MarginLayoutParams> {
-                if (DisplayController.isTransientTaskbar(activityContext)) {
+                if (activityContext.isTransientTaskbar) {
                     width =
                         resources.getDimensionPixelSize(
                             if (enableTaskbarPinning())
@@ -261,7 +263,7 @@
         // for the original 2 edu steps) as a proxy to needing to show the separate pinning edu
         if (
             !enableTaskbarPinning() ||
-                !DisplayController.isTransientTaskbar(activityContext) ||
+                !activityContext.isTransientTaskbar ||
                 !isTooltipEnabled ||
                 tooltipStep > TOOLTIP_STEP_PINNING ||
                 tooltipStep < TOOLTIP_STEP_FEATURES
@@ -275,11 +277,11 @@
             allowTouchDismissal = true
             TypefaceUtils.setTypeface(
                 requireViewById(R.id.taskbar_edu_title),
-                TypefaceUtils.FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED,
+                FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED,
             )
             TypefaceUtils.setTypeface(
                 requireViewById(R.id.pinning_text),
-                TypefaceUtils.FONT_FAMILY_BODY_MEDIUM_BASELINE,
+                FontFamily.GSF_BODY_MEDIUM,
             )
 
             val pinningAnim =
@@ -287,7 +289,7 @@
             pinningAnim.supportLightTheme()
             handleEduAnimations(listOf(pinningAnim))
             updateLayoutParams<BaseDragLayer.LayoutParams> {
-                if (DisplayController.isTransientTaskbar(activityContext)) {
+                if (activityContext.isTransientTaskbar) {
                     bottomMargin += activityContext.deviceProfile.taskbarHeight
                 }
                 // Unlike other tooltips, we want to align with taskbar divider rather than center.
@@ -336,13 +338,13 @@
 
             TypefaceUtils.setTypeface(
                 requireViewById(R.id.taskbar_edu_title),
-                TypefaceUtils.FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED,
+                FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED,
             )
-            TypefaceUtils.setTypeface(eduSubtitle, TypefaceUtils.FONT_FAMILY_BODY_SMALL_BASELINE)
+            TypefaceUtils.setTypeface(eduSubtitle, FontFamily.GSF_BODY_SMALL)
 
             showDisclosureText(eduSubtitle)
             updateLayoutParams<BaseDragLayer.LayoutParams> {
-                if (DisplayController.isTransientTaskbar(activityContext)) {
+                if (activityContext.isTransientTaskbar) {
                     bottomMargin += activityContext.deviceProfile.taskbarHeight
                 }
                 // Unlike other tooltips, we want to align with the all apps button rather than
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index a8ce10f..3af2ab6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -235,8 +235,7 @@
                         context.resources,
                     )
                 val isPinnedTaskbar =
-                    context.deviceProfile.isTaskbarPresent &&
-                        !context.deviceProfile.isTransientTaskbar
+                    context.deviceProfile.isTaskbarPresent && !context.isTransientTaskbar
                 val mandatoryGestureHeight = if (isPinnedTaskbar) contentHeight else gestureHeight
                 provider.insetsSize =
                     getInsetsForGravityWithCutout(mandatoryGestureHeight, gravity, endRotation)
@@ -388,10 +387,7 @@
                 bubbleBarVisible
         ) {
             // Taskbar has some touchable elements, take over the full taskbar area
-            if (
-                controllers.uiController.isInOverviewUi &&
-                    DisplayController.isTransientTaskbar(context)
-            ) {
+            if (controllers.uiController.isInOverviewUi && context.isTransientTaskbar) {
                 val region =
                     controllers.taskbarActivityContext.dragLayer.lastDrawnTransientRect.toRegion()
                 val bubbleBarBounds =
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 10eb64a..b510e7e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -191,6 +191,8 @@
 
     private boolean mIsQsbInline;
 
+    private RecentsAnimationCallbacks mRecentsAnimationCallbacks;
+
     private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
             new DeviceProfile.OnDeviceProfileChangeListener() {
                 @Override
@@ -295,6 +297,11 @@
         mIsDestroyed = true;
         mCanSyncViews = false;
 
+        if (mRecentsAnimationCallbacks != null) {
+            mRecentsAnimationCallbacks.removeListener(mTaskBarRecentsAnimationListener);
+            mRecentsAnimationCallbacks = null;
+        }
+
         mIconAlignment.finishAnimation();
 
         mLauncher.getHotseat().setIconsAlpha(1f, ALPHA_CHANNEL_TASKBAR_ALIGNMENT);
@@ -315,6 +322,7 @@
         // If going to overview, stash the task bar
         // If going home, align the icons to hotseat
         AnimatorSet animatorSet = new AnimatorSet();
+        mRecentsAnimationCallbacks = callbacks;
 
         // Update stashed flags first to ensure goingToUnstashedLauncherState() returns correctly.
         TaskbarStashController stashController = mControllers.taskbarStashController;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 5155ffc..e8e5b30 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -15,20 +15,22 @@
  */
 package com.android.launcher3.taskbar;
 
+import static android.content.Context.RECEIVER_EXPORTED;
 import static android.content.Context.RECEIVER_NOT_EXPORTED;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 
 import static com.android.launcher3.BaseActivity.EVENT_DESTROYED;
+import static com.android.launcher3.Flags.enableGrowthNudge;
 import static com.android.launcher3.Flags.enableUnfoldStateAnimation;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
 import static com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate;
+import static com.android.launcher3.taskbar.growth.GrowthConstants.BROADCAST_SHOW_NUDGE;
 import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
 import static com.android.launcher3.util.DisplayController.CHANGE_DESKTOP_MODE;
 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
 import static com.android.launcher3.util.DisplayController.CHANGE_SHOW_LOCKED_TASKBAR;
 import static com.android.launcher3.util.DisplayController.CHANGE_TASKBAR_PINNING;
-import static com.android.launcher3.util.DisplayController.TASKBAR_NOT_DESTROYED_TAG;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
 import static com.android.quickstep.util.SystemActionConstants.ACTION_SHOW_TASKBAR;
@@ -62,6 +64,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.anim.AnimatorListeners;
@@ -78,6 +81,7 @@
 import com.android.quickstep.RecentsActivity;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
 import com.android.quickstep.fallback.window.RecentsWindowManager;
 import com.android.quickstep.util.ContextualSearchInvoker;
 import com.android.quickstep.util.GroupTask;
@@ -102,11 +106,11 @@
 public class TaskbarManager {
     private static final String TAG = "TaskbarManager";
     private static final boolean DEBUG = false;
-    // TODO(b/382378283) remove all logs with this tag
-    public static final String NULL_TASKBAR_ROOT_LAYOUT_TAG = "b/382378283";
-    public static final String ILLEGAL_ARGUMENT_WM_ADD_VIEW = "b/391653300";
     private static final int TASKBAR_DESTROY_DURATION = 100;
 
+    // TODO: b/397738606  - Remove all logs with this tag after the growth framework is integrated.
+    public static final String GROWTH_FRAMEWORK_TAG = "Growth Framework";
+
     /**
      * All the configurations which do not initiate taskbar recreation.
      * This includes all the configurations defined in Launcher's manifest entry and
@@ -130,10 +134,10 @@
             Settings.Secure.NAV_BAR_KIDS_MODE);
 
     private final Context mBaseContext;
-    private TaskbarNavButtonCallbacks mNavCallbacks;
+    private final TaskbarNavButtonCallbacks mNavCallbacks;
     // TODO: Remove this during the connected displays lifecycle refactor.
     private final Context mPrimaryWindowContext;
-    private WindowManager mPrimaryWindowManager;
+    private final WindowManager mPrimaryWindowManager;
     private TaskbarNavButtonController mPrimaryNavButtonController;
     private ComponentCallbacks mPrimaryComponentCallbacks;
 
@@ -158,6 +162,8 @@
             new SparseArray<>();
     /** DisplayId - {@link ComponentCallbacks} map for Connected Display. */
     private final SparseArray<ComponentCallbacks> mComponentCallbacks = new SparseArray<>();
+    /** DisplayId - {@link DeviceProfile} map for Connected Display. */
+    private final SparseArray<DeviceProfile> mExternalDeviceProfiles = new SparseArray<>();
     private StatefulActivity mActivity;
     private RecentsViewContainer mRecentsViewContainer;
 
@@ -173,52 +179,59 @@
      */
     private final RecreationListener mRecreationListener = new RecreationListener();
 
+    // Currently, there is a duplicative call to recreate taskbars when user enter/exit Desktop
+    // Mode upon getting transition callback from shell side. So, we make sure that if taskbar is
+    // already in recreate process due to transition callback, don't recreate for
+    // DisplayInfoChangeListener.
+    private boolean mShouldIgnoreNextDesktopModeChangeFromDisplayController = false;
+
     private class RecreationListener implements DisplayController.DisplayInfoChangeListener {
         @Override
         public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) {
             if ((flags & CHANGE_DENSITY) != 0) {
-                debugTaskbarManager("onDisplayInfoChanged - Display density changed",
+                debugTaskbarManager("onDisplayInfoChanged: Display density changed",
                         context.getDisplayId());
             }
             if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
-                debugTaskbarManager("onDisplayInfoChanged - Navigation mode changed",
+                debugTaskbarManager("onDisplayInfoChanged: Navigation mode changed",
                         context.getDisplayId());
             }
             if ((flags & CHANGE_DESKTOP_MODE) != 0) {
-                debugTaskbarManager("onDisplayInfoChanged - Desktop mode changed",
+                debugTaskbarManager("onDisplayInfoChanged: Desktop mode changed",
                         context.getDisplayId());
             }
             if ((flags & CHANGE_TASKBAR_PINNING) != 0) {
-                debugTaskbarManager("onDisplayInfoChanged - Taskbar pinning changed",
+                debugTaskbarManager("onDisplayInfoChanged: Taskbar pinning changed",
                         context.getDisplayId());
             }
 
             if ((flags & (CHANGE_DENSITY | CHANGE_NAVIGATION_MODE | CHANGE_DESKTOP_MODE
                     | CHANGE_TASKBAR_PINNING | CHANGE_SHOW_LOCKED_TASKBAR)) != 0) {
-                debugTaskbarManager("onDisplayInfoChanged - Recreating Taskbar!",
+                debugTaskbarManager("onDisplayInfoChanged: Recreating Taskbar!",
                         context.getDisplayId());
                 TaskbarActivityContext taskbarActivityContext = getCurrentActivityContext();
                 if ((flags & CHANGE_SHOW_LOCKED_TASKBAR) != 0) {
-                    recreateTaskbar();
+                    recreateTaskbars();
                 } else if ((flags & CHANGE_DESKTOP_MODE) != 0) {
+                    if (mShouldIgnoreNextDesktopModeChangeFromDisplayController) {
+                        mShouldIgnoreNextDesktopModeChangeFromDisplayController = false;
+                        return;
+                    }
                     // Only Handles Special Exit Cases for Desktop Mode Taskbar Recreation.
                     if (taskbarActivityContext != null
-                            && !DesktopVisibilityController.INSTANCE.get(taskbarActivityContext)
-                            .isInDesktopMode()
                             && !DisplayController.showLockedTaskbarOnHome(context)) {
-                        recreateTaskbar();
+                        recreateTaskbars();
                     }
                 } else {
-                    recreateTaskbar();
+                    recreateTaskbars();
                 }
-
             }
         }
     }
 
     private final SettingsCache.OnChangeListener mOnSettingsChangeListener = c -> {
-        debugTaskbarManager("Settings changed! Recreating Taskbar!");
-        recreateTaskbar();
+        debugPrimaryTaskbar("Settings changed! Recreating Taskbar!");
+        recreateTaskbars();
     };
 
     private final PerceptibleTaskListener mTaskStackListener;
@@ -228,26 +241,23 @@
 
         @Override
         public void onTaskMovedToFront(int taskId) {
-            if (mPerceptibleTasks.contains(taskId)) {
-                return;
-            }
-
             // This listens to any Task, so we filter them by the ones shown in the launcher.
             // For Tasks restored after startup, they will by default not be Perceptible, and no
             // need to until user interacts with it by bringing it to the foreground.
             for (int i = 0; i < mTaskbars.size(); i++) {
-                // get pinned tasks
+                // get pinned tasks - we care about all tasks, not just the one moved to the front
                 Set<Integer> taskbarPinnedTasks =
                         mTaskbars.valueAt(i).getControllers().taskbarViewController
                                 .getTaskIdsForPinnedApps();
 
-                // mark as perceptible if the foregrounded task is in the list of apps shown in
-                // the launcher.
-                if (taskbarPinnedTasks.contains(taskId)
-                        && ActivityManagerWrapper.getInstance()
-                        .setTaskIsPerceptible(taskId, true)
-                ) {
-                    mPerceptibleTasks.add(taskId);
+                // filter out tasks already marked as perceptible
+                taskbarPinnedTasks.removeAll(mPerceptibleTasks);
+
+                // add the filtered tasks as perceptible
+                for (int pinnedTaskId : taskbarPinnedTasks) {
+                    ActivityManagerWrapper.getInstance()
+                            .setTaskIsPerceptible(pinnedTaskId, true);
+                    mPerceptibleTasks.add(pinnedTaskId);
                 }
             }
         }
@@ -280,8 +290,6 @@
         }
     }
 
-    ;
-
     private final DesktopVisibilityController.TaskbarDesktopModeListener
             mTaskbarDesktopModeListener =
             new DesktopVisibilityController.TaskbarDesktopModeListener() {
@@ -293,6 +301,7 @@
                                 displayId);
                         if (taskbarActivityContext != null
                                 && !taskbarActivityContext.isInOverview()) {
+                            mShouldIgnoreNextDesktopModeChangeFromDisplayController = true;
                             AnimatorSet animatorSet = taskbarActivityContext.onDestroyAnimation(
                                     TASKBAR_DESTROY_DURATION);
                             animatorSet.addListener(AnimatorListeners.forEndCallback(
@@ -309,11 +318,15 @@
                         int displayId = mTaskbars.keyAt(taskbarIndex);
                         TaskbarActivityContext taskbarActivityContext = getTaskbarForDisplay(
                                 displayId);
-                        AnimatorSet animatorSet = taskbarActivityContext.onDestroyAnimation(
-                                TASKBAR_DESTROY_DURATION);
-                        animatorSet.addListener(AnimatorListeners.forEndCallback(
-                                () -> recreateTaskbarForDisplay(getDefaultDisplayId(), duration)));
-                        animatorSet.start();
+                        if (taskbarActivityContext != null) {
+                            mShouldIgnoreNextDesktopModeChangeFromDisplayController = true;
+                            AnimatorSet animatorSet = taskbarActivityContext.onDestroyAnimation(
+                                    TASKBAR_DESTROY_DURATION);
+                            animatorSet.addListener(AnimatorListeners.forEndCallback(
+                                    () -> recreateTaskbarForDisplay(getDefaultDisplayId(),
+                                            duration)));
+                            animatorSet.start();
+                        }
                     }
                 }
 
@@ -329,6 +342,8 @@
 
     private final SimpleBroadcastReceiver mTaskbarBroadcastReceiver;
 
+    private final SimpleBroadcastReceiver mGrowthBroadcastReceiver;
+
     private final AllAppsActionManager mAllAppsActionManager;
     private final RecentsDisplayModel mRecentsDisplayModel;
 
@@ -336,14 +351,12 @@
         @Override
         public void run() {
             int displayId = getDefaultDisplayId();
-            debugTaskbarManager("mActivityOnDestroyCallback running!", displayId);
+            debugTaskbarManager("onActivityDestroyed:", displayId);
             if (mActivity != null) {
                 displayId = mActivity.getDisplayId();
                 mActivity.removeOnDeviceProfileChangeListener(
                         mDebugActivityDeviceProfileChanged);
-                Log.d(TASKBAR_NOT_DESTROYED_TAG,
-                        "unregistering activity lifecycle callbacks from "
-                                + "onActivityDestroyed.");
+                debugTaskbarManager("onActivityDestroyed: unregistering callbacks", displayId);
                 mActivity.removeEventCallback(EVENT_DESTROYED, this);
             }
             if (mActivity == mRecentsViewContainer) {
@@ -352,7 +365,10 @@
             mActivity = null;
             TaskbarActivityContext taskbar = getTaskbarForDisplay(displayId);
             if (taskbar != null) {
+                debugTaskbarManager("onActivityDestroyed: setting taskbarUIController", displayId);
                 taskbar.setUIController(TaskbarUIController.DEFAULT);
+            } else {
+                debugTaskbarManager("onActivityDestroyed: taskbar is null!", displayId);
             }
             mUnfoldProgressProvider.setSourceProvider(null);
         }
@@ -362,26 +378,26 @@
             new UnfoldTransitionProgressProvider.TransitionProgressListener() {
                 @Override
                 public void onTransitionStarted() {
-                    debugTaskbarManager("fold/unfold transition started getting called.");
+                    debugPrimaryTaskbar("fold/unfold transition started getting called.");
                 }
 
                 @Override
                 public void onTransitionProgress(float progress) {
-                    debugTaskbarManager(
+                    debugPrimaryTaskbar(
                             "fold/unfold transition progress getting called. | progress="
                                     + progress);
                 }
 
                 @Override
                 public void onTransitionFinishing() {
-                    debugTaskbarManager(
+                    debugPrimaryTaskbar(
                             "fold/unfold transition finishing getting called.");
 
                 }
 
                 @Override
                 public void onTransitionFinished() {
-                    debugTaskbarManager(
+                    debugPrimaryTaskbar(
                             "fold/unfold transition finished getting called.");
                 }
             };
@@ -399,7 +415,7 @@
 
         // Set up primary display.
         int primaryDisplayId = getDefaultDisplayId();
-        debugTaskbarManager("TaskbarManager constructor", primaryDisplayId);
+        debugPrimaryTaskbar("TaskbarManager constructor");
         mPrimaryWindowContext = createWindowContext(primaryDisplayId);
         mPrimaryWindowManager = mPrimaryWindowContext.getSystemService(WindowManager.class);
         DesktopVisibilityController.INSTANCE.get(
@@ -419,7 +435,18 @@
         mTaskbarBroadcastReceiver =
                 new SimpleBroadcastReceiver(mPrimaryWindowContext,
                         UI_HELPER_EXECUTOR, this::showTaskbarFromBroadcast);
+
         mShutdownReceiver.register(Intent.ACTION_SHUTDOWN);
+        if (enableGrowthNudge()) {
+            // TODO: b/397739323 - Add permission to limit access to Growth Framework.
+            mGrowthBroadcastReceiver =
+                    new SimpleBroadcastReceiver(
+                            mPrimaryWindowContext, UI_HELPER_EXECUTOR, this::showGrowthNudge);
+            mGrowthBroadcastReceiver.register(RECEIVER_EXPORTED,
+                    BROADCAST_SHOW_NUDGE);
+        } else {
+            mGrowthBroadcastReceiver = null;
+        }
         UI_HELPER_EXECUTOR.execute(() -> {
             mSharedState.taskbarSystemActionPendingIntent = PendingIntent.getBroadcast(
                     mPrimaryWindowContext,
@@ -436,30 +463,35 @@
         } else {
             mTaskStackListener = null;
         }
-        debugTaskbarManager("TaskbarManager created");
-        recreateTaskbar();
+        recreateTaskbars();
+        debugPrimaryTaskbar("TaskbarManager created");
     }
 
     private void destroyAllTaskbars() {
+        debugPrimaryTaskbar("destroyAllTaskbars");
         for (int i = 0; i < mTaskbars.size(); i++) {
             int displayId = mTaskbars.keyAt(i);
+            debugTaskbarManager("destroyAllTaskbars: call destroyTaskbarForDisplay", displayId);
             destroyTaskbarForDisplay(displayId);
+
+            debugTaskbarManager("destroyAllTaskbars: call removeTaskbarRootViewFromWindow",
+                    displayId);
             removeTaskbarRootViewFromWindow(displayId);
         }
     }
 
     private void destroyTaskbarForDisplay(int displayId) {
-        Log.d(ILLEGAL_ARGUMENT_WM_ADD_VIEW, "destroyTaskbarForDisplay: " + displayId);
+        debugTaskbarManager("destroyTaskbarForDisplay", displayId);
         TaskbarActivityContext taskbar = getTaskbarForDisplay(displayId);
-        debugTaskbarManager("destroyTaskbarForDisplay: " + taskbar, displayId);
         if (taskbar != null) {
             taskbar.onDestroy();
             // remove all defaults that we store
             removeTaskbarFromMap(displayId);
+        } else {
+            debugTaskbarManager("destroyTaskbarForDisplay: taskbar is NULL!", displayId);
         }
-        // TODO (b/381113004): make this display-specific via getWindowContext()
-        DeviceProfile dp = mUserUnlocked ? LauncherAppState.getIDP(
-                mPrimaryWindowContext).getDeviceProfile(mPrimaryWindowContext) : null;
+
+        DeviceProfile dp = getDeviceProfile(displayId);
         if (dp == null || !isTaskbarEnabled(dp)) {
             removeTaskbarRootViewFromWindow(displayId);
         }
@@ -469,24 +501,35 @@
      * Show Taskbar upon receiving broadcast
      */
     private void showTaskbarFromBroadcast(Intent intent) {
+        debugPrimaryTaskbar("destroyTaskbarForDisplay");
         // TODO: make this code displayId specific
         TaskbarActivityContext taskbar = getTaskbarForDisplay(getDefaultDisplayId());
-        if (ACTION_SHOW_TASKBAR.equals(intent.getAction()) && taskbar != null) {
+        if (ACTION_SHOW_TASKBAR.equals(intent.getAction())) {
             taskbar.showTaskbarFromBroadcast();
         }
     }
 
+    private void showGrowthNudge(Intent intent) {
+        if (!enableGrowthNudge()) {
+            return;
+        }
+        if (BROADCAST_SHOW_NUDGE.equals(intent.getAction())) {
+            // TODO: b/397738606 - extract the details and create a nudge payload.
+            Log.d(GROWTH_FRAMEWORK_TAG, "Intent received");
+        }
+    }
+
     /**
      * Toggles All Apps for Taskbar or Launcher depending on the current state.
      */
-    public void toggleAllApps() {
+    public void toggleAllAppsSearch() {
         TaskbarActivityContext taskbar = getTaskbarForDisplay(getDefaultDisplayId());
-        if (taskbar == null || taskbar.canToggleHomeAllApps()) {
+        if (taskbar == null) {
             // Home All Apps should be toggled from this class, because the controllers are not
             // initialized when Taskbar is disabled (i.e. TaskbarActivityContext is null).
-            if (mActivity instanceof Launcher l) l.toggleAllAppsSearch();
+            if (mActivity instanceof Launcher l) l.toggleAllApps(true);
         } else {
-            taskbar.toggleAllAppsSearch();
+            taskbar.getControllers().uiController.toggleAllApps(true);
         }
     }
 
@@ -505,12 +548,15 @@
      * Called when the user is unlocked
      */
     public void onUserUnlocked() {
+        debugPrimaryTaskbar("onUserUnlocked");
         mUserUnlocked = true;
         DisplayController.INSTANCE.get(mPrimaryWindowContext).addChangeListener(
                 mRecreationListener);
-        recreateTaskbar();
+        debugPrimaryTaskbar("onUserUnlocked: recreating all taskbars!");
+        recreateTaskbars();
         for (int i = 0; i < mTaskbars.size(); i++) {
             int displayId = mTaskbars.keyAt(i);
+            debugTaskbarManager("onUserUnlocked: addTaskbarRootViewToWindow()", displayId);
             addTaskbarRootViewToWindow(displayId);
         }
     }
@@ -519,15 +565,15 @@
      * Sets a {@link StatefulActivity} to act as taskbar callback
      */
     public void setActivity(@NonNull StatefulActivity activity) {
+        debugPrimaryTaskbar("setActivity: mActivity=" + mActivity);
         if (mActivity == activity) {
+            debugPrimaryTaskbar("setActivity: No need to set activity!");
             return;
         }
         removeActivityCallbacksAndListeners();
         mActivity = activity;
-        debugTaskbarManager("Set mActivity=" + mActivity);
         mActivity.addOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged);
-        Log.d(TASKBAR_NOT_DESTROYED_TAG,
-                "registering activity lifecycle callbacks from setActivity().");
+        debugPrimaryTaskbar("setActivity: registering activity lifecycle callbacks.");
         mActivity.addEventCallback(EVENT_DESTROYED, mActivityOnDestroyCallback);
         UnfoldTransitionProgressProvider unfoldTransitionProgressProvider =
                 getUnfoldTransitionProgressProviderForActivity(activity);
@@ -545,6 +591,7 @@
      * Sets the current RecentsViewContainer, from which we create a TaskbarUIController.
      */
     public void setRecentsViewContainer(@NonNull RecentsViewContainer recentsViewContainer) {
+        debugPrimaryTaskbar("setRecentsViewContainer");
         if (mRecentsViewContainer == recentsViewContainer) {
             return;
         }
@@ -568,6 +615,7 @@
      */
     private UnfoldTransitionProgressProvider getUnfoldTransitionProgressProviderForActivity(
             StatefulActivity activity) {
+        debugPrimaryTaskbar("getUnfoldTransitionProgressProviderForActivity");
         if (!enableUnfoldStateAnimation()) {
             if (activity instanceof QuickstepLauncher ql) {
                 return ql.getUnfoldTransitionProgressProvider();
@@ -580,7 +628,8 @@
 
     /** Creates a {@link TaskbarUIController} to use with non default displays. */
     private TaskbarUIController createTaskbarUIControllerForNonDefaultDisplay(int displayId) {
-        if (RecentsDisplayModel.enableOverviewInWindow()) {
+        debugPrimaryTaskbar("createTaskbarUIControllerForNonDefaultDisplay");
+        if (RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
             RecentsViewContainer rvc = mRecentsDisplayModel.getRecentsWindowManager(displayId);
             if (rvc != null) {
                 return createTaskbarUIControllerForRecentsViewContainer(rvc);
@@ -595,6 +644,7 @@
      */
     private TaskbarUIController createTaskbarUIControllerForRecentsViewContainer(
             RecentsViewContainer container) {
+        debugPrimaryTaskbar("createTaskbarUIControllerForRecentsViewContainer");
         if (container instanceof QuickstepLauncher quickstepLauncher) {
             return new LauncherTaskbarUIController(quickstepLauncher);
         }
@@ -615,15 +665,18 @@
      * In other case (folding/unfolding) we don't need to remove and add window.
      */
     @VisibleForTesting
-    public synchronized void recreateTaskbar() {
+    public synchronized void recreateTaskbars() {
+        debugPrimaryTaskbar("recreateTaskbars");
         // Handles initial creation case.
         if (mTaskbars.size() == 0) {
+            debugTaskbarManager("recreateTaskbars: create primary taskbar", getDefaultDisplayId());
             recreateTaskbarForDisplay(getDefaultDisplayId(), 0);
             return;
         }
 
         for (int i = 0; i < mTaskbars.size(); i++) {
             int displayId = mTaskbars.keyAt(i);
+            debugTaskbarManager("recreateTaskbars: create external taskbar", displayId);
             recreateTaskbarForDisplay(displayId, 0);
         }
     }
@@ -634,17 +687,17 @@
      * In other case (folding/unfolding) we don't need to remove and add window.
      */
     private void recreateTaskbarForDisplay(int displayId, int duration) {
+        debugTaskbarManager("recreateTaskbarForDisplay: ", displayId);
         Trace.beginSection("recreateTaskbarForDisplay");
         try {
-            Log.d(ILLEGAL_ARGUMENT_WM_ADD_VIEW, "recreateTaskbarForDisplay: " + displayId);
+            debugTaskbarManager("recreateTaskbarForDisplay: getting device profile", displayId);
             // TODO (b/381113004): make this display-specific via getWindowContext()
-            DeviceProfile dp = mUserUnlocked ? LauncherAppState.getIDP(
-                    mPrimaryWindowContext).getDeviceProfile(mPrimaryWindowContext) : null;
+            DeviceProfile dp = getDeviceProfile(displayId);
 
             // All Apps action is unrelated to navbar unification, so we only need to check DP.
             final boolean isLargeScreenTaskbar = dp != null && dp.isTaskbarPresent;
             mAllAppsActionManager.setTaskbarPresent(isLargeScreenTaskbar);
-
+            debugTaskbarManager("recreateTaskbarForDisplay: destroying taskbar", displayId);
             destroyTaskbarForDisplay(displayId);
 
             boolean displayExists = getDisplay(displayId) != null;
@@ -653,17 +706,22 @@
                     + " [dp != null (i.e. mUserUnlocked)]=" + (dp != null)
                     + " FLAG_HIDE_NAVBAR_WINDOW=" + ENABLE_TASKBAR_NAVBAR_UNIFICATION
                     + " dp.isTaskbarPresent=" + (dp == null ? "null" : dp.isTaskbarPresent)
-                    + " displayExists=" + displayExists);
+                    + " displayExists=" + displayExists, displayId);
             if (!isTaskbarEnabled || !isLargeScreenTaskbar || !displayExists) {
                 SystemUiProxy.INSTANCE.get(mBaseContext)
                         .notifyTaskbarStatus(/* visible */ false, /* stashed */ false);
                 if (!isTaskbarEnabled || !displayExists) {
+                    debugTaskbarManager(
+                            "recreateTaskbarForDisplay: exiting bc (!isTaskbarEnabled || "
+                                    + "!displayExists)",
+                            displayId);
                     return;
                 }
             }
 
             TaskbarActivityContext taskbar = getTaskbarForDisplay(displayId);
             if (enableTaskbarNoRecreate() || taskbar == null) {
+                debugTaskbarManager("recreateTaskbarForDisplay: creating taskbar", displayId);
                 taskbar = createTaskbarActivityContext(dp, displayId);
                 if (taskbar == null) {
                     debugTaskbarManager(
@@ -671,10 +729,11 @@
                     return;
                 }
             } else {
+                debugTaskbarManager("recreateTaskbarForDisplay: updating taskbar device profile",
+                        displayId);
                 taskbar.updateDeviceProfile(dp);
             }
-            mSharedState.startTaskbarVariantIsTransient =
-                    DisplayController.isTransientTaskbar(taskbar);
+            mSharedState.startTaskbarVariantIsTransient = taskbar.isTransientTaskbar();
             mSharedState.allAppsVisible = mSharedState.allAppsVisible && isLargeScreenTaskbar;
             taskbar.init(mSharedState, duration);
 
@@ -689,15 +748,17 @@
             }
 
             if (enableTaskbarNoRecreate()) {
+                debugTaskbarManager("recreateTaskbarForDisplay: adding rootView", displayId);
                 addTaskbarRootViewToWindow(displayId);
                 FrameLayout taskbarRootLayout = getTaskbarRootLayoutForDisplay(displayId);
                 if (taskbarRootLayout != null) {
+                    debugTaskbarManager("recreateTaskbarForDisplay: adding root layout", displayId);
                     taskbarRootLayout.removeAllViews();
                     taskbarRootLayout.addView(taskbar.getDragLayer());
                     taskbar.notifyUpdateLayoutParams();
                 } else {
-                    Log.e(NULL_TASKBAR_ROOT_LAYOUT_TAG,
-                            "taskbarRootLayout is null for displayId=" + displayId);
+                    debugTaskbarManager("recreateTaskbarForDisplay: taskbarRootLayout is null!",
+                            displayId);
                 }
             }
         } finally {
@@ -705,13 +766,14 @@
         }
     }
 
-    public void onSystemUiFlagsChanged(@SystemUiStateFlags long systemUiStateFlags) {
+    /** Called when the SysUI flags for a given display change. */
+    public void onSystemUiFlagsChanged(@SystemUiStateFlags long systemUiStateFlags, int displayId) {
         if (DEBUG) {
             Log.d(TAG, "SysUI flags changed: " + formatFlagChange(systemUiStateFlags,
                     mSharedState.sysuiStateFlags, QuickStepContract::getSystemUiStateString));
         }
         mSharedState.sysuiStateFlags = systemUiStateFlags;
-        TaskbarActivityContext taskbar = getTaskbarForDisplay(getDefaultDisplayId());
+        TaskbarActivityContext taskbar = getTaskbarForDisplay(displayId);
         if (taskbar != null) {
             taskbar.updateSysuiStateFlags(systemUiStateFlags, false /* fromInit */);
         }
@@ -768,7 +830,7 @@
     }
 
     public void transitionTo(int displayId, @BarTransitions.TransitionMode int barMode,
-            boolean animate) {
+                             boolean animate) {
         TaskbarActivityContext taskbar = getTaskbarForDisplay(displayId);
         if (taskbar != null) {
             taskbar.transitionTo(barMode, animate);
@@ -842,26 +904,62 @@
      * primary device or a previously mirroring display is switched to extended mode.
      */
     public void onDisplayAddSystemDecorations(int displayId) {
+        debugTaskbarManager("onDisplayAddSystemDecorations: ", displayId);
         Display display = getDisplay(displayId);
-        if (!DesktopExperienceFlags.ENABLE_TASKBAR_CONNECTED_DISPLAYS.isTrue() || isDefaultDisplay(
-                displayId) || display == null) {
-            debugTaskbarManager("onDisplayAddSystemDecorations: not adding display");
+        if (display == null) {
+            debugTaskbarManager("onDisplayAddSystemDecorations: can't find display!", displayId);
             return;
         }
 
+        if (!DesktopExperienceFlags.ENABLE_TASKBAR_CONNECTED_DISPLAYS.isTrue() || isDefaultDisplay(
+                displayId)) {
+            debugTaskbarManager(
+                    "onDisplayAddSystemDecorations: not an external display! | "
+                            + "ENABLE_TASKBAR_CONNECTED_DISPLAYS="
+                            + DesktopExperienceFlags.ENABLE_TASKBAR_CONNECTED_DISPLAYS.isTrue()
+                            + " isDefaultDisplay=" + isDefaultDisplay(displayId), displayId);
+            return;
+        }
+        debugTaskbarManager("onDisplayAddSystemDecorations: creating new windowContext!",
+                displayId);
         Context newWindowContext = createWindowContext(displayId);
         if (newWindowContext != null) {
+            debugTaskbarManager("onDisplayAddSystemDecorations: add new windowContext to map!",
+                    displayId);
             addWindowContextToMap(displayId, newWindowContext);
-            // TODO (b/391965805): remove once onDisplayAddSystemDecorations is working.
             WindowManager wm = getWindowManager(displayId);
             if (wm == null || !wm.shouldShowSystemDecors(displayId)) {
+                String wmStatus = wm == null ? "WindowManager is null!" : "WindowManager exists";
+                boolean showDecor = wm != null && wm.shouldShowSystemDecors(displayId);
+                debugTaskbarManager(
+                        "onDisplayAddSystemDecorations:\n\t" + wmStatus + "\n\tshowSystemDecors="
+                                + showDecor, displayId);
                 return;
             }
+            debugTaskbarManager("onDisplayAddSystemDecorations: creating RootLayout!", displayId);
+
+            createExternalDeviceProfile(displayId);
+
+            debugTaskbarManager("onDisplayAddSystemDecorations: creating RootLayout!", displayId);
             createTaskbarRootLayout(displayId);
+
+            debugTaskbarManager("onDisplayAddSystemDecorations: creating NavButtonController!",
+                    displayId);
             createNavButtonController(displayId);
+
+            debugTaskbarManager(
+                    "onDisplayAddSystemDecorations: createAndRegisterComponentCallbacks!",
+                    displayId);
             createAndRegisterComponentCallbacks(displayId);
+            debugTaskbarManager("onDisplayAddSystemDecorations: recreateTaskbarForDisplay!",
+                    displayId);
             recreateTaskbarForDisplay(displayId, 0);
+        } else {
+            debugTaskbarManager("onDisplayAddSystemDecorations: newWindowContext is NULL!",
+                    displayId);
         }
+
+        debugTaskbarManager("onDisplayAddSystemDecorations: finished!", displayId);
     }
 
     /**
@@ -869,17 +967,38 @@
      * removed from the primary device.
      */
     public void onDisplayRemoved(int displayId) {
+        debugTaskbarManager("onDisplayRemoved: ", displayId);
         if (!DesktopExperienceFlags.ENABLE_TASKBAR_CONNECTED_DISPLAYS.isTrue() || isDefaultDisplay(
                 displayId)) {
+            debugTaskbarManager(
+                    "onDisplayRemoved: not an external display! | "
+                            + "ENABLE_TASKBAR_CONNECTED_DISPLAYS="
+                            + DesktopExperienceFlags.ENABLE_TASKBAR_CONNECTED_DISPLAYS.isTrue()
+                            + " isDefaultDisplay=" + isDefaultDisplay(displayId), displayId);
             return;
         }
 
         Context windowContext = getWindowContext(displayId);
         if (windowContext != null) {
+            debugTaskbarManager("onDisplayRemoved: removing NavButtonController!", displayId);
             removeNavButtonController(displayId);
+
+            debugTaskbarManager("onDisplayRemoved: removeAndUnregisterComponentCallbacks!",
+                    displayId);
             removeAndUnregisterComponentCallbacks(displayId);
+
+            debugTaskbarManager("onDisplayRemoved: destroying Taskbar!", displayId);
             destroyTaskbarForDisplay(displayId);
+
+            debugTaskbarManager("onDisplayRemoved: removing DeviceProfile from map!", displayId);
+            removeDeviceProfileFromMap(displayId);
+
+            debugTaskbarManager("onDisplayRemoved: removing WindowContext from map!", displayId);
             removeWindowContextFromMap(displayId);
+
+            debugTaskbarManager("onDisplayRemoved: finished!", displayId);
+        } else {
+            debugTaskbarManager("onDisplayRemoved: removing NavButtonController!", displayId);
         }
     }
 
@@ -895,9 +1014,7 @@
     private void removeActivityCallbacksAndListeners() {
         if (mActivity != null) {
             mActivity.removeOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged);
-            Log.d(TASKBAR_NOT_DESTROYED_TAG,
-                    "unregistering activity lifecycle callbacks from "
-                            + "removeActivityCallbackAndListeners().");
+            debugPrimaryTaskbar("unregistering activity lifecycle callbacks");
             mActivity.removeEventCallback(EVENT_DESTROYED, mActivityOnDestroyCallback);
             UnfoldTransitionProgressProvider unfoldTransitionProgressProvider =
                     getUnfoldTransitionProgressProviderForActivity(mActivity);
@@ -911,13 +1028,17 @@
      * Called when the manager is no longer needed
      */
     public void destroy() {
+        debugPrimaryTaskbar("TaskbarManager#destroy()");
         mRecentsViewContainer = null;
-        debugTaskbarManager("TaskbarManager#destroy()");
+        debugPrimaryTaskbar("destroy: removing activity callbacks");
         DesktopVisibilityController.INSTANCE.get(
                 mPrimaryWindowContext).unregisterTaskbarDesktopModeListener(
                 mTaskbarDesktopModeListener);
         removeActivityCallbacksAndListeners();
         mTaskbarBroadcastReceiver.unregisterReceiverSafely();
+        if (mGrowthBroadcastReceiver != null) {
+            mGrowthBroadcastReceiver.unregisterReceiverSafely();
+        }
 
         if (mUserUnlocked) {
             DisplayController.INSTANCE.get(mPrimaryWindowContext).removeChangeListener(
@@ -927,7 +1048,7 @@
                 .unregister(USER_SETUP_COMPLETE_URI, mOnSettingsChangeListener);
         SettingsCache.INSTANCE.get(mPrimaryWindowContext)
                 .unregister(NAV_BAR_KIDS_MODE, mOnSettingsChangeListener);
-        Log.d(TASKBAR_NOT_DESTROYED_TAG, "unregistering component callbacks from destroy().");
+        debugPrimaryTaskbar("destroy: unregistering component callbacks");
         removeAndUnregisterComponentCallbacks(getDefaultDisplayId());
         mShutdownReceiver.unregisterReceiverSafely();
         if (ActivityManagerWrapper.usePerceptibleTasks(getPrimaryWindowContext())) {
@@ -936,7 +1057,9 @@
             }
         }
         TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
+        debugPrimaryTaskbar("destroy: destroying all taskbars!");
         destroyAllTaskbars();
+        debugPrimaryTaskbar("destroy: finished!");
     }
 
     public @Nullable TaskbarActivityContext getCurrentActivityContext() {
@@ -959,14 +1082,15 @@
     }
 
     private void addTaskbarRootViewToWindow(int displayId) {
+        debugTaskbarManager("addTaskbarRootViewToWindow:", displayId);
         TaskbarActivityContext taskbar = getTaskbarForDisplay(displayId);
         if (!enableTaskbarNoRecreate() || taskbar == null) {
-            debugTaskbarManager("addTaskbarRootViewToWindow - taskbar null", displayId);
+            debugTaskbarManager("addTaskbarRootViewToWindow: taskbar null", displayId);
             return;
         }
 
         if (getDisplay(displayId) == null) {
-            debugTaskbarManager("addTaskbarRootViewToWindow - display null", displayId);
+            debugTaskbarManager("addTaskbarRootViewToWindow: display null", displayId);
             return;
         }
 
@@ -977,18 +1101,21 @@
                 windowManager.addView(rootLayout, taskbar.getWindowLayoutParams());
                 mAddedRootLayouts.put(displayId, true);
             } else {
-                Log.d(ILLEGAL_ARGUMENT_WM_ADD_VIEW,
-                        "addTaskbarRootViewToWindow - root layout null | displayId=" + displayId);
+                String rootLayoutStatus =
+                        (rootLayout == null) ? "rootLayout is NULL!" : "rootLayout exists!";
+                String wmStatus = (windowManager == null) ? "windowManager is NULL!"
+                        : "windowManager exists!";
+                debugTaskbarManager(
+                        "addTaskbarRootViewToWindow: \n\t" + rootLayoutStatus + "\n\t" + wmStatus,
+                        displayId);
             }
         } else {
-            Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG,
-                    "addTaskbarRootViewToWindow - root layout already added | displayId="
-                            + displayId);
+            debugTaskbarManager("addTaskbarRootViewToWindow: rootLayout already added!", displayId);
         }
     }
 
     private void removeTaskbarRootViewFromWindow(int displayId) {
-        Log.d(ILLEGAL_ARGUMENT_WM_ADD_VIEW, "removeTaskbarRootViewFromWindow: " + displayId);
+        debugTaskbarManager("removeTaskbarRootViewFromWindow", displayId);
         FrameLayout rootLayout = getTaskbarRootLayoutForDisplay(displayId);
         if (!enableTaskbarNoRecreate() || rootLayout == null) {
             return;
@@ -1000,7 +1127,7 @@
             mAddedRootLayouts.put(displayId, false);
             removeTaskbarRootLayoutFromMap(displayId);
         } else {
-            debugTaskbarManager("removeTaskbarRootViewFromWindow - WindowManager is null",
+            debugTaskbarManager("removeTaskbarRootViewFromWindow: WindowManager is null",
                     displayId);
         }
     }
@@ -1078,12 +1205,68 @@
     }
 
     /**
+     * Creates a {@link DeviceProfile} for the given display and adds it to the map.
+     * @param displayId The ID of the display.
+     */
+    private void createExternalDeviceProfile(int displayId) {
+        if (!mUserUnlocked) {
+            return;
+        }
+
+        InvariantDeviceProfile idp = LauncherAppState.getIDP(mPrimaryWindowContext);
+        if (idp == null) {
+            return;
+        }
+
+        Context displayContext = getWindowContext(displayId);
+        if (displayContext == null) {
+            return;
+        }
+
+        DeviceProfile externalDeviceProfile = idp.createDeviceProfileForSecondaryDisplay(
+                displayContext);
+        mExternalDeviceProfiles.put(displayId, externalDeviceProfile);
+    }
+
+    /**
+     * Gets a {@link DeviceProfile} for the given displayId.
+     * @param displayId The ID of the display.
+     */
+    private @Nullable DeviceProfile getDeviceProfile(int displayId) {
+        if (!mUserUnlocked) {
+            return null;
+        }
+
+        InvariantDeviceProfile idp = LauncherAppState.getIDP(mPrimaryWindowContext);
+        if (idp == null) {
+            return null;
+        }
+
+        boolean isPrimary = isDefaultDisplay(displayId)
+                || !DesktopExperienceFlags.ENABLE_TASKBAR_CONNECTED_DISPLAYS.isTrue();
+        if (isPrimary) {
+            return idp.getDeviceProfile(mPrimaryWindowContext);
+        }
+
+        return mExternalDeviceProfiles.get(displayId);
+    }
+
+    /**
+     * Removes the {@link DeviceProfile} associated with the given display ID from the map.
+     * @param displayId The ID of the display for which to remove the taskbar.
+     */
+    private void removeDeviceProfileFromMap(int displayId) {
+        mExternalDeviceProfiles.delete(displayId);
+    }
+
+    /**
      * Create {@link ComponentCallbacks} for the given display and register it to the relevant
      * WindowContext. For external displays, populate maps.
      *
      * @param displayId The ID of the display.
      */
     private void createAndRegisterComponentCallbacks(int displayId) {
+        debugTaskbarManager("createAndRegisterComponentCallbacks", displayId);
         ComponentCallbacks callbacks = new ComponentCallbacks() {
             private Configuration mOldConfig =
                     getWindowContext(displayId).getResources().getConfiguration();
@@ -1092,15 +1275,13 @@
             public void onConfigurationChanged(Configuration newConfig) {
                 Trace.instantForTrack(Trace.TRACE_TAG_APP, "TaskbarManager",
                         "onConfigurationChanged: " + newConfig);
-                debugTaskbarManager(
-                        "TaskbarManager#mComponentCallbacks.onConfigurationChanged: " + newConfig);
-                // TODO (b/381113004): make this display-specific via getWindowContext()
-                DeviceProfile dp = mUserUnlocked ? LauncherAppState.getIDP(
-                        mPrimaryWindowContext).getDeviceProfile(mPrimaryWindowContext) : null;
+                debugTaskbarManager("onConfigurationChanged: " + newConfig, displayId);
+
+                DeviceProfile dp = getDeviceProfile(displayId);
                 int configDiff = mOldConfig.diff(newConfig) & ~SKIP_RECREATE_CONFIG_CHANGES;
 
                 if ((configDiff & ActivityInfo.CONFIG_UI_MODE) != 0) {
-                    Log.d(ILLEGAL_ARGUMENT_WM_ADD_VIEW, "onConfigurationChanged: theme changed");
+                    debugTaskbarManager("onConfigurationChanged: theme changed", displayId);
                     // Only recreate for theme changes, not other UI mode changes such as docking.
                     int oldUiNightMode = (mOldConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK);
                     int newUiNightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK);
@@ -1109,27 +1290,36 @@
                     }
                 }
 
-                debugTaskbarManager("ComponentCallbacks#onConfigurationChanged() "
-                        + "configDiff=" + Configuration.configurationDiffToString(configDiff));
+                debugTaskbarManager("onConfigurationChanged: | configDiff="
+                        + Configuration.configurationDiffToString(configDiff), displayId);
                 if (configDiff != 0 || getCurrentActivityContext() == null) {
-                    recreateTaskbar();
-                } else {
+                    debugTaskbarManager("onConfigurationChanged: call recreateTaskbars", displayId);
+                    recreateTaskbars();
+                } else if (dp != null) {
                     // Config change might be handled without re-creating the taskbar
-                    if (dp != null && !isTaskbarEnabled(dp)) {
+                    if (!isTaskbarEnabled(dp)) {
+                        debugPrimaryTaskbar(
+                                "onConfigurationChanged: isTaskbarEnabled(dp)=False | "
+                                        + "destroyTaskbarForDisplay");
                         destroyTaskbarForDisplay(getDefaultDisplayId());
                     } else {
-                        if (dp != null && isTaskbarEnabled(dp)) {
-                            if (ENABLE_TASKBAR_NAVBAR_UNIFICATION) {
-                                // Re-initialize for screen size change? Should this be done
-                                // by looking at screen-size change flag in configDiff in the
-                                // block above?
-                                recreateTaskbar();
-                            } else {
-                                getCurrentActivityContext().updateDeviceProfile(dp);
-                            }
+                        debugPrimaryTaskbar("onConfigurationChanged: isTaskbarEnabled(dp)=True");
+                        if (ENABLE_TASKBAR_NAVBAR_UNIFICATION) {
+                            // Re-initialize for screen size change? Should this be done
+                            // by looking at screen-size change flag in configDiff in the
+                            // block above?
+                            debugPrimaryTaskbar("onConfigurationChanged: call recreateTaskbars");
+                            recreateTaskbars();
+                        } else {
+                            debugPrimaryTaskbar(
+                                    "onConfigurationChanged: updateDeviceProfile for current "
+                                            + "taskbar.");
+                            getCurrentActivityContext().updateDeviceProfile(dp);
                         }
-                        getCurrentActivityContext().onConfigurationChanged(configDiff);
                     }
+                } else {
+
+                    getCurrentActivityContext().onConfigurationChanged(configDiff);
                 }
                 mOldConfig = new Configuration(newConfig);
                 // reset taskbar was pinned value, so we don't automatically unstash taskbar upon
@@ -1237,7 +1427,7 @@
      * @param displayId The ID of the display for which to create the taskbar root layout.
      */
     private void createTaskbarRootLayout(int displayId) {
-        Log.d(ILLEGAL_ARGUMENT_WM_ADD_VIEW, "createTaskbarRootLayout: " + displayId);
+        debugTaskbarManager("createTaskbarRootLayout: ", displayId);
         if (!enableTaskbarNoRecreate()) {
             return;
         }
@@ -1245,6 +1435,7 @@
         FrameLayout newTaskbarRootLayout = new FrameLayout(getWindowContext(displayId)) {
             @Override
             public boolean dispatchTouchEvent(MotionEvent ev) {
+                debugTaskbarManager("dispatchTouchEvent: ", displayId);
                 // The motion events can be outside the view bounds of task bar, and hence
                 // manually dispatching them to the drag layer here.
                 TaskbarActivityContext taskbar = getTaskbarForDisplay(displayId);
@@ -1254,8 +1445,9 @@
                 return super.dispatchTouchEvent(ev);
             }
         };
+
+        debugTaskbarManager("createTaskbarRootLayout: adding to map", displayId);
         addTaskbarRootLayoutToMap(displayId, newTaskbarRootLayout);
-        Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG, "created new root layout - displayId=" + displayId);
     }
 
     private boolean isDefaultDisplay(int displayId) {
@@ -1269,13 +1461,12 @@
      * @return The taskbar root layout {@link FrameLayout} for a given display or {@code null}.
      */
     private FrameLayout getTaskbarRootLayoutForDisplay(int displayId) {
-        Log.d(ILLEGAL_ARGUMENT_WM_ADD_VIEW, "getTaskbarRootLayoutForDisplay: " + displayId);
+        debugTaskbarManager("getTaskbarRootLayoutForDisplay:", displayId);
         FrameLayout frameLayout = mRootLayouts.get(displayId);
         if (frameLayout != null) {
             return frameLayout;
         } else {
-            Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG,
-                    "getTaskbarRootLayoutForDisplay == null | displayId=" + displayId);
+            debugTaskbarManager("getTaskbarRootLayoutForDisplay: rootLayout is null!", displayId);
             return null;
         }
     }
@@ -1287,11 +1478,14 @@
      * @param rootLayout The taskbar root layout {@link FrameLayout} to add to the map.
      */
     private void addTaskbarRootLayoutToMap(int displayId, FrameLayout rootLayout) {
+        debugTaskbarManager("addTaskbarRootLayoutToMap: ", displayId);
         if (!mRootLayouts.contains(displayId) && rootLayout != null) {
             mRootLayouts.put(displayId, rootLayout);
         }
 
-        Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG, "mRootLayouts.size()=" + mRootLayouts.size());
+        debugTaskbarManager(
+                "addTaskbarRootLayoutToMap: finished! mRootLayouts.size()=" + mRootLayouts.size(),
+                displayId);
     }
 
     /**
@@ -1300,12 +1494,14 @@
      * @param displayId The ID of the display for which to remove the taskbar root layout.
      */
     private void removeTaskbarRootLayoutFromMap(int displayId) {
+        debugTaskbarManager("removeTaskbarRootLayoutFromMap:", displayId);
         if (mRootLayouts.contains(displayId)) {
             mAddedRootLayouts.delete(displayId);
             mRootLayouts.delete(displayId);
         }
 
-        Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG, "mRootLayouts.size()=" + mRootLayouts.size());
+        debugTaskbarManager("removeTaskbarRootLayoutFromMap: finished! mRootLayouts.size="
+                + mRootLayouts.size(), displayId);
     }
 
     /**
@@ -1314,10 +1510,10 @@
      * @param displayId The ID of the display for which to create the window context.
      */
     private @Nullable Context createWindowContext(int displayId) {
-        debugTaskbarManager("createWindowContext: " + displayId);
+        debugTaskbarManager("createWindowContext: ", displayId);
         Display display = getDisplay(displayId);
         if (display == null) {
-            debugTaskbarManager("createWindowContext: display null", displayId);
+            debugTaskbarManager("createWindowContext: display null!", displayId);
             return null;
         }
 
@@ -1414,31 +1610,73 @@
         return mBaseContext.getDisplayId();
     }
 
-    /** Temp logs for b/254119092. */
-    public void debugTaskbarManager(String debugReason) {
-        debugTaskbarManager(debugReason, getDefaultDisplayId());
-    }
-
-    /** Temp logs for b/254119092. */
+    /**
+     * Logs debug information about the TaskbarManager for primary display.
+     * @param debugReason A string describing the reason for the debug log.
+     * @param displayId The ID of the display for which to log debug information.
+     */
     public void debugTaskbarManager(String debugReason, int displayId) {
         StringJoiner log = new StringJoiner("\n");
         log.add(debugReason + " displayId=" + displayId + " isDefaultDisplay=" + isDefaultDisplay(
                 displayId));
+        Log.d(TAG, log.toString());
+    }
 
+    /**
+     * Logs verbose debug information about the TaskbarManager for primary display.
+     * @param debugReason A string describing the reason for the debug log.
+     * @param displayId The ID of the display for which to log debug information.
+     * @param verbose Indicates whether or not to debug with detail.
+     */
+    public void debugTaskbarManager(String debugReason, int displayId, boolean verbose) {
+        StringJoiner log = new StringJoiner("\n");
+        log.add(debugReason + " displayId=" + displayId + " isDefaultDisplay=" + isDefaultDisplay(
+                displayId));
+        if (verbose) {
+            generateVerboseLogs(log, displayId);
+        }
+        Log.d(TAG, log.toString());
+    }
+
+    /**
+     * Logs debug information about the TaskbarManager for primary display.
+     * @param debugReason A string describing the reason for the debug log.
+     *
+     */
+    public void debugPrimaryTaskbar(String debugReason) {
+        debugTaskbarManager(debugReason, getDefaultDisplayId(), false);
+    }
+
+    /**
+     * Logs debug information about the TaskbarManager for primary display.
+     * @param debugReason A string describing the reason for the debug log.
+     *
+     */
+    public void debugPrimaryTaskbar(String debugReason, boolean verbose) {
+        debugTaskbarManager(debugReason, getDefaultDisplayId(), verbose);
+    }
+
+    /**
+     * Logs verbose debug information about the TaskbarManager for a specific display.
+     */
+    private void generateVerboseLogs(StringJoiner log, int displayId) {
         boolean activityTaskbarPresent = mActivity != null
                 && mActivity.getDeviceProfile().isTaskbarPresent;
         // TODO (b/381113004): make this display-specific via getWindowContext()
         Context windowContext = mPrimaryWindowContext;
         if (windowContext == null) {
-            log.add("window context for displayId" + displayId);
+            log.add("windowContext is null!");
             return;
         }
 
-        boolean contextTaskbarPresent = mUserUnlocked && LauncherAppState.getIDP(windowContext)
-                .getDeviceProfile(windowContext).isTaskbarPresent;
+        boolean contextTaskbarPresent = false;
+        if (mUserUnlocked) {
+            DeviceProfile dp = getDeviceProfile(displayId);
+            contextTaskbarPresent = dp != null && dp.isTaskbarPresent;
+        }
         if (activityTaskbarPresent == contextTaskbarPresent) {
             log.add("mActivity and mWindowContext agree taskbarIsPresent=" + contextTaskbarPresent);
-            Log.d(TASKBAR_NOT_DESTROYED_TAG, log.toString());
+            Log.d(TAG, log.toString());
             return;
         }
 
@@ -1457,16 +1695,14 @@
         log.add("\t\tWindowContext.getResources().getConfiguration()="
                 + windowContext.getResources().getConfiguration());
         if (mUserUnlocked) {
-            log.add("\t\tLauncherAppState.getIDP().getDeviceProfile(mPrimaryWindowContext)"
-                    + ".isTaskbarPresent=" + contextTaskbarPresent);
+            log.add("\t\tgetDeviceProfile(mPrimaryWindowContext).isTaskbarPresent="
+                    + contextTaskbarPresent);
         } else {
             log.add("\t\tCouldn't get DeviceProfile because !mUserUnlocked");
         }
-
-        Log.d(TASKBAR_NOT_DESTROYED_TAG, log.toString());
     }
 
     private final DeviceProfile.OnDeviceProfileChangeListener mDebugActivityDeviceProfileChanged =
-            dp -> debugTaskbarManager("mActivity onDeviceProfileChanged");
+            dp -> debugPrimaryTaskbar("mActivity onDeviceProfileChanged", true);
 
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
index 6815f97..15c7a8d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
@@ -18,6 +18,7 @@
 import android.util.SparseArray;
 import android.view.View;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.LauncherSettings.Favorites;
@@ -61,6 +62,7 @@
     // Used to defer any UI updates during the SUW unstash animation.
     private boolean mDeferUpdatesForSUW;
     private Runnable mDeferredUpdates;
+    private boolean mBindingItems = false;
 
     public TaskbarModelCallbacks(
             TaskbarActivityContext context, TaskbarView container) {
@@ -74,14 +76,14 @@
 
     @Override
     public void startBinding() {
-        mContext.setBindingItems(true);
+        mBindingItems = true;
         mHotseatItems.clear();
         mPredictedItems = Collections.emptyList();
     }
 
     @Override
     public void finishBindingItems(IntSet pagesBoundFirst) {
-        mContext.setBindingItems(false);
+        mBindingItems = false;
         commitItemsToUI();
     }
 
@@ -119,14 +121,15 @@
     }
 
     @Override
-    public void mapOverItems(ItemOperator op) {
+    public View mapOverItems(@NonNull ItemOperator op) {
         final int itemCount = mContainer.getChildCount();
         for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
             View item = mContainer.getChildAt(itemIdx);
             if (item.getTag() instanceof ItemInfo itemInfo && op.evaluate(itemInfo, item)) {
-                return;
+                return item;
             }
         }
+        return null;
     }
 
     @Override
@@ -167,7 +170,7 @@
     }
 
     private void commitItemsToUI() {
-        if (mContext.isBindingItems()) {
+        if (mBindingItems) {
             return;
         }
 
@@ -203,6 +206,7 @@
             ItemInfo[] hotseatItemInfos, List<GroupTask> recentTasks) {
         mContainer.updateItems(hotseatItemInfos, recentTasks);
         mControllers.taskbarViewController.updateIconViewsRunningStates();
+        mControllers.taskbarPopupController.setHotseatInfosList(mHotseatItems);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java
index 017a12c..d909d19 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java
@@ -165,7 +165,6 @@
     private boolean mIsRtlLayout;
     private final List<Task> mItems = new ArrayList<Task>();
     private int mIconSize;
-    private int mPadding;
     private Paint mItemBackgroundPaint;
     private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this);
     private float mScaleForReorderBounce = 1f;
@@ -214,25 +213,25 @@
         TaskbarOverflowView icon = (TaskbarOverflowView) inflater.inflate(resId, group, false);
 
         icon.mIconSize = iconSize;
-        icon.mPadding = padding;
 
         final float taskbarIconRadius =
-                iconSize * IconNormalizer.ICON_VISIBLE_AREA_FACTOR / 2f - padding;
+                (iconSize - padding * 2f) * IconNormalizer.ICON_VISIBLE_AREA_FACTOR / 2f;
 
         icon.mLeaveBehindSizeDefault = taskbarIconRadius;  // 1/2 of taskbar app icon size
         icon.mLeaveBehindSizeScaledDown =
                 icon.mLeaveBehindSizeDefault * LEAVE_BEHIND_SIZE_SCALE_DOWN_MULTIPLIER;
         icon.mLeaveBehindSize = icon.mLeaveBehindSizeScaledDown;
 
-        icon.mItemIconStrokeWidthDefault = taskbarIconRadius / 5f;  // 1/10 of taskbar app icon size
+        icon.mItemIconStrokeWidthDefault =
+                taskbarIconRadius / 10f;  // 1/20 of taskbar app icon size
         icon.mItemIconStrokeWidth = icon.mItemIconStrokeWidthDefault;
 
-        icon.mItemIconSizeDefault = 2 * (taskbarIconRadius - icon.mItemIconStrokeWidthDefault)
-                * TWO_ITEM_ICONS_BOX_ASPECT_RATIO;
+        icon.mItemIconSizeDefault = 2f * taskbarIconRadius * TWO_ITEM_ICONS_BOX_ASPECT_RATIO;
         icon.mItemIconSizeScaledDown = icon.mLeaveBehindSizeScaledDown;
         icon.mItemIconSize = icon.mItemIconSizeDefault;
 
-        icon.mItemIconCenterOffsetDefault = taskbarIconRadius - icon.mItemIconSizeDefault / 2f
+        icon.mItemIconCenterOffsetDefault = taskbarIconRadius
+                - icon.mItemIconSizeDefault * IconNormalizer.ICON_VISIBLE_AREA_FACTOR / 2f
                 - icon.mItemIconStrokeWidthDefault;
         icon.mItemIconCenterOffset = icon.mItemIconCenterOffsetDefault;
 
@@ -242,9 +241,8 @@
     private void init() {
         mIsRtlLayout = Utilities.isRtl(getResources());
         mItemBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        mItemBackgroundColor = getContext().getColor(Utilities.isDarkTheme(getContext())
-                ? com.android.internal.R.color.materialColorSurface
-                : com.android.internal.R.color.materialColorInverseOnSurface);
+        mItemBackgroundColor = getContext().getColor(
+                com.android.internal.R.color.materialColorInverseOnSurface);
         mLeaveBehindColor = Themes.getAttrColor(getContext(), android.R.attr.textColorTertiary);
 
         setWillNotDraw(false);
@@ -260,8 +258,9 @@
 
     private void drawAppIcons(@NonNull Canvas canvas) {
         mItemBackgroundPaint.setColor(mItemBackgroundColor);
-        float radius = mIconSize / 2f - mPadding;
+        float canvasCenterXY = mIconSize / 2f;
         int adjustedItemIconSize = Math.round(mItemIconSize);
+        float itemIconRadius = adjustedItemIconSize / 2f;
 
         int itemsToShow = Math.min(mItems.size(), MAX_ITEMS_IN_PREVIEW);
         for (int i = itemsToShow - 1; i >= 0; --i) {
@@ -280,12 +279,12 @@
                     BlendMode.SRC_ATOP));
 
             canvas.save();
-            float itemIconRadius = adjustedItemIconSize / 2f;
             canvas.translate(
-                    mPadding + itemCenterX + radius - itemIconRadius,
-                    mPadding + itemCenterY + radius - itemIconRadius);
+                    canvasCenterXY + itemCenterX - itemIconRadius,
+                    canvasCenterXY + itemCenterY - itemIconRadius);
             canvas.drawCircle(itemIconRadius, itemIconRadius,
-                    itemIconRadius + mItemIconStrokeWidth, mItemBackgroundPaint);
+                    itemIconRadius * IconNormalizer.ICON_VISIBLE_AREA_FACTOR + mItemIconStrokeWidth,
+                    mItemBackgroundPaint);
             iconCopy.draw(canvas);
             canvas.restore();
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 5d8b821..1a6cd60 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -16,18 +16,20 @@
 package com.android.launcher3.taskbar;
 
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
 import static com.android.launcher3.model.data.AppInfo.COMPONENT_KEY_COMPARATOR;
-import static com.android.launcher3.popup.SystemShortcut.PIN_UNPIN_ITEM;
 import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
 
 import android.content.Intent;
 import android.content.pm.LauncherApps;
 import android.graphics.Point;
 import android.util.Pair;
+import android.util.SparseArray;
 import android.view.MotionEvent;
 import android.view.View;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.internal.logging.InstanceId;
 import com.android.launcher3.AbstractFloatingView;
@@ -35,11 +37,7 @@
 import com.android.launcher3.Flags;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
-import com.android.launcher3.dot.FolderDotInfo;
-import com.android.launcher3.folder.Folder;
-import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.notification.NotificationListener;
@@ -49,13 +47,12 @@
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.splitscreen.SplitShortcut;
 import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.LauncherBindableItemsContainer;
-import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.ShortcutUtil;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.views.ActivityContext;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.util.LogUtils;
+import com.android.quickstep.util.SingleTask;
 import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 
@@ -65,7 +62,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Objects;
-import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -88,12 +84,14 @@
     private TaskbarControllers mControllers;
     private boolean mAllowInitialSplitSelection;
     private AppInfo[] mAppInfosList;
+    // Saves the ItemInfos in the hotseat without the predicted items.
+    private SparseArray<ItemInfo> mHotseatInfosList;
     private ManageWindowsTaskbarShortcut<BaseTaskbarContext> mManageWindowsTaskbarShortcut;
 
 
     public TaskbarPopupController(TaskbarActivityContext context) {
         mContext = context;
-        mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
+        mPopupDataProvider = new PopupDataProvider(mContext);
     }
 
     public void init(TaskbarControllers controllers) {
@@ -132,39 +130,6 @@
         mAllowInitialSplitSelection = allowInitialSplitSelection;
     }
 
-    private void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
-        final PackageUserKey packageUserKey = new PackageUserKey(null, null);
-        Predicate<ItemInfo> matcher = info -> !packageUserKey.updateFromItemInfo(info)
-                || updatedDots.test(packageUserKey);
-
-        LauncherBindableItemsContainer.ItemOperator op = (info, v) -> {
-            if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView) {
-                if (matcher.test(info)) {
-                    ((BubbleTextView) v).applyDotState(info, true /* animate */);
-                }
-            } else if (info instanceof FolderInfo && v instanceof FolderIcon) {
-                FolderInfo fi = (FolderInfo) info;
-                if (fi.anyMatch(matcher)) {
-                    FolderDotInfo folderDotInfo = new FolderDotInfo();
-                    for (ItemInfo si : fi.getContents()) {
-                        folderDotInfo.addDotInfo(mPopupDataProvider.getDotInfoForItem(si));
-                    }
-                    ((FolderIcon) v).setDotInfo(folderDotInfo);
-                }
-            }
-
-            // process all the shortcuts
-            return false;
-        };
-
-        mControllers.taskbarViewController.mapOverItems(op);
-        Folder folder = Folder.getOpen(mContext);
-        if (folder != null) {
-            folder.iterateOverItems(op);
-        }
-        mControllers.taskbarAllAppsController.updateNotificationDots(updatedDots);
-    }
-
     /**
      * Shows the notifications and deep shortcuts associated with a Taskbar {@param icon}.
      * @return the container if shown or null.
@@ -176,22 +141,35 @@
             icon.clearFocus();
             return null;
         }
-        // TODO(b/344657629) support GroupTask as well, for Taskbar Recent apps
-        if (!(icon.getTag() instanceof ItemInfo item) || !ShortcutUtil.supportsShortcuts(item)) {
+
+        ItemInfo itemInfo;
+        if (icon.getTag() instanceof ItemInfo item && ShortcutUtil.supportsShortcuts(item)) {
+            itemInfo = item;
+        } else if (icon.getTag() instanceof SingleTask task) {
+            itemInfo = SingleTask.Companion.createTaskItemInfo(task);
+        } else {
             return null;
         }
 
         PopupContainerWithArrow<BaseTaskbarContext> container;
-        int deepShortcutCount = mPopupDataProvider.getShortcutCountForItem(item);
+        int deepShortcutCount = mPopupDataProvider.getShortcutCountForItem(itemInfo);
         // TODO(b/198438631): add support for INSTALL shortcut factory
         List<SystemShortcut> systemShortcuts = getSystemShortcuts()
-                .map(s -> s.getShortcut(context, item, icon))
+                .map(s -> s.getShortcut(context, itemInfo, icon))
                 .filter(Objects::nonNull)
                 .collect(Collectors.toList());
 
+        // TODO(b/375648361): Revisit to see if this can be implemented within getSystemShortcuts().
+        if (Flags.enablePinningAppWithContextMenu()) {
+            SystemShortcut shortcut = createPinShortcut(context, itemInfo, icon);
+            if (shortcut != null) {
+                systemShortcuts.add(0, shortcut);
+            }
+        }
+
         container = (PopupContainerWithArrow) context.getLayoutInflater().inflate(
                     R.layout.popup_container, context.getDragLayer(), false);
-        container.populateAndShowRows(icon, deepShortcutCount, systemShortcuts);
+        container.populateAndShowRows(icon, itemInfo, deepShortcutCount, systemShortcuts);
 
         // TODO (b/198438631): configure for taskbar/context
         container.setPopupItemDragHandler(new TaskbarPopupItemDragHandler());
@@ -212,9 +190,6 @@
         // append split options to APP_INFO shortcut if not in Desktop Windowing mode, the order
         // here will reflect in the popup
         ArrayList<SystemShortcut.Factory> shortcuts = new ArrayList<>();
-        if (Flags.enablePinningAppWithContextMenu()) {
-            shortcuts.add(PIN_UNPIN_ITEM);
-        }
         shortcuts.add(APP_INFO);
         if (!mControllers.taskbarDesktopModeController
                 .isInDesktopModeAndNotInOverview(mContext.getDisplayId())) {
@@ -233,6 +208,26 @@
         return shortcuts.stream();
     }
 
+    @Nullable
+    private SystemShortcut createPinShortcut(BaseTaskbarContext target, ItemInfo itemInfo,
+            BubbleTextView originalView) {
+        // Predicted items use {@code HotseatPredictionController.PinPrediction} shortcut to pin.
+        if (itemInfo.isPredictedItem()) {
+            return null;
+        }
+        if (itemInfo.container == CONTAINER_HOTSEAT) {
+            return new PinToTaskbarShortcut<>(target, itemInfo, originalView, false,
+                    mHotseatInfosList);
+        }
+        if (mHotseatInfosList.size()
+                < mContext.getTaskbarSpecsEvaluator().getNumShownHotseatIcons()) {
+            return new PinToTaskbarShortcut<>(target, itemInfo, originalView, true,
+                    mHotseatInfosList);
+        }
+
+        return null;
+    }
+
     @Override
     public void dumpLogs(String prefix, PrintWriter pw) {
         pw.println(prefix + "TaskbarPopupController:");
@@ -316,6 +311,10 @@
         return index < 0 ? null : mAppInfosList[index];
     }
 
+    public void setHotseatInfosList(SparseArray<ItemInfo> info) {
+        mHotseatInfosList = info;
+    }
+
     /**
      * Returns a stream of Multi Instance menu options if an app supports it.
      */
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
index 4a7e4f0..bf73f02 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -31,7 +31,6 @@
 
 import com.android.launcher3.anim.AnimatedFloat;
 import com.android.launcher3.taskbar.bubbles.BubbleControllers;
-import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.SystemUiProxy;
 import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
 
@@ -93,7 +92,8 @@
             // There is no scrim for the bar in the phone mode.
             return;
         }
-        if (isBubbleBarEnabled() && DisplayController.isTransientTaskbar(mActivity)) {
+        boolean isTransient = mActivity.isTransientTaskbar();
+        if (isBubbleBarEnabled() && isTransient) {
             // These scrims aren't used if bubble bar & transient taskbar are active.
             return;
         }
@@ -112,7 +112,7 @@
         boolean showScrimForBubbles = bubblesExpanded
                 && !mTaskbarVisible
                 && isBubbleControllersPresented
-                && !DisplayController.isTransientTaskbar(mActivity)
+                && !mActivity.isTransientTaskbar()
                 && !bubbleControllers.bubbleStashController.isBubblesShowingOnHome();
         return bubblesExpanded && !mControllers.navbarButtonsViewController.isImeVisible()
                 && !isShadeVisible
@@ -122,8 +122,8 @@
     }
 
     private float computeScrimAlpha() {
-        final boolean isPersistentTaskBarVisible =
-                mTaskbarVisible && !DisplayController.isTransientTaskbar(mScrimView.getContext());
+        boolean isTransient = mActivity.isTransientTaskbar();
+        final boolean isPersistentTaskBarVisible = mTaskbarVisible && !isTransient;
         final boolean manageMenuExpanded =
                 (mSysUiStateFlags & SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0;
         if (isPersistentTaskBarVisible && manageMenuExpanded) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
index 25db960..94cff0b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
@@ -22,6 +22,7 @@
 
 import android.content.Intent;
 import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
 import android.util.Pair;
 import android.view.KeyEvent;
 import android.view.View;
@@ -38,6 +39,7 @@
 import com.android.launcher3.util.ShortcutUtil;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.util.LogUtils;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
 
 import java.util.List;
 
@@ -50,6 +52,7 @@
 
     public static final int MOVE_TO_TOP_OR_LEFT = R.id.action_move_to_top_or_left;
     public static final int MOVE_TO_BOTTOM_OR_RIGHT = R.id.action_move_to_bottom_or_right;
+    public static final int CREATE_APPLICATION_BUBBLE = R.id.action_create_application_bubble;
 
     private final LauncherApps mLauncherApps;
     private final StatsLogManager mStatsLogManager;
@@ -67,6 +70,9 @@
                 MOVE_TO_BOTTOM_OR_RIGHT,
                 R.string.move_drop_target_bottom_or_right,
                 KeyEvent.KEYCODE_R));
+        mActions.put(CREATE_APPLICATION_BUBBLE, new LauncherAction(
+                CREATE_APPLICATION_BUBBLE, R.string.open_app_as_a_bubble,
+                KeyEvent.KEYCODE_L));
     }
 
     @Override
@@ -76,11 +82,27 @@
         }
         out.add(mActions.get(MOVE_TO_TOP_OR_LEFT));
         out.add(mActions.get(MOVE_TO_BOTTOM_OR_RIGHT));
+        if (BubbleAnythingFlagHelper.enableCreateAnyBubble()) {
+            out.add(mActions.get(CREATE_APPLICATION_BUBBLE));
+        }
     }
 
     @Override
     protected boolean performAction(View host, ItemInfo item, int action, boolean fromKeyboard) {
-        if (item instanceof ItemInfoWithIcon
+        if (action == DEEP_SHORTCUTS) {
+            mContext.showPopupMenuForIcon((BubbleTextView) host);
+            return true;
+        } else if (action == CREATE_APPLICATION_BUBBLE) {
+            if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+                    && item instanceof WorkspaceItemInfo) {
+                ShortcutInfo shortcutInfo = ((WorkspaceItemInfo) item).getDeepShortcutInfo();
+                SystemUiProxy.INSTANCE.get(mContext).showShortcutBubble(shortcutInfo);
+                return true;
+            } else if (item.getIntent() != null && item.getIntent().getPackage() != null) {
+                SystemUiProxy.INSTANCE.get(mContext).showAppBubble(item.getIntent(), item.user);
+                return true;
+            }
+        } else if (item instanceof ItemInfoWithIcon
                 && (action == MOVE_TO_TOP_OR_LEFT || action == MOVE_TO_BOTTOM_OR_RIGHT)) {
             ItemInfoWithIcon info = (ItemInfoWithIcon) item;
             int side = action == MOVE_TO_TOP_OR_LEFT
@@ -112,10 +134,6 @@
                         instanceIds.first);
             }
             return true;
-        } else if (action == DEEP_SHORTCUTS) {
-            mContext.showPopupMenuForIcon((BubbleTextView) host);
-
-            return true;
         }
         return false;
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java
index f87c21e..fa35a03 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java
@@ -27,7 +27,6 @@
 import com.android.launcher3.anim.AnimatedFloat;
 import com.android.launcher3.anim.SpringAnimationBuilder;
 import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController;
-import com.android.launcher3.util.DisplayController;
 
 import java.io.PrintWriter;
 
@@ -47,7 +46,7 @@
 
     public TaskbarSpringOnStashController(TaskbarActivityContext context) {
         mContext = context;
-        mIsTransientTaskbar = DisplayController.isTransientTaskbar(mContext);
+        mIsTransientTaskbar = context.isTransientTaskbar();
         mStartVelocityPxPerS = context.getResources()
                 .getDimension(R.dimen.transient_taskbar_stash_spring_velocity_dp_per_s);
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 95724ad..5284edd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -346,7 +346,7 @@
                 StashedHandleViewController.ALPHA_INDEX_STASHED);
         mTaskbarStashedHandleHintScale = stashedHandleController.getStashedHandleHintScale();
 
-        boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
+        boolean isTransientTaskbar = mActivity.isTransientTaskbar();
         boolean isInSetup = !mActivity.isUserSetupComplete() || setupUIVisible;
         boolean isStashedInAppAuto =
                 isTransientTaskbar && !mTaskbarSharedState.getTaskbarWasPinned();
@@ -367,9 +367,7 @@
 
         // Hide the background while stashed so it doesn't show on fast swipes home
         boolean shouldHideTaskbarBackground = mActivity.isPhoneMode() ||
-                (enableScalingRevealHomeAnimation()
-                        && DisplayController.isTransientTaskbar(mActivity)
-                        && isStashed());
+                (enableScalingRevealHomeAnimation() && isTransientTaskbar && isStashed());
 
         mTaskbarBackgroundAlphaForStash.setValue(shouldHideTaskbarBackground ? 0 : 1);
 
@@ -414,8 +412,7 @@
         if (DisplayController.isPinnedTaskbar(mActivity)) {
             return PINNED_TASKBAR_TRANSITION_DURATION;
         }
-        return DisplayController.isTransientTaskbar(mActivity)
-                ? TRANSIENT_TASKBAR_STASH_DURATION
+        return mActivity.isTransientTaskbar() ? TRANSIENT_TASKBAR_STASH_DURATION
                 : TASKBAR_STASH_DURATION;
     }
 
@@ -509,8 +506,8 @@
      * @see android.view.WindowInsets.Type#systemBars()
      */
     public int getContentHeightToReportToApps() {
-        if (mActivity.isUserSetupComplete() && (mActivity.isPhoneGestureNavMode()
-                || DisplayController.isTransientTaskbar(mActivity))) {
+        boolean isTransient = mActivity.isTransientTaskbar();
+        if (mActivity.isUserSetupComplete() && (mActivity.isPhoneGestureNavMode() || isTransient)) {
             return getStashedHeight();
         }
 
@@ -577,8 +574,7 @@
      */
     public void updateAndAnimateTransientTaskbar(boolean stash, boolean shouldBubblesFollow,
             boolean delayTaskbarBackground) {
-        if (!DisplayController.isTransientTaskbar(mActivity)
-                || mActivity.isBubbleBarOnPhone()) {
+        if (!mActivity.isTransientTaskbar() || mActivity.isBubbleBarOnPhone()) {
             return;
         }
 
@@ -646,7 +642,7 @@
 
     /** Toggles the Taskbar's stash state. */
     public void toggleTaskbarStash() {
-        if (!DisplayController.isTransientTaskbar(mActivity) || !hasAnyFlag(FLAGS_IN_APP)) return;
+        if (!mActivity.isTransientTaskbar() || !hasAnyFlag(FLAGS_IN_APP)) return;
         updateAndAnimateTransientTaskbar(!hasAnyFlag(FLAG_STASHED_IN_APP_AUTO));
     }
 
@@ -697,8 +693,7 @@
         mAnimator = new AnimatorSet();
         addJankMonitorListener(
                 mAnimator, /* expanding= */ !isStashed, /* tag= */ jankTag);
-        boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
-        final float stashTranslation = mActivity.isPhoneMode() || isTransientTaskbar
+        final float stashTranslation = mActivity.isPhoneMode() || mActivity.isTransientTaskbar()
                 ? 0
                 : (mUnstashedHeight - mStashedHeight);
 
@@ -722,7 +717,7 @@
             return;
         }
 
-        if (isTransientTaskbar) {
+        if (mActivity.isTransientTaskbar()) {
             createTransientAnimToIsStashed(mAnimator, isStashed, duration,
                     shouldDelayBackground, animationType);
         } else {
@@ -1144,7 +1139,7 @@
 
         boolean stashForBubbles = hasAnyFlag(FLAG_IN_OVERVIEW)
                 && hasAnyFlag(systemUiStateFlags, SYSUI_STATE_BUBBLES_EXPANDED)
-                && DisplayController.isTransientTaskbar(mActivity);
+                && mActivity.isTransientTaskbar();
         updateStateForFlag(FLAG_STASHED_SYSUI,
                 hasAnyFlag(systemUiStateFlags, SYSUI_STATE_SCREEN_PINNING) || stashForBubbles);
         updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED,
@@ -1174,7 +1169,7 @@
      * <p>Do not stash if a system gesture is started.
      */
     private boolean shouldStashForIme() {
-        if (DisplayController.isTransientTaskbar(mActivity)) {
+        if (mActivity.isTransientTaskbar()) {
             return false;
         }
         // Do not stash if in small screen, with 3 button nav, and in landscape.
@@ -1270,7 +1265,7 @@
      */
     public void setUpTaskbarSystemAction(boolean visible) {
         UI_HELPER_EXECUTOR.execute(() -> {
-            if (!visible || !DisplayController.isTransientTaskbar(mActivity)
+            if (!visible || !mActivity.isTransientTaskbar()
                     || mActivity.isPhoneMode()) {
                 mAccessibilityManager.unregisterSystemAction(SYSTEM_ACTION_ID_TASKBAR);
                 mIsTaskbarSystemActionRegistered = false;
@@ -1321,7 +1316,7 @@
      *                            If false, attempts to re/start the timeout
      */
     public void updateTaskbarTimeout(boolean isAutohideSuspended) {
-        if (!DisplayController.isTransientTaskbar(mActivity)) {
+        if (!mActivity.isTransientTaskbar()) {
             return;
         }
         if (isAutohideSuspended) {
@@ -1335,9 +1330,7 @@
      * Attempts to start timer to auto hide the taskbar based on time.
      */
     private void tryStartTaskbarTimeout() {
-        if (!DisplayController.isTransientTaskbar(mActivity)
-                || mIsStashed
-                || mEnableBlockingTimeoutDuringTests) {
+        if (!mActivity.isTransientTaskbar() || mIsStashed || mEnableBlockingTimeoutDuringTests) {
             return;
         }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt
index deaf024..df10d24 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt
@@ -23,7 +23,6 @@
 import com.android.launcher3.touch.SingleAxisSwipeDetector
 import com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE
 import com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL
-import com.android.launcher3.util.DisplayController
 import com.android.launcher3.util.TouchController
 import com.android.quickstep.inputconsumers.TaskbarUnstashInputConsumer
 
@@ -39,7 +38,7 @@
 class TaskbarStashViaTouchController(val controllers: TaskbarControllers) : TouchController {
 
     private val activity: TaskbarActivityContext = controllers.taskbarActivityContext
-    private val enabled = DisplayController.isTransientTaskbar(activity)
+    private val enabled = activity.isTransientTaskbar
     private val swipeDownDetector: SingleAxisSwipeDetector
     private val translationCallback = controllers.taskbarTranslationController.transitionCallback
     /** Interpolator to apply resistance as user swipes down to the bottom of the screen. */
@@ -67,7 +66,7 @@
         val gestureHeight: Int =
             ResourceUtils.getNavbarSize(
                 ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
-                activity.resources
+                activity.resources,
             )
         gestureHeightYThreshold = (activity.deviceProfile.heightPx - gestureHeight).toFloat()
     }
@@ -89,7 +88,7 @@
                         maxTouchDisplacement,
                         0f,
                         maxVisualDisplacement,
-                        displacementInterpolator
+                        displacementInterpolator,
                     )
                 )
                 return false
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
index 5a5d6d0..13fb296 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
@@ -29,7 +29,6 @@
 import com.android.app.animation.Interpolators;
 import com.android.launcher3.anim.AnimatedFloat;
 import com.android.launcher3.anim.SpringAnimationBuilder;
-import com.android.launcher3.util.DisplayController;
 
 import java.io.PrintWriter;
 
@@ -63,7 +62,7 @@
 
     public TaskbarTranslationController(TaskbarActivityContext context) {
         mContext = context;
-        mIsTransientTaskbar = DisplayController.isTransientTaskbar(mContext);
+        mIsTransientTaskbar = mContext.isTransientTaskbar();
         mCallback = new TransitionCallback();
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 89bcb41..061a5a1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -37,7 +37,6 @@
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.taskbar.bubbles.BubbleBarController;
-import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.quickstep.util.SplitTask;
 import com.android.quickstep.views.RecentsView;
@@ -116,9 +115,8 @@
      */
     public void hideOverlayWindow() {
         mControllers.keyboardQuickSwitchController.closeQuickSwitchView();
-
-        if (!DisplayController.isTransientTaskbar(mControllers.taskbarActivityContext)
-                || mControllers.taskbarAllAppsController.isOpen()) {
+        boolean isTransientTaskbar = mControllers.taskbarActivityContext.isTransientTaskbar();
+        if (!isTransientTaskbar || mControllers.taskbarAllAppsController.isOpen()) {
             mControllers.taskbarOverlayController.hideWindow();
         }
     }
@@ -207,9 +205,18 @@
         return false;
     }
 
-    /** Returns {@code true} if Home All Apps available instead of Taskbar All Apps. */
-    protected boolean canToggleHomeAllApps() {
-        return false;
+
+    /**
+     * Toggles all apps UI. Default implementation opens Taskbar All Apps, but may be overridden to
+     * open different Alls Apps variant depending on the context.
+     * @param focusSearch indicates whether All Apps should be opened with search input focused.
+     */
+    protected void toggleAllApps(boolean focusSearch) {
+        if (focusSearch) {
+            mControllers.taskbarAllAppsController.toggleSearch();
+        } else {
+            mControllers.taskbarAllAppsController.toggle();
+        }
     }
 
     @CallSuper
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index c92f20b..3ff037e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -16,11 +16,11 @@
 package com.android.launcher3.taskbar;
 
 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
+import static android.window.DesktopModeFlags.ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION;
 
 import static com.android.launcher3.BubbleTextView.DISPLAY_TASKBAR;
 import static com.android.launcher3.Flags.enableCursorHoverStates;
 import static com.android.launcher3.Flags.enableRecentsInTaskbar;
-import static com.android.launcher3.Flags.taskbarRecentsLayoutTransition;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
 import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning;
@@ -61,8 +61,6 @@
 import com.android.launcher3.taskbar.customization.TaskbarAllAppsButtonContainer;
 import com.android.launcher3.taskbar.customization.TaskbarDividerContainer;
 import com.android.launcher3.uioverrides.PredictedAppIcon;
-import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.LauncherBindableItemsContainer;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ActivityContext;
 import com.android.quickstep.util.GroupTask;
@@ -71,13 +69,11 @@
 import com.android.systemui.shared.recents.model.Task;
 import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
-import java.util.function.Predicate;
 
 /**
  * Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
@@ -136,6 +132,7 @@
     private final int mNumStaticViews;
 
     private Set<GroupTask> mPrevRecentTasks = Collections.emptySet();
+    private Set<GroupTask> mPrevOverflowTasks = Collections.emptySet();
 
     public TaskbarView(@NonNull Context context) {
         this(context, null);
@@ -186,8 +183,9 @@
         setWillNotDraw(false);
 
         mAllAppsButtonContainer = new TaskbarAllAppsButtonContainer(context);
-        mAllAppsButtonTranslationOffset =  (int) getResources().getDimension(
-                mAllAppsButtonContainer.getAllAppsButtonTranslationXOffset(isTransientTaskbar()));
+        mAllAppsButtonTranslationOffset = (int) getResources().getDimension(
+                mAllAppsButtonContainer.getAllAppsButtonTranslationXOffset(
+                        mActivityContext.isTransientTaskbar()));
 
         if (enableTaskbarPinning() || enableRecentsInTaskbar()) {
             mTaskbarDividerContainer = new TaskbarDividerContainer(context);
@@ -202,8 +200,7 @@
         // TODO: Disable touch events on QSB otherwise it can crash.
         mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
 
-        mNumStaticViews = taskbarRecentsLayoutTransition() && !mActivityContext.isPhoneMode()
-                ? addStaticViews() : 0;
+        mNumStaticViews = ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue() ? addStaticViews() : 0;
     }
 
     /**
@@ -243,7 +240,7 @@
                 enableTaskbarPinning() && !mActivityContext.isThreeButtonNav();
         availableWidth -= iconSize - (int) getResources().getDimension(
                 mAllAppsButtonContainer.getAllAppsButtonTranslationXOffset(
-                        forceTransientTaskbarSize || isTransientTaskbar()));
+                        forceTransientTaskbarSize || mActivityContext.isTransientTaskbar()));
         ++additionalIcons;
 
         return Math.floorDiv(availableWidth, iconSize) + additionalIcons;
@@ -400,7 +397,7 @@
         // TODO(b/343289567 and b/316004172): support app pairs and desktop mode.
         recentTasks = recentTasks.stream().filter(it -> it instanceof SingleTask).toList();
 
-        if (taskbarRecentsLayoutTransition()) {
+        if (ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue()) {
             updateItemsWithLayoutTransition(hotseatItemInfos, recentTasks);
         } else {
             updateItemsWithoutLayoutTransition(hotseatItemInfos, recentTasks);
@@ -430,7 +427,7 @@
             mAddedDividerForRecents = true;
         }
 
-        updateRecents(recentTasks);
+        updateRecents(recentTasks, hotseatItemInfos.length);
 
         addView(mAllAppsButtonContainer, mIsRtl ? hotseatItemInfos.length : 0);
 
@@ -459,7 +456,7 @@
 
         // Update left section.
         if (mIsRtl) {
-            updateRecents(recentTasks.reversed());
+            updateRecents(recentTasks.reversed(), hotseatItemInfos.length);
         } else {
             updateHotseatItems(hotseatItemInfos);
         }
@@ -474,11 +471,11 @@
         if (mIsRtl) {
             updateHotseatItems(hotseatItemInfos);
         } else {
-            updateRecents(recentTasks);
+            updateRecents(recentTasks, hotseatItemInfos.length);
         }
 
         // Recents divider takes priority.
-        if (!mAddedDividerForRecents) {
+        if (!mAddedDividerForRecents && !mActivityContext.areDesktopTasksVisible()) {
             updateAllAppsDivider();
         }
     }
@@ -605,47 +602,59 @@
         }
     }
 
-    private void updateRecents(List<GroupTask> recentTasks) {
-        // At this point, the all apps button has not been added as a child view, but needs to be
-        // accounted for when comparing current icon count to max number of icons.
-        int nonTaskIconsToBeAdded = 1;
-
+    private void updateRecents(List<GroupTask> recentTasks, int hotseatSize) {
         boolean supportsOverflow = Flags.taskbarOverflow() && recentTasks.size() > 1;
         int overflowSize = 0;
-        if (supportsOverflow) {
-            mIdealNumIcons = mNextViewIndex + recentTasks.size() + nonTaskIconsToBeAdded;
+        boolean hasOverflow = false;
+        if (supportsOverflow && mTaskbarOverflowView != null) {
+            // Need to account for All Apps and the divider. If we need to have an overflow, we will
+            // have a divider for recents.
+            final int nonTaskIconsToBeAdded = 2;
+            mIdealNumIcons = hotseatSize + recentTasks.size() + nonTaskIconsToBeAdded;
             overflowSize = mIdealNumIcons - mMaxNumIcons;
+            hasOverflow = overflowSize > 0;
 
-            if (overflowSize > 0 && mTaskbarOverflowView != null) {
+            if (!ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue() && hasOverflow) {
                 addView(mTaskbarOverflowView, mNextViewIndex++);
-            } else if (mTaskbarOverflowView != null) {
+            } else if (ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue()) {
+                // RTL case is handled after we add the recent icons, because the button needs to
+                // then be to the right of them.
+                if (hasOverflow && !mIsRtl) {
+                    if (mPrevOverflowTasks.isEmpty()) addView(mTaskbarOverflowView, mNextViewIndex);
+                    // NOTE: If overflow already existed, assume the overflow view is already
+                    // at the correct position.
+                    mNextViewIndex++;
+                } else if (!hasOverflow && !mPrevOverflowTasks.isEmpty()) {
+                    removeView(mTaskbarOverflowView);
+                    mTaskbarOverflowView.clearItems();
+                }
+            } else {
                 mTaskbarOverflowView.clearItems();
             }
         }
 
-        List<Task> overflownTasks = null;
         // An extra item needs to be added to overflow button to account for the space taken up by
         // the overflow button.
         final int itemsToAddToOverflow =
-                (overflowSize > 0) ? Math.min(overflowSize + 1, recentTasks.size()) : 0;
-        if (overflowSize > 0) {
-            overflownTasks = new ArrayList<>(itemsToAddToOverflow);
+                hasOverflow ? Math.min(overflowSize + 1, recentTasks.size()) : 0;
+        final Set<GroupTask> overflownRecentsSet;
+        if (hasOverflow && mTaskbarOverflowView != null) {
+            final int startIndex = mIsRtl ? recentTasks.size() - itemsToAddToOverflow : 0;
+            final int endIndex = mIsRtl ? recentTasks.size() : itemsToAddToOverflow;
+            final List<GroupTask> overflownRecents = recentTasks.subList(startIndex, endIndex);
+            mTaskbarOverflowView.setItems(
+                    overflownRecents.stream().map(t -> ((SingleTask) t).getTask()).toList());
+            overflownRecentsSet = new ArraySet<>(overflownRecents);
+        } else {
+            overflownRecentsSet = Collections.emptySet();
         }
 
         // Add Recent/Running icons.
         final Set<GroupTask> recentTasksSet = new ArraySet<>(recentTasks);
-        for (GroupTask task : recentTasks) {
-            if (mTaskbarOverflowView != null && overflownTasks != null
-                    && overflownTasks.size() < itemsToAddToOverflow
-                    && task instanceof SingleTask singleTask) {
-                // TODO(b/343289567 and b/316004172): support app pairs and desktop mode.
-                overflownTasks.add(singleTask.getTask());
-                if (overflownTasks.size() == itemsToAddToOverflow) {
-                    mTaskbarOverflowView.setItems(overflownTasks);
-                }
-                continue;
-            }
-
+        final int startIndex = mIsRtl ? 0 : itemsToAddToOverflow;
+        final int endIndex =
+                mIsRtl ? recentTasks.size() - itemsToAddToOverflow : recentTasks.size();
+        for (GroupTask task : recentTasks.subList(startIndex, endIndex)) {
             // Replace any Recent views with the appropriate type if it's not already that type.
             final int expectedLayoutResId;
             boolean isCollection = false;
@@ -664,17 +673,19 @@
 
             View recentIcon = null;
             // If a task is new, we should not reuse a view so that it animates in when it is added.
-            final boolean canReuseView = !taskbarRecentsLayoutTransition()
-                    || mPrevRecentTasks.contains(task);
+            final boolean canReuseView = !ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue()
+                    || (mPrevRecentTasks.contains(task) && !mPrevOverflowTasks.contains(task));
             while (canReuseView && isNextViewInSection(GroupTask.class)) {
                 recentIcon = getChildAt(mNextViewIndex);
+                GroupTask tag = (GroupTask) recentIcon.getTag();
 
                 // see if the view can be reused
                 if ((recentIcon.getSourceLayoutResId() != expectedLayoutResId)
-                        || (isCollection && (recentIcon.getTag() != task))
+                        || (isCollection && tag != task)
                         // Remove view corresponding to removed task so that it animates out.
-                        || (taskbarRecentsLayoutTransition()
-                                && !recentTasksSet.contains(recentIcon.getTag()))) {
+                        || (ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue()
+                                && (!recentTasksSet.contains(tag)
+                                        || overflownRecentsSet.contains(tag)))) {
                     removeAndRecycle(recentIcon);
                     recentIcon = null;
                 } else {
@@ -705,7 +716,15 @@
             removeAndRecycle(getChildAt(mNextViewIndex));
         }
 
+        if (ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue() && mIsRtl && hasOverflow) {
+            if (mPrevOverflowTasks.isEmpty()) {
+                addView(mTaskbarOverflowView, mNextViewIndex);
+            }
+            mNextViewIndex++;
+        }
+
         mPrevRecentTasks = recentTasksSet;
+        mPrevOverflowTasks = overflownRecentsSet;
     }
 
     private boolean isNextViewInSection(Class<?> tagClass) {
@@ -1079,11 +1098,6 @@
         // Ignore, we just implement Insettable to draw behind system insets.
     }
 
-    private boolean isTransientTaskbar() {
-        return DisplayController.isTransientTaskbar(mActivityContext)
-                && !mActivityContext.isPhoneMode();
-    }
-
     public boolean areIconsVisible() {
         // Consider the overall visibility
         return getVisibility() == VISIBLE;
@@ -1098,43 +1112,6 @@
     }
 
     /**
-     * Maps {@code op} over all the child views.
-     */
-    public void mapOverItems(LauncherBindableItemsContainer.ItemOperator op) {
-        // map over all the shortcuts on the taskbar
-        for (int i = 0; i < getChildCount(); i++) {
-            View item = getChildAt(i);
-            // TODO(b/344657629): Support GroupTask as well for notification dots/popup
-            if (item.getTag() instanceof ItemInfo itemInfo && op.evaluate(itemInfo, item)) {
-                return;
-            }
-        }
-    }
-
-    /**
-     * Finds the first icon to match one of the given matchers, from highest to lowest priority.
-     *
-     * @return The first match, or All Apps button if no match was found.
-     */
-    public View getFirstMatch(Predicate<ItemInfo>... matchers) {
-        for (Predicate<ItemInfo> matcher : matchers) {
-            for (int i = 0; i < getChildCount(); i++) {
-                View item = getChildAt(i);
-                if (!(item.getTag() instanceof ItemInfo)) {
-                    // Should only happen for All Apps button.
-                    // Will also happen for Recent/Running app icons. (Which have GroupTask as tags)
-                    continue;
-                }
-                ItemInfo info = (ItemInfo) item.getTag();
-                if (matcher.test(info)) {
-                    return item;
-                }
-            }
-        }
-        return mAllAppsButtonContainer;
-    }
-
-    /**
      * This method only works for bubble bar enabled in persistent task bar and the taskbar is start
      * aligned.
      */
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
index c7ef960..dcb9fbf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
@@ -16,7 +16,8 @@
 
 package com.android.launcher3.taskbar;
 
-import static com.android.launcher3.Flags.taskbarRecentsLayoutTransition;
+import static android.window.DesktopModeFlags.ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION;
+
 import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_LONG_PRESS;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP;
@@ -66,7 +67,16 @@
         InteractionJankMonitorWrapper.begin(v, Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS,
                 /* tag= */ "TASKBAR_BUTTON");
         mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP);
-        mControllers.taskbarAllAppsController.toggle();
+        if (DisplayController.showLockedTaskbarOnHome(mActivity)
+                || DisplayController.showDesktopTaskbarForFreeformDisplay(mActivity)) {
+            // If the taskbar can be shown on the home screen, use mAllAppsToggler to toggle all
+            // apps, which will toggle the launcher activity all apps when on home screen.
+            // TODO(b/395913143): Reconsider this if a gap in taskbar all apps functionality that
+            //  prevents users to drag items to workspace is addressed.
+            mControllers.uiController.toggleAllApps(false);
+        } else {
+            mControllers.taskbarAllAppsController.toggle();
+        }
     }
 
     /** Trigger All Apps button long click action. */
@@ -114,7 +124,7 @@
 
     /** Callback invoked before Taskbar icons are laid out. */
     void onPreLayoutChildren() {
-        if (enableTaskbarPinning() && taskbarRecentsLayoutTransition()) {
+        if (enableTaskbarPinning() && ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue()) {
             mControllers.taskbarViewController.updateTaskbarIconTranslationXForPinning();
         }
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 384468c..605171a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -19,13 +19,14 @@
 import static android.animation.LayoutTransition.CHANGE_APPEARING;
 import static android.animation.LayoutTransition.CHANGE_DISAPPEARING;
 import static android.animation.LayoutTransition.DISAPPEARING;
+import static android.window.DesktopModeFlags.ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION;
 
 import static com.android.app.animation.Interpolators.EMPHASIZED;
 import static com.android.app.animation.Interpolators.FINAL_FRAME;
 import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.launcher3.BubbleTextView.LINE_INDICATOR_ANIM_DURATION;
 import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
 import static com.android.launcher3.Flags.taskbarOverflow;
-import static com.android.launcher3.Flags.taskbarRecentsLayoutTransition;
 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;
@@ -83,14 +84,16 @@
 import com.android.launcher3.model.data.TaskItemInfo;
 import com.android.launcher3.taskbar.bubbles.BubbleBarController;
 import com.android.launcher3.taskbar.bubbles.BubbleControllers;
+import com.android.launcher3.taskbar.customization.TaskbarAllAppsButtonContainer;
+import com.android.launcher3.taskbar.customization.TaskbarDividerContainer;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.LauncherBindableItemsContainer;
-import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
 import com.android.launcher3.util.MultiPropertyFactory;
 import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
 import com.android.launcher3.util.MultiTranslateDelegate;
 import com.android.launcher3.util.MultiValueAlpha;
+import com.android.launcher3.util.SandboxContext;
 import com.android.quickstep.util.GroupTask;
 import com.android.quickstep.util.SingleTask;
 import com.android.systemui.shared.recents.model.Task;
@@ -136,6 +139,8 @@
     private static final int TRANSITION_DEFAULT_DURATION = 500;
     private static final int TRANSITION_FADE_IN_DURATION = 167;
     private static final int TRANSITION_FADE_OUT_DURATION = 83;
+    private static final int APPEARING_LINE_INDICATOR_ANIM_DELAY =
+            TRANSITION_DEFAULT_DURATION - LINE_INDICATOR_ANIM_DURATION;
 
     private final TaskbarActivityContext mActivity;
     private @Nullable TaskbarDragLayerController mDragLayerController;
@@ -187,7 +192,7 @@
 
     private final View.OnLayoutChangeListener mTaskbarViewLayoutChangeListener =
             (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
-                if (!taskbarRecentsLayoutTransition()) {
+                if (!ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue()) {
                     // update shiftX is handled with the animation at the end of the method
                     updateTaskbarIconTranslationXForPinning(/* updateShiftXForBubbleBar = */ false);
                 }
@@ -262,9 +267,8 @@
                 : mActivity.getDeviceProfile().taskbarHeight;
 
         mTaskbarIconScaleForStash.updateValue(1f);
-        float pinningValue = DisplayController.isTransientTaskbar(mActivity)
-                ? PINNING_TRANSIENT
-                : PINNING_PERSISTENT;
+        float pinningValue =
+                mActivity.isTransientTaskbar() ? PINNING_TRANSIENT : PINNING_PERSISTENT;
         mTaskbarIconScaleForPinning.updateValue(pinningValue);
         mTaskbarIconTranslationYForPinning.updateValue(pinningValue);
         mTaskbarIconTranslationXForPinning.updateValue(pinningValue);
@@ -734,11 +738,22 @@
     public void updateIconViewsRunningStates() {
         for (View iconView : getIconViews()) {
             if (iconView instanceof BubbleTextView btv) {
-                btv.updateRunningState(getRunningAppState(btv));
+                updateRunningState(btv);
+                if (shouldUpdateIconContentDescription(btv)) {
+                    btv.setContentDescription(
+                            btv.getContentDescription() + " " + btv.getIconStateDescription());
+                }
             }
         }
     }
 
+    private boolean shouldUpdateIconContentDescription(BubbleTextView btv) {
+        boolean isInDesktopMode = mControllers.taskbarDesktopModeController.isInDesktopMode();
+        boolean isAllAppsButton = btv instanceof TaskbarAllAppsButtonContainer;
+        boolean isDividerButton = btv instanceof TaskbarDividerContainer;
+        return isInDesktopMode && !isAllAppsButton && !isDividerButton;
+    }
+
     /**
      * @return A set of Task ids of running apps that are pinned in the taskbar.
      */
@@ -757,6 +772,10 @@
         return pinnedAppsWithTasks;
     }
 
+    private void updateRunningState(BubbleTextView btv) {
+        btv.updateRunningState(getRunningAppState(btv), mTaskbarView.getLayoutTransition() != null);
+    }
+
     private BubbleTextView.RunningAppState getRunningAppState(BubbleTextView btv) {
         Object tag = btv.getTag();
         if (tag instanceof TaskItemInfo itemInfo) {
@@ -916,7 +935,7 @@
         mOnControllerPreCreateCallback.run();
         DeviceProfile taskbarDp = mActivity.getDeviceProfile();
         Rect hotseatPadding = launcherDp.getHotseatLayoutPadding(mActivity);
-        boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
+        boolean isTransientTaskbar = mActivity.isTransientTaskbar();
 
         float scaleUp = ((float) launcherDp.iconSizePx) / taskbarDp.taskbarIconSize;
         int borderSpacing = launcherDp.hotseatBorderSpace;
@@ -1167,11 +1186,8 @@
         mTaskbarNavButtonTranslationY.updateValue(-deviceProfile.getTaskbarOffsetY());
     }
 
-    /**
-     * Maps the given operator to all the top-level children of TaskbarView.
-     */
-    public void mapOverItems(LauncherBindableItemsContainer.ItemOperator op) {
-        mTaskbarView.mapOverItems(op);
+    public LauncherBindableItemsContainer getContent() {
+        return mModelCallbacks;
     }
 
     /**
@@ -1181,8 +1197,8 @@
      * 3) All Apps button
      */
     public View getFirstIconMatch(Predicate<ItemInfo> matcher) {
-        Predicate<ItemInfo> collectionMatcher = ItemInfoMatcher.forFolderMatch(matcher);
-        return mTaskbarView.getFirstMatch(matcher, collectionMatcher);
+        View icon = mModelCallbacks.getFirstMatch(matcher, ItemInfoMatcher.forFolderMatch(matcher));
+        return icon != null ? icon : mTaskbarView.getAllAppsButtonContainer();
     }
 
     /**
@@ -1196,7 +1212,8 @@
     /** Called when there's a change in running apps to update the UI. */
     public void commitRunningAppsToUI() {
         mModelCallbacks.commitRunningAppsToUI();
-        if (taskbarRecentsLayoutTransition() && mTaskbarView.getLayoutTransition() == null) {
+        if (ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue()
+                && mTaskbarView.getLayoutTransition() == null) {
             // Set up after the first commit so that the initial recents do not animate (janky).
             mTaskbarView.setLayoutTransition(createLayoutTransitionForRunningApps());
         }
@@ -1214,13 +1231,22 @@
                     view.setAlpha(0f);
                     view.setScaleX(0f);
                     view.setScaleY(0f);
+                    if (view instanceof BubbleTextView btv) {
+                        // Defer so that app is mostly scaled in before showing indicator.
+                        btv.setLineIndicatorAnimStartDelay(APPEARING_LINE_INDICATOR_ANIM_DELAY);
+                    }
+                } else if (type == DISAPPEARING && view instanceof BubbleTextView btv) {
+                    // Running state updates happen after removing this view, so update it here.
+                    updateRunningState(btv);
                 }
             }
 
             @Override
             public void endTransition(
                     LayoutTransition transition, ViewGroup container, View view, int type) {
-                // Do nothing.
+                if (type == APPEARING && view instanceof BubbleTextView btv) {
+                    btv.setLineIndicatorAnimStartDelay(0);
+                }
             }
         });
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TypefaceUtils.kt b/quickstep/src/com/android/launcher3/taskbar/TypefaceUtils.kt
deleted file mode 100644
index e9c62d1..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/TypefaceUtils.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.taskbar
-
-import android.graphics.Typeface
-import android.widget.TextView
-import com.android.launcher3.Flags
-
-/**
- * Helper util class to set pre-defined typefaces to textviews
- *
- * If the typeface font family is already defined here, you can just reuse it directly. Otherwise,
- * please define it here for future use. You do not need to define the font style. If you need
- * anything other than [Typeface.NORMAL], pass it inline when calling [setTypeface]
- */
-class TypefaceUtils {
-
-    companion object {
-        const val FONT_FAMILY_BODY_SMALL_BASELINE = "variable-body-small"
-        const val FONT_FAMILY_BODY_MEDIUM_BASELINE = "variable-body-medium"
-        const val FONT_FAMILY_BODY_LARGE_BASELINE = "variable-body-large"
-        const val FONT_FAMILY_LABEL_LARGE_BASELINE = "variable-label-large"
-        const val FONT_FAMILY_DISPLAY_SMALL_EMPHASIZED = "variable-display-small-emphasized"
-        const val FONT_FAMILY_DISPLAY_MEDIUM_EMPHASIZED = "variable-display-medium-emphasized"
-        const val FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED = "variable-headline-small-emphasized"
-        const val FONT_FAMILY_HEADLINE_LARGE_EMPHASIZED = "variable-headline-large-emphasized"
-
-        @JvmStatic
-        @JvmOverloads
-        fun setTypeface(
-            textView: TextView?,
-            fontFamilyName: String,
-            fontStyle: Int = Typeface.NORMAL,
-        ) {
-            if (!Flags.expressiveThemeInTaskbarAndNavigation()) return
-            textView?.typeface = Typeface.create(fontFamilyName, fontStyle)
-        }
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
index c380c8d..c7d42b1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
@@ -22,7 +22,6 @@
 import android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION
 import android.view.WindowManager
 import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
-import com.android.launcher3.util.DisplayController
 import com.android.launcher3.views.BaseDragLayer
 import com.android.systemui.animation.ViewRootSync
 import java.io.PrintWriter
@@ -41,8 +40,7 @@
 class VoiceInteractionWindowController(val context: TaskbarActivityContext) :
     TaskbarControllers.LoggableTaskbarController, TaskbarControllers.BackgroundRendererController {
 
-    private val isSeparateBackgroundEnabled =
-        !DisplayController.isTransientTaskbar(context) && !context.isPhoneMode
+    private val isSeparateBackgroundEnabled = !context.isTransientTaskbar && !context.isPhoneMode
     private val taskbarBackgroundRenderer = TaskbarBackgroundRenderer(context)
     private val nonTouchableInsetsComputer =
         ViewTreeObserver.OnComputeInternalInsetsListener {
@@ -97,7 +95,7 @@
         separateWindowLayoutParams =
             context.createDefaultWindowLayoutParams(
                 TYPE_APPLICATION_OVERLAY,
-                TEMP_BACKGROUND_WINDOW_TITLE
+                TEMP_BACKGROUND_WINDOW_TITLE,
             )
         separateWindowLayoutParams?.isSystemApplicationOverlay = true
     }
@@ -163,7 +161,7 @@
                 // First add the temporary window, then hide the overlapping taskbar background.
                 context.addWindowView(
                     separateWindowForTaskbarBackground,
-                    separateWindowLayoutParams
+                    separateWindowLayoutParams,
                 );
                 { controllers.taskbarDragLayerController.setIsBackgroundDrawnElsewhere(true) }
             } else {
@@ -179,7 +177,7 @@
                 ViewRootSync.synchronizeNextDraw(
                     separateWindowForTaskbarBackground!!,
                     context.dragLayer,
-                    onWindowsSynchronized
+                    onWindowsSynchronized,
                 )
             }
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
index ddbf3b7..6c55b28 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
@@ -35,7 +35,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.function.Predicate;
 /**
  * Handles the all apps overlay window initialization, updates, and its data.
  * <p>
@@ -120,13 +119,6 @@
         mZeroStateSearchSuggestions = zeroStateSearchSuggestions;
     }
 
-    /** Updates the current notification dots. */
-    public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
-        if (mAppsView != null) {
-            mAppsView.getAppsStore().updateNotificationDots(updatedDots);
-        }
-    }
-
     /** Toggles visibility of {@link TaskbarAllAppsContainerView} in the overlay window. */
     public void toggle() {
         toggle(false);
@@ -218,6 +210,11 @@
         mAppsView = null;
     }
 
+    @Nullable
+    public TaskbarAllAppsContainerView getAppsView() {
+        return mAppsView;
+    }
+
     @VisibleForTesting
     public int getTaskbarAllAppsTopPadding() {
         // Allow null-pointer since this should only be null if the apps view is not showing.
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
index 52f7176..f1ccd39 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
@@ -31,7 +31,6 @@
 import com.android.launcher3.taskbar.TaskbarStashController;
 import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
 import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
-import com.android.launcher3.util.DisplayController;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 
 import java.util.Optional;
@@ -90,7 +89,7 @@
     }
 
     private void setUpTaskbarStashing() {
-        if (DisplayController.isTransientTaskbar(mContext)) {
+        if (mContext.isTransientTaskbar()) {
             mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, true);
             mTaskbarStashController.applyState();
         }
@@ -103,7 +102,7 @@
             AbstractFloatingView.closeOpenContainer(
                     mContext, AbstractFloatingView.TYPE_ACTION_POPUP);
 
-            if (DisplayController.isTransientTaskbar(mContext)) {
+            if (mContext.isTransientTaskbar()) {
                 mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, false);
                 mTaskbarStashController.applyState();
             }
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarLocationDropTarget.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarLocationDropTarget.kt
new file mode 100644
index 0000000..383f4d2
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarLocationDropTarget.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar.bubbles
+
+import android.graphics.Rect
+import android.view.View
+import com.android.launcher3.DropTarget
+import com.android.launcher3.dragndrop.DragOptions
+import com.android.launcher3.model.data.ItemInfo
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
+
+/**
+ * Implementation of the {@link DropTarget} that handles drag and drop events over the bubble bar
+ * locations.
+ */
+class BubbleBarLocationDropTarget(
+    private val bubbleBarLocation: BubbleBarLocation,
+    private val bubbleBarDragListener: BubbleBarDragListener,
+) : DropTarget {
+
+    /** Controller that takes care of the bubble bar drag events inside launcher process. */
+    interface BubbleBarDragListener {
+
+        /** Called when the drag event is over the bubble bar drop zone. */
+        fun onLauncherItemDraggedOverBubbleBarDragZone(location: BubbleBarLocation)
+
+        /** Called when the drag event leaves the bubble bar drop zone. */
+        fun onLauncherItemDraggedOutsideBubbleBarDropZone()
+
+        /** Called when the drop event happens over the bubble bar drop zone. */
+        fun onLauncherItemDroppedOverBubbleBarDragZone(
+            location: BubbleBarLocation,
+            itemInfo: ItemInfo,
+        )
+
+        /** Gets the hit [rect][android.graphics.Rect] of the bubble bar location. */
+        fun getBubbleBarLocationHitRect(bubbleBarLocation: BubbleBarLocation, outRect: Rect)
+
+        /** Provides the view that will accept the drop. */
+        fun getDropView(): View
+    }
+
+    private var isShowingDropTarget = false
+
+    override fun isDropEnabled(): Boolean = true
+
+    override fun onDrop(dragObject: DropTarget.DragObject, options: DragOptions) {
+        val itemInfo = dragObject.dragInfo ?: return
+        // TODO(b/397459664) : fix task bar icon animation after drop
+        // TODO(b/397459664) : update bubble bar location
+        bubbleBarDragListener.onLauncherItemDroppedOverBubbleBarDragZone(
+            bubbleBarLocation,
+            itemInfo,
+        )
+    }
+
+    override fun onDragEnter(dragObject: DropTarget.DragObject) {}
+
+    override fun onDragOver(dragObject: DropTarget.DragObject) {
+        if (isShowingDropTarget) return
+        isShowingDropTarget = true
+        bubbleBarDragListener.onLauncherItemDraggedOverBubbleBarDragZone(bubbleBarLocation)
+    }
+
+    override fun onDragExit(dragObject: DropTarget.DragObject) {
+        // TODO(b/397459664) : fix the issue for no bubbles, when moving task bar icon out of
+        // the bubble bar drag zone drag ends and swipes gesture swipes the overview
+        if (!isShowingDropTarget) return
+        isShowingDropTarget = false
+        bubbleBarDragListener.onLauncherItemDraggedOutsideBubbleBarDropZone()
+    }
+
+    override fun acceptDrop(dragObject: DropTarget.DragObject): Boolean = true
+
+    override fun prepareAccessibilityDrop() {}
+
+    override fun getHitRectRelativeToDragLayer(outRect: Rect) {
+        bubbleBarDragListener.getBubbleBarLocationHitRect(bubbleBarLocation, outRect)
+    }
+
+    override fun getDropView(): View = bubbleBarDragListener.getDropView()
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index b90a5b0..ddf9d51 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -24,6 +24,8 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.PointF;
@@ -43,11 +45,15 @@
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AnimatedFloat;
 import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.taskbar.TaskbarActivityContext;
 import com.android.launcher3.taskbar.TaskbarControllers;
+import com.android.launcher3.taskbar.TaskbarDragController;
 import com.android.launcher3.taskbar.TaskbarInsetsController;
 import com.android.launcher3.taskbar.TaskbarSharedState;
 import com.android.launcher3.taskbar.TaskbarStashController;
+import com.android.launcher3.taskbar.bubbles.BubbleBarLocationDropTarget.BubbleBarDragListener;
 import com.android.launcher3.taskbar.bubbles.animation.BubbleBarViewAnimator;
 import com.android.launcher3.taskbar.bubbles.flyout.BubbleBarFlyoutController;
 import com.android.launcher3.taskbar.bubbles.flyout.BubbleBarFlyoutPositioner;
@@ -59,6 +65,7 @@
 import com.android.quickstep.SystemUiProxy;
 import com.android.wm.shell.Flags;
 import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.DeviceConfig;
 
 import java.io.PrintWriter;
 import java.util.List;
@@ -117,6 +124,59 @@
         updateTranslationY();
         setBubbleBarScaleAndPadding(pinningProgress);
     });
+    private final BubbleBarDragListener mDragListener = new BubbleBarDragListener() {
+
+        @Override
+        public void getBubbleBarLocationHitRect(@NonNull BubbleBarLocation bubbleBarLocation,
+                Rect outRect) {
+            Point screenSize = DisplayController.INSTANCE.get(mActivity).getInfo().currentSize;
+            outRect.top = screenSize.y - mBubbleBarDropTargetSize;
+            outRect.bottom = screenSize.y;
+            if (bubbleBarLocation.isOnLeft(mBarView.isLayoutRtl())) {
+                outRect.left = 0;
+                outRect.right = mBubbleBarDropTargetSize;
+            } else {
+                outRect.left = screenSize.x - mBubbleBarDropTargetSize;
+                outRect.right = screenSize.x;
+            }
+        }
+
+        @Override
+        public void onLauncherItemDroppedOverBubbleBarDragZone(@NonNull BubbleBarLocation location,
+                @NonNull ItemInfo itemInfo) {
+            if (itemInfo instanceof WorkspaceItemInfo) {
+                ShortcutInfo shortcutInfo = ((WorkspaceItemInfo) itemInfo).getDeepShortcutInfo();
+                if (shortcutInfo != null) {
+                    mSystemUiProxy.showShortcutBubble(shortcutInfo, location);
+                    return;
+                }
+            }
+            Intent itemIntent = itemInfo.getIntent();
+            if (itemIntent != null && itemIntent.getComponent() != null) {
+                itemIntent.setPackage(itemIntent.getComponent().getPackageName());
+                mSystemUiProxy.showAppBubble(itemIntent, itemInfo.user, location);
+            }
+        }
+
+        @Override
+        public void onLauncherItemDraggedOutsideBubbleBarDropZone() {
+            onItemDraggedOutsideBubbleBarDropZone();
+            mSystemUiProxy.showBubbleDropTarget(/* show = */ false);
+        }
+
+        @Override
+        public void onLauncherItemDraggedOverBubbleBarDragZone(
+                @NonNull BubbleBarLocation location) {
+            onDragItemOverBubbleBarDragZone(location);
+            mSystemUiProxy.showBubbleDropTarget(/* show = */ true, location);
+        }
+
+        @NonNull
+        @Override
+        public View getDropView() {
+            return mBarView;
+        }
+    };
 
     // Modified when swipe up is happening on the bubble bar or task bar.
     private float mBubbleBarSwipeUpTranslationY;
@@ -139,8 +199,12 @@
     private BubbleBarFlyoutController mBubbleBarFlyoutController;
     private BubbleBarPinController mBubbleBarPinController;
     private TaskbarSharedState mTaskbarSharedState;
+    private TaskbarDragController mTaskbarDragController;
+    private final BubbleBarLocationDropTarget mBubbleBarLeftDropTarget;
+    private final BubbleBarLocationDropTarget mBubbleBarRightDropTarget;
     private final TimeSource mTimeSource = System::currentTimeMillis;
     private final int mTaskbarTranslationDelta;
+    private final int mBubbleBarDropTargetSize;
 
     @Nullable
     private BubbleBarBoundsChangeListener mBoundsChangeListener;
@@ -158,11 +222,21 @@
                 R.dimen.bubblebar_transient_taskbar_min_distance);
         mDragElevation = res.getDimensionPixelSize(R.dimen.bubblebar_drag_elevation);
         mTaskbarTranslationDelta = getBubbleBarTranslationDeltaForTaskbar(activity);
+        if (DeviceConfig.isSmallTablet(mActivity)) {
+            mBubbleBarDropTargetSize = res.getDimensionPixelSize(R.dimen.drag_zone_bubble_fold);
+        } else {
+            mBubbleBarDropTargetSize = res.getDimensionPixelSize(R.dimen.drag_zone_bubble_tablet);
+        }
+        mBubbleBarLeftDropTarget = new BubbleBarLocationDropTarget(BubbleBarLocation.LEFT,
+                mDragListener);
+        mBubbleBarRightDropTarget = new BubbleBarLocationDropTarget(BubbleBarLocation.RIGHT,
+                mDragListener);
     }
 
     /** Initializes controller. */
     public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers,
             TaskbarViewPropertiesProvider taskbarViewPropertiesProvider) {
+        mTaskbarDragController = controllers.taskbarDragController;
         mTaskbarSharedState = controllers.getSharedState();
         mBubbleStashController = bubbleControllers.bubbleStashController;
         mBubbleBarController = bubbleControllers.bubbleBarController;
@@ -199,7 +273,7 @@
                         mBoundsChangeListener.onBoundsChanged();
                     }
                 });
-        float pinningValue = DisplayController.isTransientTaskbar(mActivity)
+        float pinningValue = mActivity.isTransientTaskbar()
                 ? PINNING_TRANSIENT
                 : PINNING_PERSISTENT;
         mBubbleBarPinning.updateValue(pinningValue);
@@ -264,6 +338,8 @@
                 mBubbleBarController.updateBubbleBarLocation(location, source);
             }
         };
+        mTaskbarDragController.addDropTarget(mBubbleBarLeftDropTarget);
+        mTaskbarDragController.addDropTarget(mBubbleBarRightDropTarget);
     }
 
     /** Returns animated float property responsible for pinning transition animation. */
@@ -542,7 +618,9 @@
      */
     public void onDragItemOverBubbleBarDragZone(@NonNull BubbleBarLocation bubbleBarLocation) {
         mBarView.showDropTarget(/* isDropTarget = */ true);
-        mIsLocationUpdatedForDropTarget = getBubbleBarLocation() != bubbleBarLocation;
+        boolean isRtl = mBarView.isLayoutRtl();
+        mIsLocationUpdatedForDropTarget = getBubbleBarLocation().isOnLeft(isRtl)
+                != bubbleBarLocation.isOnLeft(isRtl);
         if (mIsLocationUpdatedForDropTarget) {
             animateBubbleBarLocation(bubbleBarLocation);
         }
@@ -1278,6 +1356,21 @@
     /** Called when the controller is destroyed. */
     public void onDestroy() {
         adjustTaskbarAndHotseatToBubbleBarState(/*isBubbleBarExpanded = */false);
+        mTaskbarDragController.removeDropTarget(mBubbleBarLeftDropTarget);
+        mTaskbarDragController.removeDropTarget(mBubbleBarRightDropTarget);
+    }
+
+    /**
+     * Removes the bubble from the bubble bar and notifies sysui that the bubble should move to
+     * full screen.
+     */
+    public void moveBubbleToFullscreen(@NonNull BubbleView bubbleView) {
+        if (bubbleView.getBubble() == null) {
+            return;
+        }
+        String key = bubbleView.getBubble().getKey();
+        mSystemUiProxy.moveBubbleToFullscreen(key);
+        onBubbleDismissed(bubbleView);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
index 0abd88c..f77b934 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
@@ -21,6 +21,8 @@
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -28,7 +30,16 @@
 
 import com.android.launcher3.taskbar.TaskbarActivityContext;
 import com.android.wm.shell.shared.bubbles.BaseBubblePinController.LocationChangeListener;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
 import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.DeviceConfig;
+import com.android.wm.shell.shared.bubbles.DragZone;
+import com.android.wm.shell.shared.bubbles.DragZoneFactory;
+import com.android.wm.shell.shared.bubbles.DragZoneFactory.DesktopWindowModeChecker;
+import com.android.wm.shell.shared.bubbles.DragZoneFactory.SplitScreenModeChecker;
+import com.android.wm.shell.shared.bubbles.DraggedObject;
+import com.android.wm.shell.shared.bubbles.DropTargetManager;
+import com.android.wm.shell.shared.bubbles.DropTargetManager.DragZoneChangedListener;
 
 /**
  * Controls bubble bar drag interactions.
@@ -76,11 +87,36 @@
     private BubbleDismissController mBubbleDismissController;
     private BubbleBarPinController mBubbleBarPinController;
     private BubblePinController mBubblePinController;
+    private final DropTargetManager mDropTargetManager;
+    private final DragZoneFactory mDragZoneFactory;
+    private final BubbleDragZoneChangedListener mBubbleDragZoneChangedListener;
 
     private boolean mIsDragging;
 
-    public BubbleDragController(TaskbarActivityContext activity) {
+    public BubbleDragController(TaskbarActivityContext activity, FrameLayout dropTargetParent) {
         mActivity = activity;
+        WindowManager windowManager =
+                mActivity.getApplicationContext().getSystemService(WindowManager.class);
+        DeviceConfig deviceConfig =
+                DeviceConfig.create(mActivity.getApplicationContext(), windowManager);
+        SplitScreenModeChecker splitScreenModeChecker = new SplitScreenModeChecker() {
+            @NonNull
+            @Override
+            public SplitScreenMode getSplitScreenMode() {
+                return SplitScreenMode.NONE;
+            }
+        };
+        DesktopWindowModeChecker desktopWindowModeChecker = new DesktopWindowModeChecker() {
+            @Override
+            public boolean isSupported() {
+                return false;
+            }
+        };
+        mDragZoneFactory = new DragZoneFactory(mActivity.getApplicationContext(), deviceConfig,
+                splitScreenModeChecker, desktopWindowModeChecker);
+        mBubbleDragZoneChangedListener = new BubbleDragZoneChangedListener();
+        mDropTargetManager = new DropTargetManager(mActivity.getApplicationContext(),
+                dropTargetParent, mBubbleDragZoneChangedListener);
     }
 
     /**
@@ -130,47 +166,89 @@
                         }
                     };
 
+            private BubbleBarLocation getBubbleBarLocationDuringDrag() {
+                return BubbleAnythingFlagHelper.enableBubbleToFullscreen()
+                        ? mBubbleDragZoneChangedListener.mBubbleBarLocation
+                        : mReleasedLocation;
+            }
+
             @Override
             void onDragStart() {
-                mBubblePinController.setListener(mLocationChangeListener);
                 mBubbleBarViewController.onBubbleDragStart(bubbleView);
-                mBubblePinController.onDragStart(
-                        mBubbleBarViewController.getBubbleBarLocation().isOnLeft(
-                                bubbleView.isLayoutRtl()));
+                if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+                    DraggedObject.Bubble draggedBubble =
+                            new DraggedObject.Bubble(
+                                    mBubbleBarViewController.getBubbleBarLocation());
+                    mDropTargetManager.onDragStarted(draggedBubble,
+                            mDragZoneFactory.createSortedDragZones(draggedBubble));
+                } else {
+                    mBubblePinController.setListener(mLocationChangeListener);
+                    mBubblePinController.onDragStart(
+                            mBubbleBarViewController.getBubbleBarLocation().isOnLeft(
+                                    bubbleView.isLayoutRtl()));
+                }
             }
 
             @Override
             protected void onDragUpdate(float x, float y, float newTx, float newTy) {
                 bubbleView.setDragTranslationX(newTx);
                 bubbleView.setTranslationY(newTy);
-                mBubblePinController.onDragUpdate(x, y);
+                if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+                    mDropTargetManager.onDragUpdated((int) x, (int) y);
+                } else {
+                    mBubblePinController.onDragUpdate(x, y);
+                }
             }
 
             @Override
             protected void onDragRelease() {
-                mBubblePinController.onDragEnd();
-                mBubbleBarViewController.onBubbleDragRelease(mReleasedLocation);
+                if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+                    mDropTargetManager.onDragEnded();
+                    if (!mBubbleDragZoneChangedListener.isDraggedToFullscreen()) {
+                        // TODO b/393173014: check for desktop window and split once they're
+                        //  implemented. this notifies wm shell that the dragged bubble was
+                        //  released so that we can show the expanded view. we only want to show it
+                        //  after releasing in a Bubble zone. But Split and Desktop Window aren't
+                        //  implemented yet, so we only check for full screen for now.
+                        mBubbleBarViewController.onBubbleDragRelease(
+                                getBubbleBarLocationDuringDrag());
+                    }
+                } else {
+                    mBubblePinController.onDragEnd();
+                    mBubbleBarViewController.onBubbleDragRelease(getBubbleBarLocationDuringDrag());
+                }
             }
 
             @Override
             protected void onDragDismiss() {
-                mBubblePinController.onDragEnd();
+                if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+                    mDropTargetManager.onDragEnded();
+                } else {
+                    mBubblePinController.onDragEnd();
+                }
                 mBubbleBarViewController.onBubbleDismissed(bubbleView);
                 mBubbleBarViewController.onBubbleDragEnd();
             }
 
             @Override
             void onDragEnd() {
-                mBubbleBarController.updateBubbleBarLocation(mReleasedLocation,
+                mBubbleBarController.updateBubbleBarLocation(getBubbleBarLocationDuringDrag(),
                         BubbleBarLocation.UpdateSource.DRAG_BUBBLE);
+                if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+                    mDropTargetManager.onDragEnded();
+                    if (mBubbleDragZoneChangedListener.isDraggedToFullscreen()) {
+                        mBubbleBarViewController.moveBubbleToFullscreen(bubbleView);
+                    }
+                } else {
+                    mBubblePinController.setListener(null);
+                }
                 mBubbleBarViewController.onBubbleDragEnd();
-                mBubblePinController.setListener(null);
             }
 
             @Override
             protected PointF getRestingPosition() {
                 return mBubbleBarViewController.getDraggedBubbleReleaseTranslation(
-                        getInitialPosition(), mReleasedLocation);
+                        getInitialPosition(), getBubbleBarLocationDuringDrag());
             }
         });
     }
@@ -188,6 +266,12 @@
             private final LocationChangeListener mLocationChangeListener =
                     location -> mReleasedLocation = location;
 
+            private BubbleBarLocation getBubbleBarLocationDuringDrag() {
+                return BubbleAnythingFlagHelper.enableBubbleToFullscreen()
+                        ? mBubbleDragZoneChangedListener.mBubbleBarLocation
+                        : mReleasedLocation;
+            }
+
             @Override
             protected boolean onTouchDown(@NonNull View view, @NonNull MotionEvent event) {
                 if (bubbleBarView.isExpanded()) return false;
@@ -196,50 +280,74 @@
 
             @Override
             void onDragStart() {
-                mBubbleBarPinController.setListener(mLocationChangeListener);
                 initialRelativePivot.set(bubbleBarView.getRelativePivotX(),
                         bubbleBarView.getRelativePivotY());
                 // By default the bubble bar view pivot is in bottom right corner, while dragging
                 // it should be centered in order to align it with the dismiss target view
                 bubbleBarView.setRelativePivot(/* x = */ 0.5f, /* y = */ 0.5f);
                 bubbleBarView.setIsDragging(true);
-                mBubbleBarPinController.onDragStart(
-                        bubbleBarView.getBubbleBarLocation().isOnLeft(bubbleBarView.isLayoutRtl()));
+                if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+                    DraggedObject.BubbleBar draggedBubbleBar = new DraggedObject.BubbleBar(
+                            mBubbleBarViewController.getBubbleBarLocation());
+                    mDropTargetManager.onDragStarted(draggedBubbleBar,
+                            mDragZoneFactory.createSortedDragZones(draggedBubbleBar));
+                } else {
+                    mBubbleBarPinController.setListener(mLocationChangeListener);
+                    mBubbleBarPinController.onDragStart(
+                            bubbleBarView.getBubbleBarLocation().isOnLeft(
+                                    bubbleBarView.isLayoutRtl()));
+                }
             }
 
             @Override
             protected void onDragUpdate(float x, float y, float newTx, float newTy) {
                 bubbleBarView.setTranslationX(newTx);
                 bubbleBarView.setTranslationY(newTy);
-                mBubbleBarPinController.onDragUpdate(x, y);
+                if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+                    mDropTargetManager.onDragUpdated((int) x, (int) y);
+                } else {
+                    mBubbleBarPinController.onDragUpdate(x, y);
+                }
             }
 
             @Override
             protected void onDragRelease() {
-                mBubbleBarPinController.onDragEnd();
+                if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+                    mDropTargetManager.onDragEnded();
+                } else {
+                    mBubbleBarPinController.onDragEnd();
+                }
             }
 
             @Override
             protected void onDragDismiss() {
-                mBubbleBarPinController.onDragEnd();
+                if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+                    mDropTargetManager.onDragEnded();
+                } else {
+                    mBubbleBarPinController.onDragEnd();
+                }
             }
 
             @Override
             void onDragEnd() {
                 // Make sure to update location as the first thing. Pivot update causes a relayout
-                mBubbleBarController.updateBubbleBarLocation(mReleasedLocation,
+                mBubbleBarController.updateBubbleBarLocation(getBubbleBarLocationDuringDrag(),
                         BubbleBarLocation.UpdateSource.DRAG_BAR);
                 bubbleBarView.setIsDragging(false);
                 // Restoring the initial pivot for the bubble bar view
                 bubbleBarView.setRelativePivot(initialRelativePivot.x, initialRelativePivot.y);
                 mBubbleBarViewController.onBubbleBarDragEnd();
-                mBubbleBarPinController.setListener(null);
+                if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+                    mDropTargetManager.onDragEnded();
+                } else {
+                    mBubbleBarPinController.setListener(null);
+                }
             }
 
             @Override
             protected PointF getRestingPosition() {
                 return mBubbleBarViewController.getBubbleBarDragReleaseTranslation(
-                        getInitialPosition(), mReleasedLocation);
+                        getInitialPosition(), getBubbleBarLocationDuringDrag());
             }
         });
     }
@@ -479,8 +587,17 @@
                 mAnimator.animateDismiss(mViewInitialPosition, onComplete);
             } else {
                 onDragRelease();
-                mAnimator.animateToRestingState(getRestingPosition(), getCurrentVelocity(),
+                if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+                    if (mBubbleDragZoneChangedListener.isDraggedToFullscreen()) {
+                        onComplete.run();
+                    } else {
+                        mAnimator.animateToRestingState(getRestingPosition(), getCurrentVelocity(),
+                                onComplete);
+                    }
+                } else {
+                    mAnimator.animateToRestingState(getRestingPosition(), getCurrentVelocity(),
                         onComplete);
+                }
             }
             mBubbleDismissController.hideDismissView();
         }
@@ -520,4 +637,46 @@
             return new PointF(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
         }
     }
+
+    private class BubbleDragZoneChangedListener implements DragZoneChangedListener {
+
+        private BubbleBarLocation mBubbleBarLocation = BubbleBarLocation.DEFAULT;
+        private DragZone mDragZone;
+
+        boolean isDraggedToFullscreen() {
+            return mDragZone instanceof DragZone.FullScreen;
+        }
+
+        @Override
+        public void onInitialDragZoneSet(@NonNull DragZone dragZone) {
+            mDragZone = dragZone;
+            if (dragZone instanceof DragZone.Bubble.Left) {
+                mBubbleBarLocation = BubbleBarLocation.LEFT;
+            } else if (dragZone instanceof DragZone.Bubble.Right) {
+                mBubbleBarLocation = BubbleBarLocation.RIGHT;
+            }
+        }
+
+        @Override
+        public void onDragZoneChanged(@NonNull DraggedObject draggedObject, @NonNull DragZone from,
+                @NonNull DragZone to) {
+            mDragZone = to;
+            if (to instanceof DragZone.Bubble.Left
+                    && mBubbleBarLocation != BubbleBarLocation.LEFT) {
+                if (draggedObject instanceof DraggedObject.Bubble) {
+                    mBubbleBarController.animateBubbleBarLocation(BubbleBarLocation.LEFT);
+                }
+                mBubbleBarLocation = BubbleBarLocation.LEFT;
+            } else if (to instanceof DragZone.Bubble.Right
+                    && mBubbleBarLocation != BubbleBarLocation.RIGHT) {
+                if (draggedObject instanceof DraggedObject.Bubble) {
+                    mBubbleBarController.animateBubbleBarLocation(BubbleBarLocation.RIGHT);
+                }
+                mBubbleBarLocation = BubbleBarLocation.RIGHT;
+            }
+        }
+
+        @Override
+        public void onDragEnded(@NonNull DragZone zone) {}
+    }
 }
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 30cfafe..0da8c1f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
@@ -215,6 +215,7 @@
         animator.spring(DynamicAnimation.TRANSLATION_Y, totalTranslationY, initialVelocity ?: 0f)
         animator.addUpdateListener { handle, values ->
             val ty = values[DynamicAnimation.TRANSLATION_Y]?.value ?: return@addUpdateListener
+            if (animatingBubble == null) return@addUpdateListener
             when {
                 ty >= stashedHandleTranslationYForAnimation -> {
                     // we're in the first leg of the animation. only animate the handle. the bubble
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
index 4932654..82b1295 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
@@ -32,7 +32,6 @@
 import com.android.launcher3.config.FeatureFlags.enableTaskbarPinning
 import com.android.launcher3.taskbar.TaskbarActivityContext
 import com.android.launcher3.taskbar.TaskbarViewCallbacks
-import com.android.launcher3.util.DisplayController
 import com.android.launcher3.util.Executors.MAIN_EXECUTOR
 import com.android.launcher3.views.ActivityContext
 import com.android.launcher3.views.IconButtonView
@@ -69,7 +68,7 @@
             )
         backgroundTintList = ColorStateList.valueOf(TRANSPARENT)
         setIconDrawable(drawable)
-        if (!DisplayController.isTransientTaskbar(context)) {
+        if (activityContext.isTransientTaskbar) {
             setPadding(dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconPadding.toFloat()))
         }
         setForegroundTint(activityContext.getColor(R.color.all_apps_button_color))
@@ -106,7 +105,7 @@
 
     @DimenRes
     fun getAllAppsButtonTranslationXOffset(isTransientTaskbar: Boolean): Int {
-        return if (isTransientTaskbar) {
+        return if (isTransientTaskbar && activityContext.isTransientTaskbar) {
             R.dimen.transient_taskbar_all_apps_button_translation_x_offset
         } else {
             R.dimen.taskbar_all_apps_search_button_translation_x_offset
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
index d5f72d5..060ce46 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
@@ -26,7 +26,6 @@
 import com.android.launcher3.Utilities.dpToPx
 import com.android.launcher3.taskbar.TaskbarActivityContext
 import com.android.launcher3.taskbar.TaskbarViewCallbacks
-import com.android.launcher3.util.DisplayController
 import com.android.launcher3.views.ActivityContext
 import com.android.launcher3.views.IconButtonView
 
@@ -52,7 +51,7 @@
         backgroundTintList = ColorStateList.valueOf(TRANSPARENT)
         val drawable = resources.getDrawable(R.drawable.taskbar_divider_button)
         setIconDrawable(drawable)
-        if (!DisplayController.isTransientTaskbar(context)) {
+        if (!activityContext.isTransientTaskbar) {
             setPadding(dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconPadding.toFloat()))
         }
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
index f130d29..a14c5a4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
@@ -19,7 +19,6 @@
 import com.android.launcher3.Flags.enableRecentsInTaskbar
 import com.android.launcher3.config.FeatureFlags.enableTaskbarPinning
 import com.android.launcher3.taskbar.TaskbarActivityContext
-import com.android.launcher3.util.DisplayController
 
 /** Evaluates all the features taskbar can have. */
 class TaskbarFeatureEvaluator
@@ -36,7 +35,7 @@
         get() = enableTaskbarPinning() || isRecentsEnabled
 
     val isTransient: Boolean
-        get() = DisplayController.isTransientTaskbar(taskbarActivityContext)
+        get() = taskbarActivityContext.isTransientTaskbar
 
     val isLandscape: Boolean
         get() = taskbarActivityContext.deviceProfile.isLandscape
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt
index 822ca64..f1ed6c5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt
@@ -26,6 +26,8 @@
     numColumns: Int = taskbarActivityContext.deviceProfile.inv.numColumns,
 ) {
     var taskbarIconSize: TaskbarIconSize = getIconSizeByGrid(numColumns, numRows)
+    val numShownHotseatIcons
+        get() = taskbarActivityContext.deviceProfile.numShownHotseatIcons
 
     // TODO(b/341146605) : initialize it to taskbar container in later cl.
     private var taskbarContainer: List<TaskbarContainer> = emptyList()
diff --git a/quickstep/src/com/android/launcher3/taskbar/growth/GrowthConstants.java b/quickstep/src/com/android/launcher3/taskbar/growth/GrowthConstants.java
new file mode 100644
index 0000000..78ef152
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/growth/GrowthConstants.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar.growth;
+
+/**
+ * Constants for registering Growth framework.
+ */
+public final class GrowthConstants {
+    /**
+     * For Taskbar broadcast intent filter.
+     */
+    public static final String BROADCAST_SHOW_NUDGE =
+            "com.android.launcher3.growth.BROADCAST_SHOW_NUDGE";
+    private GrowthConstants() {}
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/growth/NudgePayload.kt b/quickstep/src/com/android/launcher3/taskbar/growth/NudgePayload.kt
new file mode 100644
index 0000000..7498cbc
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/growth/NudgePayload.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar.growth
+
+sealed interface Action {
+    data class Dismiss(
+        val markAsDismissed: Boolean = true,
+        val dismissRetentionInDays: Int? = null,
+    ) : Action
+
+    data class OpenUrl(val url: String) : Action
+}
+
+sealed class Image {
+    data class ResourceId(val resId: Int) : Image()
+
+    data class Url(val url: String) : Image()
+}
+
+data class ButtonPayload(val label: String, val actions: List<Action>)
+
+data class NudgePayload(
+    val titleText: String,
+    val bodyText: String,
+    val image: Image?,
+    val primaryButton: ButtonPayload?,
+    val secondaryButton: ButtonPayload?,
+
+    // TODO: b/396223717 - add anchoring information.
+)
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
index 64cc47c..fdb5315 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
@@ -18,12 +18,11 @@
 import android.content.Context;
 import android.view.View;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
-import com.android.launcher3.dot.DotInfo;
-import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.taskbar.BaseTaskbarContext;
 import com.android.launcher3.taskbar.TaskbarActivityContext;
@@ -56,7 +55,7 @@
             Context windowContext,
             TaskbarActivityContext taskbarContext,
             TaskbarControllers controllers) {
-        super(windowContext);
+        super(windowContext, taskbarContext.isPrimaryDisplay());
         mTaskbarContext = taskbarContext;
         mOverlayController = controllers.taskbarOverlayController;
         mDragController = new TaskbarDragController(this);
@@ -65,6 +64,7 @@
         mStashedTaskbarHeight = controllers.taskbarStashController.getStashedHeight();
 
         mUiController = controllers.uiController;
+        onViewCreated();
     }
 
     public @Nullable TaskbarSearchSessionController getSearchSessionController() {
@@ -116,11 +116,6 @@
     }
 
     @Override
-    public boolean isBindingItems() {
-        return mTaskbarContext.isBindingItems();
-    }
-
-    @Override
     public View.OnClickListener getItemOnClickListener() {
         return mTaskbarContext.getItemOnClickListener();
     }
@@ -130,6 +125,7 @@
         return mDragController::startDragOnLongClick;
     }
 
+    @NonNull
     @Override
     public PopupDataProvider getPopupDataProvider() {
         return mTaskbarContext.getPopupDataProvider();
@@ -141,11 +137,6 @@
     }
 
     @Override
-    public DotInfo getDotInfoForItem(ItemInfo info) {
-        return mTaskbarContext.getDotInfoForItem(info);
-    }
-
-    @Override
     public void onDragStart() {}
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
index 669850c..41694ec 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
@@ -34,7 +34,6 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.BaseDragLayer;
 
@@ -147,7 +146,7 @@
      * 2) Sets tappableInsets bottom inset to 0.
      */
     private WindowInsets updateInsetsDueToStashing(WindowInsets oldInsets) {
-        if (!DisplayController.isTransientTaskbar(mContainer)) {
+        if (!mContainer.isTransientTaskbar()) {
             return oldInsets;
         }
         WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 019e746..cd0a4f3 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -48,7 +48,6 @@
 import static com.android.launcher3.popup.SystemShortcut.BUBBLE_SHORTCUT;
 import static com.android.launcher3.popup.SystemShortcut.DONT_SUGGEST_APP;
 import static com.android.launcher3.popup.SystemShortcut.INSTALL;
-import static com.android.launcher3.popup.SystemShortcut.PIN_UNPIN_ITEM;
 import static com.android.launcher3.popup.SystemShortcut.PRIVATE_PROFILE_INSTALL;
 import static com.android.launcher3.popup.SystemShortcut.UNINSTALL_APP;
 import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
@@ -85,6 +84,7 @@
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.Display;
 import android.view.HapticFeedbackConstants;
@@ -117,6 +117,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.allapps.AllAppsRecyclerView;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.apppairs.AppPairIcon;
@@ -166,8 +167,10 @@
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
+import com.android.launcher3.util.StableViewInfo;
 import com.android.launcher3.util.StartActivityParams;
 import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.FloatingIconView;
 import com.android.launcher3.widget.LauncherWidgetHolder;
 import com.android.quickstep.OverviewCommandHelper;
 import com.android.quickstep.OverviewComponentObserver;
@@ -475,9 +478,6 @@
         List<SystemShortcut.Factory> shortcuts = new ArrayList(Arrays.asList(
                 APP_INFO, WellbeingModel.SHORTCUT_FACTORY, mHotseatPredictionController));
 
-        if (Flags.enablePinningAppWithContextMenu()) {
-            shortcuts.add(0, PIN_UNPIN_ITEM);
-        }
         shortcuts.addAll(getSplitShortcuts());
         shortcuts.add(WIDGETS);
         shortcuts.add(INSTALL);
@@ -1274,7 +1274,7 @@
         options.setPendingIntentBackgroundActivityStartMode(
                 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
 
-        IRemoteCallback endCallback = completeRunnableListCallback(callbacks);
+        IRemoteCallback endCallback = completeRunnableListCallback(callbacks, this);
         options.setOnAnimationAbortListener(endCallback);
         options.setOnAnimationFinishedListener(endCallback);
         return new ActivityOptionsWrapper(options, callbacks);
@@ -1380,7 +1380,8 @@
         SystemUiProxy.INSTANCE.get(this).setLauncherAppIconSize(mDeviceProfile.iconSizePx);
         TaskbarManager taskbarManager = mTISBindHelper.getTaskbarManager();
         if (taskbarManager != null) {
-            taskbarManager.debugTaskbarManager("QuickstepLauncher#onDeviceProfileChanged");
+            taskbarManager.debugPrimaryTaskbar("QuickstepLauncher#onDeviceProfileChanged",
+                    true);
         }
     }
 
@@ -1454,6 +1455,45 @@
         mBubbleBarLocation = bubbleBarLocation;
     }
 
+    /**
+     * Similar to {@link #getFirstHomeElementForAppClose} but also matches all apps if its visible
+     */
+    @Nullable
+    public View getFirstVisibleElementForAppClose(
+            @Nullable StableViewInfo svi, String packageName, UserHandle user) {
+        if (isInState(LauncherState.ALL_APPS)) {
+            AllAppsRecyclerView activeRecyclerView = getAppsView().getActiveRecyclerView();
+            View v = null;
+            if (svi != null) {
+                // Preferred item match
+                v = activeRecyclerView.findViewByPredicate(view ->
+                        view.isAggregatedVisible()
+                                && view.getTag() instanceof ItemInfo info && svi.matches(info));
+            }
+            if (v == null) {
+                // Package user match
+                v = activeRecyclerView.findViewByPredicate(view ->
+                        view.isAggregatedVisible() && view.getTag() instanceof ItemInfo info
+                                && info.itemType == ITEM_TYPE_APPLICATION
+                                && info.user.equals(user)
+                                && TextUtils.equals(info.getTargetPackage(), packageName));
+            }
+
+            if (v != null && activeRecyclerView.computeVerticalScrollOffset() > 0) {
+                RectF locationBounds = new RectF();
+                FloatingIconView.getLocationBoundsForView(this, v, false, locationBounds,
+                        new Rect());
+                if (locationBounds.top < getAppsView().getHeaderBottom()) {
+                    // Icon is covered by scrim, return null to play fallback animation.
+                    return null;
+                }
+            }
+            return v;
+        }
+
+        return getFirstHomeElementForAppClose(svi, packageName, user);
+    }
+
     @Override
     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
         super.dump(prefix, fd, writer, args);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
index cca8bf8..23dc81d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
@@ -27,18 +27,17 @@
 import com.android.launcher3.anim.AnimatorListeners.forSuccessCallback
 import com.android.launcher3.anim.PendingAnimation
 import com.android.launcher3.anim.PropertySetter
-import com.android.launcher3.logging.StatsLogManager.LauncherEvent
 import com.android.launcher3.statemanager.StateManager.StateHandler
 import com.android.launcher3.states.StateAnimationConfig
 import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE
 import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE
 import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL
 import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE
-import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE
 import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X
 import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y
 import com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW
 import com.android.quickstep.util.AnimUtils
+import com.android.quickstep.views.AddDesktopButton
 import com.android.quickstep.views.ClearAllButton
 import com.android.quickstep.views.RecentsView
 import com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET
@@ -77,10 +76,7 @@
             if (state.displayOverviewTasksAsGrid(launcher.deviceProfile)) 1f else 0f,
         )
         if (enableDesktopExplodedView()) {
-            DESK_EXPLODE_PROGRESS.set(
-                recentsView,
-                if (state.displayOverviewTasksAsGrid(launcher.deviceProfile)) 1f else 0f,
-            )
+            DESK_EXPLODE_PROGRESS.set(recentsView, if (state.showExplodedDesktopView()) 1f else 0f)
         }
 
         TASK_THUMBNAIL_SPLASH_ALPHA.set(
@@ -169,7 +165,7 @@
             builder.setFloat(
                 recentsView,
                 DESK_EXPLODE_PROGRESS,
-                if (toState.isRecentsViewVisible) 1f else 0f,
+                if (toState.showExplodedDesktopView()) 1f else 0f,
                 getOverviewInterpolator(fromState, toState),
             )
         }
@@ -226,8 +222,8 @@
         builder: PendingAnimation,
         animate: Boolean,
     ) {
-        val goingToOverviewFromWorkspaceContextual = toState == LauncherState.OVERVIEW &&
-                launcher.isSplitSelectionActive
+        val goingToOverviewFromWorkspaceContextual =
+            toState == LauncherState.OVERVIEW && launcher.isSplitSelectionActive
         if (
             toState != LauncherState.OVERVIEW_SPLIT_SELECT &&
                 !goingToOverviewFromWorkspaceContextual
@@ -302,6 +298,14 @@
             overviewButtonAlpha,
             config.getInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, LINEAR),
         )
+        recentsView.addDeskButton?.let {
+            propertySetter.setFloat(
+                it,
+                AddDesktopButton.VISIBILITY_ALPHA,
+                if (state.areElementsVisible(launcher, LauncherState.ADD_DESK_BUTTON)) 1f else 0f,
+                LINEAR,
+            )
+        }
     }
 
     private fun getOverviewInterpolator(fromState: LauncherState, toState: LauncherState) =
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index ca388c6..a5b1ee7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -77,7 +77,8 @@
         return super.getVisibleElements(launcher)
                 & ~OVERVIEW_ACTIONS
                 & ~CLEAR_ALL_BUTTON
-                & ~VERTICAL_SWIPE_INDICATOR;
+                & ~VERTICAL_SWIPE_INDICATOR
+                & ~ADD_DESK_BUTTON;
     }
 
     @Override
@@ -96,6 +97,11 @@
     }
 
     @Override
+    public boolean showExplodedDesktopView() {
+        return false;
+    }
+
+    @Override
     protected float getDepthUnchecked(Context context) {
         if (Launcher.getLauncher(context).areDesktopTasksVisible()) {
             // Don't blur the background while desktop tasks are visible
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index 80fc5fa..0c0b4fd 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -45,7 +45,7 @@
 
     @Override
     public int getVisibleElements(Launcher launcher) {
-        return OVERVIEW_ACTIONS | CLEAR_ALL_BUTTON;
+        return OVERVIEW_ACTIONS;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 15216fe..963504f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.uioverrides.states;
 
 import static com.android.app.animation.Interpolators.DECELERATE_2;
+import static com.android.launcher3.Flags.enableDesktopExplodedView;
 import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
 
@@ -110,7 +111,7 @@
 
     @Override
     public int getVisibleElements(Launcher launcher) {
-        int elements = CLEAR_ALL_BUTTON | OVERVIEW_ACTIONS;
+        int elements = CLEAR_ALL_BUTTON | OVERVIEW_ACTIONS | ADD_DESK_BUTTON;
         DeviceProfile dp = launcher.getDeviceProfile();
         boolean showFloatingSearch;
         if (dp.isPhone) {
@@ -124,7 +125,7 @@
             elements |= FLOATING_SEARCH_BAR;
         }
         if (launcher.isSplitSelectionActive()) {
-            elements &= ~CLEAR_ALL_BUTTON;
+            elements &= ~CLEAR_ALL_BUTTON & ~ADD_DESK_BUTTON;
         }
         return elements;
     }
@@ -171,6 +172,11 @@
     }
 
     @Override
+    public boolean showExplodedDesktopView() {
+        return enableDesktopExplodedView();
+    }
+
+    @Override
     public boolean disallowTaskbarGlobalDrag() {
         // Disable global drag in overview
         return true;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewDismissTouchController.kt b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewDismissTouchController.kt
index 88b7155..454a307 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewDismissTouchController.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewDismissTouchController.kt
@@ -36,7 +36,6 @@
 import com.android.quickstep.views.TaskView
 import com.google.android.msdl.data.model.MSDLToken
 import kotlin.math.abs
-import kotlin.math.sign
 
 /** Touch controller for handling task view card dismiss swipes */
 class TaskViewDismissTouchController<CONTAINER>(
@@ -53,6 +52,8 @@
             recentsView.pagedOrientationHandler.upDownSwipeDirection,
         )
     private val isRtl = isRtl(container.resources)
+    private val upDirection: Int = recentsView.pagedOrientationHandler.getUpDirection(isRtl)
+
     private val tempTaskThumbnailBounds = Rect()
 
     private var taskBeingDragged: TaskView? = null
@@ -96,7 +97,11 @@
         }
 
         onControllerTouchEvent(ev)
-        return detector.isDraggingState && detector.wasInitialTouchPositive()
+        val upDirectionIsPositive = upDirection == SingleAxisSwipeDetector.DIRECTION_POSITIVE
+        val wasInitialTouchUp =
+            (upDirectionIsPositive && detector.wasInitialTouchPositive()) ||
+                (!upDirectionIsPositive && !detector.wasInitialTouchPositive())
+        return detector.isDraggingState && wasInitialTouchUp
     }
 
     override fun onControllerTouchEvent(ev: MotionEvent?): Boolean = detector.onTouchEvent(ev)
@@ -107,25 +112,27 @@
         if (!canInterceptTouch(ev)) {
             return false
         }
-
         taskBeingDragged =
             recentsView.taskViews
                 .firstOrNull {
                     recentsView.isTaskViewVisible(it) && container.dragLayer.isEventOverView(it, ev)
                 }
                 ?.also {
+                    val secondaryLayerDimension =
+                        recentsView.pagedOrientationHandler.getSecondaryDimension(
+                            container.dragLayer
+                        )
                     // Dismiss length as bottom of task so it is fully off screen when dismissed.
                     it.getThumbnailBounds(tempTaskThumbnailBounds, relativeToDragLayer = true)
-                    dismissLength = tempTaskThumbnailBounds.bottom
+                    dismissLength =
+                        recentsView.pagedOrientationHandler.getTaskDismissLength(
+                            secondaryLayerDimension,
+                            tempTaskThumbnailBounds,
+                        )
                     verticalFactor =
-                        recentsView.pagedOrientationHandler.secondaryTranslationDirectionFactor
+                        recentsView.pagedOrientationHandler.getTaskDismissVerticalDirection()
                 }
-
-        detector.setDetectableScrollConditions(
-            recentsView.pagedOrientationHandler.getUpDirection(isRtl),
-            /* ignoreSlop = */ false,
-        )
-
+        detector.setDetectableScrollConditions(upDirection, /* ignoreSlop= */ false)
         return true
     }
 
@@ -148,8 +155,8 @@
             boundToRange(abs(currentDisplacement), 0f, dismissLength.toFloat())
         // When swiping below origin, allow slight undershoot to simulate resisting the movement.
         val totalDisplacement =
-            if (isDisplacementPositiveDirection(currentDisplacement))
-                boundedDisplacement * sign(currentDisplacement)
+            if (recentsView.pagedOrientationHandler.isGoingUp(currentDisplacement, isRtl))
+                boundedDisplacement * verticalFactor
             else
                 mapToRange(
                     boundedDisplacement,
@@ -158,7 +165,7 @@
                     0f,
                     container.resources.getDimension(R.dimen.task_dismiss_max_undershoot),
                     DECELERATE,
-                )
+                ) * -verticalFactor
         taskBeingDragged.secondaryDismissTranslationProperty.setValue(
             taskBeingDragged,
             totalDisplacement,
@@ -207,8 +214,9 @@
         }
         val isBeyondDismissThreshold =
             abs(currentDisplacement) > abs(DISMISS_THRESHOLD_FRACTION * dismissLength)
-        val isFlingingTowardsDismiss = detector.isFling(velocity) && velocity < 0
-        val isFlingingTowardsRestState = detector.isFling(velocity) && velocity > 0
+        val velocityIsGoingUp = recentsView.pagedOrientationHandler.isGoingUp(velocity, isRtl)
+        val isFlingingTowardsDismiss = detector.isFling(velocity) && velocityIsGoingUp
+        val isFlingingTowardsRestState = detector.isFling(velocity) && !velocityIsGoingUp
         val isDismissing =
             isFlingingTowardsDismiss || (isBeyondDismissThreshold && !isFlingingTowardsRestState)
         springAnimation =
@@ -232,10 +240,6 @@
             }
     }
 
-    // Returns if the current task being dragged is towards "positive" (e.g. dismissal).
-    private fun isDisplacementPositiveDirection(displacement: Float): Boolean =
-        sign(displacement) == sign(verticalFactor.toFloat())
-
     private fun clearState() {
         detector.finishedScrolling()
         detector.setDetectableScrollConditions(0, false)
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewLaunchTouchController.kt b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewLaunchTouchController.kt
index c740dad..8ee552d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewLaunchTouchController.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewLaunchTouchController.kt
@@ -53,6 +53,7 @@
             recentsView.pagedOrientationHandler.upDownSwipeDirection,
         )
     private val isRtl = isRtl(container.resources)
+    private val downDirection = recentsView.pagedOrientationHandler.getDownDirection(isRtl)
 
     private var taskBeingDragged: TaskView? = null
     private var launchEndDisplacement: Float = 0f
@@ -104,7 +105,11 @@
             }
         }
         onControllerTouchEvent(ev)
-        return detector.isDraggingState && !detector.wasInitialTouchPositive()
+        val downDirectionIsNegative = downDirection == SingleAxisSwipeDetector.DIRECTION_NEGATIVE
+        val wasInitialTouchDown =
+            (downDirectionIsNegative && !detector.wasInitialTouchPositive()) ||
+                (!downDirectionIsNegative && detector.wasInitialTouchPositive())
+        return detector.isDraggingState && wasInitialTouchDown
     }
 
     override fun onControllerTouchEvent(ev: MotionEvent) = detector.onTouchEvent(ev)
@@ -120,15 +125,12 @@
                 }
                 ?.also {
                     verticalFactor =
-                        recentsView.pagedOrientationHandler.secondaryTranslationDirectionFactor
+                        recentsView.pagedOrientationHandler.getTaskDragDisplacementFactor(isRtl)
                 }
         if (!canTaskLaunchTaskView(taskBeingDragged)) {
             return false
         }
-        detector.setDetectableScrollConditions(
-            recentsView.pagedOrientationHandler.getDownDirection(isRtl),
-            /* ignoreSlop = */ false,
-        )
+        detector.setDetectableScrollConditions(downDirection, /* ignoreSlop= */ false)
         return true
     }
 
@@ -143,7 +145,10 @@
             recentsView.createTaskLaunchAnimation(taskBeingDragged, maxDuration, ZOOM_IN)
         // Since the thumbnail is what is filling the screen, based the end displacement on it.
         taskBeingDragged.getThumbnailBounds(tempRect, /* relativeToDragLayer= */ true)
-        launchEndDisplacement = (secondaryLayerDimension - tempRect.bottom).toFloat()
+        launchEndDisplacement =
+            recentsView.pagedOrientationHandler
+                .getTaskLaunchLength(secondaryLayerDimension, tempRect)
+                .toFloat() * verticalFactor
         playbackController =
             pendingAnimation.createPlaybackController()?.apply {
                 taskViewRecentsTouchContext.onUserControlledAnimationCreated(this)
@@ -163,8 +168,9 @@
 
         val isBeyondLaunchThreshold =
             abs(playbackController.progressFraction) > abs(LAUNCH_THRESHOLD_FRACTION)
-        val isFlingingTowardsLaunch = detector.isFling(velocity) && velocity > 0
-        val isFlingingTowardsRestState = detector.isFling(velocity) && velocity < 0
+        val velocityIsNegative = !recentsView.pagedOrientationHandler.isGoingUp(velocity, isRtl)
+        val isFlingingTowardsLaunch = detector.isFling(velocity) && velocityIsNegative
+        val isFlingingTowardsRestState = detector.isFling(velocity) && !velocityIsNegative
         val isLaunching =
             isFlingingTowardsLaunch || (isBeyondLaunchThreshold && !isFlingingTowardsRestState)
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchControllerDeprecated.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchControllerDeprecated.java
index b1a36c7..f26bd13 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchControllerDeprecated.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchControllerDeprecated.java
@@ -239,7 +239,7 @@
             pa = new PendingAnimation(maxDuration);
             mRecentsView.createTaskDismissAnimation(pa, mTaskBeingDragged,
                     true /* animateTaskView */, true /* removeTask */, maxDuration,
-                    false /* dismissingForSplitSelection*/);
+                    false /* dismissingForSplitSelection*/, false /* isExpressiveDismiss */);
 
             mEndDisplacement = -secondaryTaskDimension;
         } else {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index c51f659..7574c7f 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -31,6 +31,7 @@
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
 import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
 import static com.android.launcher3.Flags.enableAdditionalHomeAnimations;
+import static com.android.launcher3.Flags.enableGestureNavHorizontalTouchSlop;
 import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
 import static com.android.launcher3.Flags.msdlFeedback;
 import static com.android.launcher3.PagedView.INVALID_PAGE;
@@ -102,6 +103,7 @@
 import android.window.TransitionInfo;
 import android.window.WindowAnimationState;
 
+import androidx.annotation.CallSuper;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
@@ -221,6 +223,7 @@
 
     private final Runnable mLauncherOnDestroyCallback = () -> {
         ActiveGestureProtoLogProxy.logLauncherDestroyed();
+        mRecentsView.removeOnScrollChangedListener(mOnRecentsScrollListener);
         mRecentsView = null;
         mContainer = null;
         mStateCallback.clearState(STATE_LAUNCHER_PRESENT);
@@ -313,7 +316,7 @@
      */
     private static final int LOG_NO_OP_PAGE_INDEX = -1;
 
-    protected final TaskAnimationManager mTaskAnimationManager;
+    protected TaskAnimationManager mTaskAnimationManager;
     // Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
     private RunningWindowAnim[] mRunningWindowAnim;
     // Possible second animation running at the same time as mRunningWindowAnim
@@ -410,7 +413,7 @@
         mMSDLPlayerWrapper = msdlPlayerWrapper;
 
         initTransitionEndpoints(mRemoteTargetHandles[0].getTaskViewSimulator()
-                .getOrientationState().getLauncherDeviceProfile());
+                .getOrientationState().getLauncherDeviceProfile(gestureState.getDisplayId()));
         initStateCallbacks();
 
         mIsTransientTaskbar = mDp.isTaskbarPresent
@@ -988,7 +991,8 @@
             // both split and non-split
             RecentsOrientedState orientationState = mRemoteTargetHandles[0].getTaskViewSimulator()
                     .getOrientationState();
-            DeviceProfile dp = orientationState.getLauncherDeviceProfile();
+            DeviceProfile dp = orientationState.getLauncherDeviceProfile(
+                    mGestureState.getDisplayId());
             if (targets.minimizedHomeBounds != null && primaryTaskTarget != null) {
                 Rect overviewStackBounds = mContainerInterface
                         .getOverviewWindowBounds(targets.minimizedHomeBounds, primaryTaskTarget);
@@ -1102,7 +1106,12 @@
     public void onGestureCancelled() {
         updateDisplacement(0);
         mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
-        handleNormalGestureEnd(0, false, new PointF(), true /* isCancel */);
+        handleNormalGestureEnd(
+                /* endVelocityPxPerMs= */ 0,
+                /* isFling= */ false,
+                /* velocityPxPerMs= */ new PointF(),
+                /* isCancel= */ true,
+                /* horizontalTouchSlopPassed= */ false);
     }
 
     /**
@@ -1111,12 +1120,10 @@
      * @param velocityPxPerMs The x and y components of the velocity when the gesture ends.
      */
     @UiThread
-    public void onGestureEnded(float endVelocityPxPerMs, PointF velocityPxPerMs) {
+    public void onGestureEnded(
+            float endVelocityPxPerMs, PointF velocityPxPerMs, boolean horizontalTouchSlopPassed) {
         float flingThreshold = mContext.getResources()
                 .getDimension(R.dimen.quickstep_fling_threshold_speed);
-        Log.d(TAG, "onGestureEnded: mGestureStarted=" + mGestureStarted
-                + ", mIsMotionPaused=" + mIsMotionPaused
-                + ", flingThresholdPassed=" + (Math.abs(endVelocityPxPerMs) > flingThreshold));
         boolean isFling = mGestureStarted && !mIsMotionPaused
                 && Math.abs(endVelocityPxPerMs) > flingThreshold;
         mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
@@ -1127,7 +1134,11 @@
             mLogDirectionUpOrLeft = velocityPxPerMs.x < 0;
         }
         Runnable handleNormalGestureEndCallback = () -> handleNormalGestureEnd(
-                endVelocityPxPerMs, isFling, velocityPxPerMs, /* isCancel= */ false);
+                endVelocityPxPerMs,
+                isFling,
+                velocityPxPerMs,
+                /* isCancel= */ false,
+                horizontalTouchSlopPassed);
         if (mRecentsView != null) {
             mRecentsView.runOnPageScrollsInitialized(handleNormalGestureEndCallback);
         } else {
@@ -1260,27 +1271,30 @@
     }
 
     private GestureEndTarget calculateEndTarget(
-            PointF velocityPxPerMs, float endVelocityPxPerMs, boolean isFlingY, boolean isCancel) {
+            PointF velocityPxPerMs,
+            float endVelocityPxPerMs,
+            boolean isFlingY,
+            boolean isCancel,
+            boolean horizontalTouchSlopPassed) {
         ActiveGestureProtoLogProxy.logOnCalculateEndTarget(
                 dpiFromPx(velocityPxPerMs.x),
                 dpiFromPx(velocityPxPerMs.y),
                 Math.toDegrees(Math.atan2(-velocityPxPerMs.y, velocityPxPerMs.x)));
+
         if (mGestureState.isHandlingAtomicEvent()) {
             // Button mode, this is only used to go to recents.
             return RECENTS;
         }
 
-        Log.d(TAG, "calculateEndTarget: isCancel=" + isCancel + ", isFlingY=" + isFlingY);
         GestureEndTarget endTarget;
         if (isCancel) {
             endTarget = LAST_TASK;
         } else if (isFlingY) {
             endTarget = calculateEndTargetForFlingY(velocityPxPerMs, endVelocityPxPerMs);
         } else {
-            endTarget = calculateEndTargetForNonFling(velocityPxPerMs);
+            endTarget = calculateEndTargetForNonFling(velocityPxPerMs, horizontalTouchSlopPassed);
         }
 
-        Log.d(TAG, "calculateEndTarget: endTarget(1)=" + endTarget);
         if (mDeviceState.isOverviewDisabled() && endTarget == RECENTS) {
             return LAST_TASK;
         }
@@ -1299,7 +1313,6 @@
                 return LAST_TASK;
             }
         }
-        Log.d(TAG, "calculateEndTarget: endTarget(2)=" + endTarget);
         return endTarget;
     }
 
@@ -1308,27 +1321,23 @@
         final boolean willGoToNewTask =
                 isScrollingToNewTask() && Math.abs(velocity.x) > Math.abs(endVelocity);
         final boolean isSwipeUp = endVelocity < 0;
-        Log.d(TAG, "calculateEndTargetForFlingY: willGoToNewTask=" + willGoToNewTask
-                + ", isSwipeUp=" + isSwipeUp);
         if (!isSwipeUp) {
             final boolean isCenteredOnNewTask = mRecentsView != null
                     && mRecentsView.getDestinationPage() != mRecentsView.getRunningTaskIndex();
-            Log.d(TAG, "calculateEndTargetForFlingY: isCenteredOnNewTask=" + isCenteredOnNewTask);
             return willGoToNewTask || isCenteredOnNewTask ? NEW_TASK : LAST_TASK;
         }
 
         return willGoToNewTask ? NEW_TASK : HOME;
     }
 
-    private GestureEndTarget calculateEndTargetForNonFling(PointF velocity) {
+    private GestureEndTarget calculateEndTargetForNonFling(
+            PointF velocity, boolean horizontalTouchSlopPassed) {
         final boolean isScrollingToNewTask = isScrollingToNewTask();
 
         // Fully gestural mode.
         final boolean isFlingX = Math.abs(velocity.x) > mContext.getResources()
-                .getDimension(R.dimen.quickstep_fling_threshold_speed);
-        Log.d(TAG, "calculateEndTargetForNonFling: isScrollingToNewTask=" + isScrollingToNewTask
-                + ", isFlingX=" + isFlingX
-                + ", mIsMotionPaused=" + mIsMotionPaused);
+                .getDimension(R.dimen.quickstep_fling_threshold_speed)
+                && (!enableGestureNavHorizontalTouchSlop() || horizontalTouchSlopPassed);
         if (isScrollingToNewTask && isFlingX) {
             // Flinging towards new task takes precedence over mIsMotionPaused (which only
             // checks y-velocity).
@@ -1338,7 +1347,6 @@
         } else if (isScrollingToNewTask) {
             return NEW_TASK;
         }
-        Log.d(TAG, "calculateEndTargetForNonFling: mCanSlowSwipeGoHome=" + mCanSlowSwipeGoHome);
         return velocity.y < 0 && mCanSlowSwipeGoHome ? HOME : LAST_TASK;
     }
 
@@ -1369,11 +1377,15 @@
 
     @UiThread
     private void handleNormalGestureEnd(
-            float endVelocityPxPerMs, boolean isFling, PointF velocityPxPerMs, boolean isCancel) {
+            float endVelocityPxPerMs,
+            boolean isFling,
+            PointF velocityPxPerMs,
+            boolean isCancel,
+            boolean horizontalTouchSlopPassed) {
         long duration = MAX_SWIPE_DURATION;
         float currentShift = mCurrentShift.value;
         final GestureEndTarget endTarget = calculateEndTarget(
-                velocityPxPerMs, endVelocityPxPerMs, isFling, isCancel);
+                velocityPxPerMs, endVelocityPxPerMs, isFling, isCancel, horizontalTouchSlopPassed);
         // Set the state, but don't notify until the animation completes
         mGestureState.setEndTarget(endTarget, false /* isAtomic */);
         mAnimationFactory.setEndTarget(endTarget);
@@ -1382,7 +1394,7 @@
                 && mIsTransientTaskbar
                 && mContainerInterface.getTaskbarController() != null) {
             mContainerInterface.getTaskbarController()
-                    .setUserIsNotGoingHome(endTarget != GestureState.GestureEndTarget.HOME);
+                    .setUserIsNotGoingHome(endTarget != HOME);
         }
 
         float endShift = endTarget.isLauncher ? 1 : 0;
@@ -2070,7 +2082,7 @@
      * specific edge case: if we switch from A to B, and back to A before B appears, we need to
      * start A again to ensure it stays on top.
      */
-    @androidx.annotation.CallSuper
+    @CallSuper
     protected void onRestartPreviouslyAppearedTask() {
         // Finish the controller here, since we won't get onTaskAppeared() for a task that already
         // appeared.
diff --git a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
index 914855b..4280baf 100644
--- a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
+++ b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
@@ -82,6 +82,7 @@
                             taskKey.numActivities,
                             taskKey.isTopActivityNoDisplay,
                             taskKey.isActivityStackTransparent,
+                            taskKey.userId,
                         ) -> null
 
                         !taskContainer.task.isDockable -> null
diff --git a/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt b/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt
index f610014..9d7f704 100644
--- a/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt
+++ b/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt
@@ -152,6 +152,9 @@
         writer.println("$prefix\tanimateLpnh=$animateLpnh")
         writer.println("$prefix\tshrinkNavHandleOnPress=$shrinkNavHandleOnPress")
         writer.println("$prefix\tlpnhTimeoutMs=$lpnhTimeoutMs")
+        writer.println("$prefix\tenableLpnhTwoStages=$enableLpnhTwoStages")
+        writer.println("$prefix\ttwoStageDurationPercentage=$twoStageDurationPercentage")
+        writer.println("$prefix\ttwoStageSlopPercentage=$twoStageSlopPercentage")
         writer.println("$prefix\tenableLongPressNavHandle=$enableLongPressNavHandle")
         writer.println("$prefix\tenableSearchHapticHint=$enableSearchHapticHint")
         writer.println("$prefix\tenableSearchHapticCommit=$enableSearchHapticCommit")
diff --git a/quickstep/src/com/android/quickstep/DisplayModel.kt b/quickstep/src/com/android/quickstep/DisplayModel.kt
index cbc2f7d..ac94375 100644
--- a/quickstep/src/com/android/quickstep/DisplayModel.kt
+++ b/quickstep/src/com/android/quickstep/DisplayModel.kt
@@ -20,28 +20,28 @@
 import android.hardware.display.DisplayManager
 import android.util.Log
 import android.util.SparseArray
+import android.view.Display
 import androidx.core.util.valueIterator
+import com.android.launcher3.util.Executors
 import com.android.quickstep.DisplayModel.DisplayResource
+import java.io.PrintWriter
 
 /** data model for managing resources with lifecycles that match that of the connected display */
 abstract class DisplayModel<RESOURCE_TYPE : DisplayResource>(val context: Context) {
 
     companion object {
-        private const val TAG = "DisplayViewModel"
+        private const val TAG = "DisplayModel"
         private const val DEBUG = false
     }
 
-    protected val displayManager =
-        context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
+    private val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
     protected val displayResourceArray = SparseArray<RESOURCE_TYPE>()
 
-    abstract fun createDisplayResource(displayId: Int)
-
-    protected val displayListener: DisplayManager.DisplayListener =
+    private val displayListener: DisplayManager.DisplayListener =
         (object : DisplayManager.DisplayListener {
             override fun onDisplayAdded(displayId: Int) {
                 if (DEBUG) Log.d(TAG, "onDisplayAdded: displayId=$displayId")
-                createDisplayResource(displayId)
+                storeDisplayResource(displayId)
             }
 
             override fun onDisplayRemoved(displayId: Int) {
@@ -54,6 +54,17 @@
             }
         })
 
+    protected abstract fun createDisplayResource(display: Display): RESOURCE_TYPE
+
+    protected fun registerDisplayListener() {
+        displayManager.registerDisplayListener(displayListener, Executors.MAIN_EXECUTOR.handler)
+        // In the scenario where displays were added before this display listener was
+        // registered, we should store the DisplayResources for those displays directly.
+        displayManager.displays
+            .filter { getDisplayResource(it.displayId) == null }
+            .forEach { storeDisplayResource(it.displayId) }
+    }
+
     fun destroy() {
         displayResourceArray.valueIterator().forEach { displayResource ->
             displayResource.cleanup()
@@ -73,7 +84,36 @@
         displayResourceArray.remove(displayId)
     }
 
-    abstract class DisplayResource() {
+    fun storeDisplayResource(displayId: Int) {
+        if (DEBUG) Log.d(TAG, "store: displayId=$displayId")
+        getDisplayResource(displayId)?.let {
+            return
+        }
+        val display = displayManager.getDisplay(displayId)
+        if (display == null) {
+            if (DEBUG)
+                Log.w(
+                    TAG,
+                    "storeDisplayResource: could not create display for displayId=$displayId",
+                    Exception(),
+                )
+            return
+        }
+        displayResourceArray[displayId] = createDisplayResource(display)
+    }
+
+    fun dump(prefix: String, writer: PrintWriter) {
+        writer.println("${prefix}${this::class.simpleName}: display resources=[")
+
+        displayResourceArray.valueIterator().forEach { displayResource ->
+            displayResource.dump("${prefix}\t", writer)
+        }
+        writer.println("${prefix}]")
+    }
+
+    abstract class DisplayResource {
         abstract fun cleanup()
+
+        abstract fun dump(prefix: String, writer: PrintWriter)
     }
 }
diff --git a/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt b/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt
index f97cf9c..3b823f5 100644
--- a/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt
+++ b/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt
@@ -80,6 +80,7 @@
                             taskKey.numActivities,
                             taskKey.isTopActivityNoDisplay,
                             taskKey.isActivityStackTransparent,
+                            taskKey.userId,
                         ) -> null
 
                         else -> {
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index e1d4536..c4ba2d5 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -30,6 +30,7 @@
 
 import android.content.Intent;
 import android.os.SystemClock;
+import android.view.Display;
 import android.view.MotionEvent;
 import android.view.RemoteAnimationTarget;
 import android.window.TransitionInfo;
@@ -37,10 +38,10 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.Flags;
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StatefulContainer;
 import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
 import com.android.quickstep.util.ActiveGestureErrorDetector;
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.ActiveGestureProtoLogProxy;
@@ -158,6 +159,7 @@
     private final BaseContainerInterface mContainerInterface;
     private final MultiStateCallback mStateCallback;
     private final int mGestureId;
+    private final int mDisplayId;
 
     public enum TrackpadGestureType {
         NONE,
@@ -190,16 +192,18 @@
     private boolean mHandlingAtomicEvent;
     private boolean mIsInExtendedSlopRegion;
 
-    public GestureState(OverviewComponentObserver componentObserver, int gestureId) {
+    public GestureState(OverviewComponentObserver componentObserver, int displayId, int gestureId) {
+        mDisplayId = displayId;
         mHomeIntent = componentObserver.getHomeIntent();
         mOverviewIntent = componentObserver.getOverviewIntent();
-        mContainerInterface = componentObserver.getContainerInterface();
+        mContainerInterface = componentObserver.getContainerInterface(displayId);
         mStateCallback = new MultiStateCallback(
                 STATE_NAMES.toArray(new String[0]), GestureState::getTrackedEventForState);
         mGestureId = gestureId;
     }
 
     public GestureState(GestureState other) {
+        mDisplayId = other.mDisplayId;
         mHomeIntent = other.mHomeIntent;
         mOverviewIntent = other.mOverviewIntent;
         mContainerInterface = other.mContainerInterface;
@@ -214,6 +218,7 @@
 
     public GestureState() {
         // Do nothing, only used for initializing the gesture state prior to user unlock
+        mDisplayId = Display.DEFAULT_DISPLAY;
         mHomeIntent = new Intent();
         mOverviewIntent = new Intent();
         mContainerInterface = null;
@@ -285,6 +290,13 @@
     }
 
     /**
+     * @return the id for the display this particular gesture was performed on.
+     */
+    public int getDisplayId() {
+        return mDisplayId;
+    }
+
+    /**
      * Sets if the gesture is is from the trackpad, if so, whether 3-finger, or 4-finger
      */
     public void setTrackpadGestureType(TrackpadGestureType trackpadGestureType) {
@@ -311,8 +323,7 @@
      */
     public boolean useSyntheticRecentsTransition() {
         return mRunningTask.isHomeTask()
-                && (Flags.enableFallbackOverviewInWindow()
-                        || Flags.enableLauncherOverviewInWindow());
+                && RecentsWindowFlags.Companion.getEnableOverviewInWindow();
     }
 
     /**
@@ -545,6 +556,7 @@
 
     public void dump(String prefix, PrintWriter pw) {
         pw.println(prefix + "GestureState:");
+        pw.println(prefix + "\tdisplayID=" + mDisplayId);
         pw.println(prefix + "\tgestureID=" + mGestureId);
         pw.println(prefix + "\trunningTask=" + mRunningTask);
         pw.println(prefix + "\tendTarget=" + mEndTarget);
diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java
index 0185737..081ed9d 100644
--- a/quickstep/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/src/com/android/quickstep/InputConsumer.java
@@ -17,6 +17,7 @@
 
 import android.annotation.TargetApi;
 import android.os.Build;
+import android.view.Display;
 import android.view.InputEvent;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -66,6 +67,10 @@
 
     int getType();
 
+    default int getDisplayId() {
+        return Display.DEFAULT_DISPLAY;
+    }
+
     /**
      * Returns true if the user has crossed the threshold for it to be an explicit action.
      */
diff --git a/quickstep/src/com/android/quickstep/InputConsumerUtils.kt b/quickstep/src/com/android/quickstep/InputConsumerUtils.kt
index c340c92..cd3ac12 100644
--- a/quickstep/src/com/android/quickstep/InputConsumerUtils.kt
+++ b/quickstep/src/com/android/quickstep/InputConsumerUtils.kt
@@ -76,7 +76,12 @@
         val bubbleControllers = tac?.bubbleControllers
         if (bubbleControllers != null && BubbleBarInputConsumer.isEventOnBubbles(tac, event)) {
             val consumer: InputConsumer =
-                BubbleBarInputConsumer(context, bubbleControllers, inputMonitorCompat)
+                BubbleBarInputConsumer(
+                    context,
+                    gestureState.displayId,
+                    bubbleControllers,
+                    inputMonitorCompat,
+                )
             logInputConsumerSelectionReason(
                 consumer,
                 newCompoundString("event is on bubbles, creating new input consumer"),
@@ -285,7 +290,13 @@
                             "%ssystem dialog is showing, using SysUiOverlayInputConsumer",
                             SUBSTRING_PREFIX,
                         )
-                base = SysUiOverlayInputConsumer(context, deviceState, inputMonitorCompat)
+                base =
+                    SysUiOverlayInputConsumer(
+                        context,
+                        gestureState.displayId,
+                        deviceState,
+                        inputMonitorCompat,
+                    )
             }
 
             if (
@@ -299,7 +310,13 @@
                             "%sTrackpad 3-finger gesture, using TrackpadStatusBarInputConsumer",
                             SUBSTRING_PREFIX,
                         )
-                base = TrackpadStatusBarInputConsumer(context, base, inputMonitorCompat)
+                base =
+                    TrackpadStatusBarInputConsumer(
+                        context,
+                        gestureState.displayId,
+                        base,
+                        inputMonitorCompat,
+                    )
             }
 
             if (deviceState.isScreenPinningActive) {
@@ -322,7 +339,14 @@
                     reasonPrefix,
                     SUBSTRING_PREFIX,
                 )
-                base = OneHandedModeInputConsumer(context, deviceState, base, inputMonitorCompat)
+                base =
+                    OneHandedModeInputConsumer(
+                        context,
+                        gestureState.displayId,
+                        deviceState,
+                        base,
+                        inputMonitorCompat,
+                    )
             }
 
             if (deviceState.isAccessibilityMenuAvailable) {
@@ -332,7 +356,14 @@
                     reasonPrefix,
                     SUBSTRING_PREFIX,
                 )
-                base = AccessibilityInputConsumer(context, deviceState, base, inputMonitorCompat)
+                base =
+                    AccessibilityInputConsumer(
+                        context,
+                        gestureState.displayId,
+                        deviceState,
+                        base,
+                        inputMonitorCompat,
+                    )
             }
         } else {
             val reasonPrefix = "device is not in gesture navigation mode"
@@ -354,7 +385,14 @@
                     reasonPrefix,
                     SUBSTRING_PREFIX,
                 )
-                base = OneHandedModeInputConsumer(context, deviceState, base, inputMonitorCompat)
+                base =
+                    OneHandedModeInputConsumer(
+                        context,
+                        gestureState.displayId,
+                        deviceState,
+                        base,
+                        inputMonitorCompat,
+                    )
             }
         }
         logInputConsumerSelectionReason(base, reasonString)
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 783ec2c..2ff42cd 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -220,7 +220,7 @@
             mHandler.post(() -> {
                 LauncherBackAnimationController controller = mControllerRef.get();
                 if (controller != null) {
-                    controller.startBack(backEvent);
+                    controller.initBackMotion(backEvent);
                     mProgressAnimator.onBackStarted(backEvent, event -> {
                         float backProgress = event.getProgress();
                         controller.mBackProgress =
@@ -270,6 +270,7 @@
                 }
             }
             controller.mAnimationFinishedCallback = finishedCallback;
+            controller.startBack();
         }
 
         @Override
@@ -294,34 +295,32 @@
         mBackCallback = null;
     }
 
-    private void startBack(BackMotionEvent backEvent) {
+    private void initBackMotion(BackMotionEvent backEvent) {
         // in case we're still animating an onBackCancelled event, let's remove the finish-
         // callback from the progress animator to prevent calling finishAnimation() before
         // restarting a new animation
-        // Side note: startBack is never called during the post-commit phase if the back gesture
-        // was committed (not cancelled). BackAnimationController prevents that. Therefore we
-        // don't have to handle that case.
+        // Side note: initBackMotion is never called during the post-commit phase if the back
+        // gesture was committed (not cancelled). BackAnimationController prevents that. Therefore
+        // we don't have to handle that case.
         mProgressAnimator.removeOnBackCancelledFinishCallback();
 
         mBackInProgress = true;
-        RemoteAnimationTarget appTarget = backEvent.getDepartingAnimationTarget();
-
-        if (appTarget == null || appTarget.leash == null || !appTarget.leash.isValid()) {
+        mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
+    }
+    private void startBack() {
+        if (mBackTarget == null) {
             return;
         }
 
         mTransaction
-                .show(appTarget.leash)
+                .show(mBackTarget.leash)
                 .setAnimationTransaction();
-        mBackTarget = appTarget;
-        mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
-
-        mStartRect.set(appTarget.windowConfiguration.getMaxBounds());
+        mStartRect.set(mBackTarget.windowConfiguration.getMaxBounds());
 
         // inset bottom in case of taskbar being present
         if (!predictiveBackThreeButtonNav() || mLauncher.getDeviceProfile().isTaskbarPresent
                 || DisplayController.getNavigationMode(mLauncher) == NavigationMode.NO_BUTTON) {
-            mStartRect.inset(0, 0, 0, appTarget.contentInsets.bottom);
+            mStartRect.inset(0, 0, 0, mBackTarget.contentInsets.bottom);
         }
 
         mLauncherTargetView = mQuickstepTransitionManager.findLauncherView(
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 0a77688..4c56f35 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -308,10 +308,10 @@
             return null;
         }
 
-        return mContainer.getFirstMatchForAppClose(StableViewInfo.fromLaunchCookies(launchCookies),
+        return mContainer.getFirstHomeElementForAppClose(
+                StableViewInfo.fromLaunchCookies(launchCookies),
                 sourceTaskView.getFirstTask().key.getComponent().getPackageName(),
-                UserHandle.of(sourceTaskView.getFirstTask().key.userId),
-                false /* supportsAllAppsState */);
+                UserHandle.of(sourceTaskView.getFirstTask().key.userId));
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
index afdb403..bf87291 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
@@ -22,6 +22,7 @@
 import android.os.SystemClock
 import android.os.Trace
 import android.util.Log
+import android.view.Display.DEFAULT_DISPLAY
 import android.view.View
 import android.window.TransitionInfo
 import androidx.annotation.BinderThread
@@ -29,9 +30,7 @@
 import androidx.annotation.VisibleForTesting
 import com.android.internal.jank.Cuj
 import com.android.launcher3.Flags.enableAltTabKqsOnConnectedDisplays
-import com.android.launcher3.Flags.enableFallbackOverviewInWindow
 import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
-import com.android.launcher3.Flags.enableLauncherOverviewInWindow
 import com.android.launcher3.Flags.enableOverviewCommandHelperTimeout
 import com.android.launcher3.PagedView
 import com.android.launcher3.logger.LauncherAtom
@@ -52,6 +51,7 @@
 import com.android.quickstep.OverviewCommandHelper.CommandType.SHOW
 import com.android.quickstep.OverviewCommandHelper.CommandType.TOGGLE
 import com.android.quickstep.fallback.window.RecentsDisplayModel
+import com.android.quickstep.fallback.window.RecentsWindowFlags.Companion.enableOverviewInWindow
 import com.android.quickstep.util.ActiveGestureLog
 import com.android.quickstep.util.ActiveGestureProtoLogProxy
 import com.android.quickstep.views.RecentsView
@@ -91,9 +91,11 @@
      */
     private var keyboardTaskFocusIndex = -1
 
+    // TODO (b/397942185): get per-display interface
     private val containerInterface: BaseContainerInterface<*, *>
-        get() = overviewComponentObserver.containerInterface
+        get() = overviewComponentObserver.getContainerInterface(DEFAULT_DISPLAY)
 
+    // TODO (b/397942185): get per-display RecentsView
     private val visibleRecentsView: RecentsView<*, *>?
         get() = containerInterface.getVisibleRecentsView<RecentsView<*, *>>()
 
@@ -296,7 +298,7 @@
 
         val focusedDisplayId = focusState.focusedDisplayId
         val focusedDisplayUIController: TaskbarUIController? =
-            if (RecentsDisplayModel.enableOverviewInWindow()) {
+            if (enableOverviewInWindow) {
                 Log.d(
                     TAG,
                     "Querying RecentsDisplayModel for TaskbarUIController for display: $focusedDisplayId",
@@ -389,9 +391,7 @@
             return false
         }
 
-        val recentsInWindowFlagSet =
-            enableFallbackOverviewInWindow() || enableLauncherOverviewInWindow()
-        if (!recentsInWindowFlagSet) {
+        if (!enableOverviewInWindow) {
             containerInterface.getCreatedContainer()?.rootView?.let { view ->
                 InteractionJankMonitorWrapper.begin(view, Cuj.CUJ_LAUNCHER_QUICK_SWITCH)
             }
@@ -399,6 +399,7 @@
 
         val gestureState =
             touchInteractionService.createGestureState(
+                focusedDisplayId,
                 GestureState.DEFAULT_STATE,
                 GestureState.TrackpadGestureType.NONE,
             )
@@ -421,7 +422,7 @@
                     transitionInfo: TransitionInfo?,
                 ) {
                     Log.d(TAG, "recents animation started: $command")
-                    if (recentsInWindowFlagSet) {
+                    if (enableOverviewInWindow) {
                         containerInterface.getCreatedContainer()?.rootView?.let { view ->
                             InteractionJankMonitorWrapper.begin(view, Cuj.CUJ_LAUNCHER_QUICK_SWITCH)
                         }
@@ -431,7 +432,11 @@
                     logShowOverviewFrom(command.type)
                     containerInterface.runOnInitBackgroundStateUI {
                         Log.d(TAG, "recents animation started - onInitBackgroundStateUI: $command")
-                        interactionHandler.onGestureEnded(0f, PointF())
+                        interactionHandler.onGestureEnded(
+                            0f,
+                            PointF(),
+                            /* horizontalTouchSlopPassed= */ false,
+                        )
                     }
                     command.removeListener(this)
                 }
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index bc3de41..7d3a1da 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -21,8 +21,10 @@
 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static com.android.launcher3.Flags.enableOverviewOnConnectedDisplays;
 import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.quickstep.fallback.window.RecentsWindowFlags.enableLauncherOverviewInWindow;
 import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
 
 import android.content.ActivityNotFoundException;
@@ -40,7 +42,6 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 
-import com.android.launcher3.Flags;
 import com.android.launcher3.R;
 import com.android.launcher3.dagger.ApplicationContext;
 import com.android.launcher3.dagger.LauncherAppComponent;
@@ -49,6 +50,7 @@
 import com.android.launcher3.util.DaggerSingletonTracker;
 import com.android.launcher3.util.SimpleBroadcastReceiver;
 import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
 import com.android.quickstep.util.ActiveGestureProtoLogProxy;
 import com.android.systemui.shared.system.PackageManagerWrapper;
 
@@ -87,7 +89,7 @@
             new CopyOnWriteArrayList<>();
 
     private String mUpdateRegisteredPackage;
-    private BaseContainerInterface mContainerInterface;
+    private BaseContainerInterface mDefaultDisplayContainerInterface;
     private Intent mOverviewIntent;
     private boolean mIsHomeAndOverviewSame;
     private boolean mIsDefaultHome;
@@ -175,11 +177,11 @@
         // Set assistant visibility to 0 from launcher's perspective, ensures any elements that
         // launcher made invisible become visible again before the new activity control helper
         // becomes active.
-        if (mContainerInterface != null) {
-            mContainerInterface.onAssistantVisibilityChanged(0.f);
+        if (mDefaultDisplayContainerInterface != null) {
+            mDefaultDisplayContainerInterface.onAssistantVisibilityChanged(0.f);
         }
 
-        if (SEPARATE_RECENTS_ACTIVITY.get() || Flags.enableLauncherOverviewInWindow()) {
+        if (SEPARATE_RECENTS_ACTIVITY.get() || enableLauncherOverviewInWindow.isTrue()) {
             mIsDefaultHome = false;
             if (defaultHome == null) {
                 defaultHome = mMyHomeIntent.getComponent();
@@ -193,7 +195,7 @@
 
         if (!mIsHomeDisabled && (defaultHome == null || mIsDefaultHome)) {
             // User default home is same as out home app. Use Overview integrated in Launcher.
-            mContainerInterface = LauncherActivityInterface.INSTANCE;
+            mDefaultDisplayContainerInterface = LauncherActivityInterface.INSTANCE;
             mIsHomeAndOverviewSame = true;
             mOverviewIntent = mMyHomeIntent;
             mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent());
@@ -202,12 +204,11 @@
             unregisterOtherHomeAppUpdateReceiver();
         } else {
             // The default home app is a different launcher. Use the fallback Overview instead.
-
-            if (Flags.enableLauncherOverviewInWindow() || Flags.enableFallbackOverviewInWindow()) {
-                mContainerInterface =
+            if (RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
+                mDefaultDisplayContainerInterface =
                         mRecentsDisplayModel.getFallbackWindowInterface(DEFAULT_DISPLAY);
             } else {
-                mContainerInterface = FallbackActivityInterface.INSTANCE;
+                mDefaultDisplayContainerInterface = FallbackActivityInterface.INSTANCE;
             }
             mIsHomeAndOverviewSame = false;
             mOverviewIntent = mFallbackIntent;
@@ -296,12 +297,16 @@
     }
 
     /**
-     * Get the current control helper for managing interactions to the overview container.
+     * Get the current control helper for managing interactions to the overview container for
+     * the given displayId.
      *
-     * @return the current control helper
+     * @param displayId The display id
+     * @return the control helper for the given display
      */
-    public BaseContainerInterface<?,?> getContainerInterface() {
-        return mContainerInterface;
+    public BaseContainerInterface<?, ?> getContainerInterface(int displayId) {
+        return (enableOverviewOnConnectedDisplays() && displayId != DEFAULT_DISPLAY)
+                ? mRecentsDisplayModel.getFallbackWindowInterface(displayId)
+                : mDefaultDisplayContainerInterface;
     }
 
     public void dump(PrintWriter pw) {
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index 9b0e75c..e9f7024 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -1,5 +1,7 @@
 package com.android.quickstep;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static com.android.launcher3.taskbar.TaskbarThresholdUtils.getFromNavThreshold;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
@@ -177,7 +179,7 @@
 
             case TestProtocol.REQUEST_RECREATE_TASKBAR:
                 // Allow null-pointer to catch illegal states.
-                runOnTISBinder(tisBinder -> tisBinder.getTaskbarManager().recreateTaskbar());
+                runOnTISBinder(tisBinder -> tisBinder.getTaskbarManager().recreateTaskbars());
                 return response;
             case TestProtocol.REQUEST_TASKBAR_IME_DOCKED:
                 return getTISBinderUIProperty(Bundle::putBoolean, tisBinder ->
@@ -210,8 +212,9 @@
     }
 
     private RecentsViewContainer getRecentsViewContainer() {
+        // TODO (b/400647896): support per-display container in e2e tests
         return OverviewComponentObserver.INSTANCE.get(mContext)
-                .getContainerInterface().getCreatedContainer();
+                .getContainerInterface(DEFAULT_DISPLAY).getCreatedContainer();
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 2c4c2f9..cc5b2da 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -38,9 +38,12 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.statehandlers.DesktopVisibilityController;
+import com.android.launcher3.util.DaggerSingletonTracker;
 import com.android.launcher3.util.LooperExecutor;
 import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.window.WindowManagerProxy;
 import com.android.quickstep.util.DesktopTask;
+import com.android.quickstep.util.ExternalDisplaysKt;
 import com.android.quickstep.util.GroupTask;
 import com.android.quickstep.util.SingleTask;
 import com.android.quickstep.util.SplitTask;
@@ -69,7 +72,10 @@
 /**
  * Manages the recent task list from the system, caching it as necessary.
  */
-public class RecentTasksList {
+// TODO: b/401602554 - Consider letting [DesktopTasksController] notify [RecentTasksController] of
+//  desk changes to trigger [IRecentTasksListener.onRecentTasksChanged()], instead of implementing
+//  [DesktopVisibilityListener].
+public class RecentTasksList implements WindowManagerProxy.DesktopVisibilityListener {
 
     private static final TaskLoadResult INVALID_RESULT = new TaskLoadResult(-1, false, 0);
 
@@ -77,6 +83,7 @@
     private final KeyguardManager mKeyguardManager;
     private final LooperExecutor mMainThreadExecutor;
     private final SystemUiProxy mSysUiProxy;
+    private final DesktopVisibilityController mDesktopVisibilityController;
 
     // The list change id, increments as the task list changes in the system
     private int mChangeId;
@@ -94,13 +101,16 @@
 
     public RecentTasksList(Context context, LooperExecutor mainThreadExecutor,
             KeyguardManager keyguardManager, SystemUiProxy sysUiProxy,
-            TopTaskTracker topTaskTracker) {
+            TopTaskTracker topTaskTracker,
+            DesktopVisibilityController desktopVisibilityController,
+            DaggerSingletonTracker tracker) {
         mContext = context;
         mMainThreadExecutor = mainThreadExecutor;
         mKeyguardManager = keyguardManager;
         mChangeId = 1;
         mSysUiProxy = sysUiProxy;
-        sysUiProxy.registerRecentTasksListener(new IRecentTasksListener.Stub() {
+        mDesktopVisibilityController = desktopVisibilityController;
+        final IRecentTasksListener recentTasksListener = new IRecentTasksListener.Stub() {
             @Override
             public void onRecentTasksChanged() throws RemoteException {
                 mMainThreadExecutor.execute(RecentTasksList.this::onRecentTasksChanged);
@@ -146,7 +156,19 @@
                     topTaskTracker.onVisibleTasksChanged(visibleTasks);
                 });
             }
-        });
+        };
+
+        mSysUiProxy.registerRecentTasksListener(recentTasksListener);
+        tracker.addCloseable(
+                () -> mSysUiProxy.unregisterRecentTasksListener(recentTasksListener));
+
+        if (DesktopModeStatus.enableMultipleDesktops(mContext)) {
+            mDesktopVisibilityController.registerDesktopVisibilityListener(
+                    this);
+            tracker.addCloseable(
+                    () -> mDesktopVisibilityController.unregisterDesktopVisibilityListener(this));
+        }
+
         // We may receive onRunningTaskAppeared events later for tasks which have already been
         // included in the list returned by mSysUiProxy.getRunningTasks(), or may receive
         // onRunningTaskVanished for tasks not included in the returned list. These cases will be
@@ -285,6 +307,27 @@
         return mRunningTasks;
     }
 
+    @Override
+    public void onDeskAdded(int displayId, int deskId) {
+        onRecentTasksChanged();
+    }
+
+    @Override
+    public void onDeskRemoved(int displayId, int deskId) {
+        onRecentTasksChanged();
+    }
+
+    @Override
+    public void onActiveDeskChanged(int displayId, int newActiveDesk, int oldActiveDesk) {
+        // Should desk activation changes lead to the invalidation of the loaded tasks? The cases
+        // are:
+        // - Switching from one active desk to another.
+        // - Switching from out of a desk session into an active desk.
+        // - Switching from an active desk to a non-desk session.
+        // These changes don't affect the list of desks, nor their contents, so let's ignore them
+        // for now.
+    }
+
     private void onRunningTaskAppeared(RunningTaskInfo taskInfo) {
         // Make sure this task is not already in the list
         for (RunningTaskInfo existingTask : mRunningTasks) {
@@ -353,7 +396,7 @@
 
         TaskLoadResult allTasks = new TaskLoadResult(requestId, loadKeysOnly, rawTasks.size());
 
-        int numVisibleTasks = 0;
+        boolean isFirstVisibleTaskFound = false;
         for (GroupedTaskInfo rawTask : rawTasks) {
             if (rawTask.isBaseType(TYPE_DESK)) {
                 // TYPE_DESK tasks is only created when desktop mode can be entered,
@@ -362,6 +405,14 @@
                     List<DesktopTask> desktopTasks = createDesktopTasks(
                             rawTask.getBaseGroupedTask());
                     allTasks.addAll(desktopTasks);
+
+                    // If any task in desktop group task is visible, set isFirstVisibleTaskFound to
+                    // true. This way if there is a transparent task in the list later on, it does
+                    // not get its own tile in Overview.
+                    if (rawTask.getBaseGroupedTask().getTaskInfoList().stream().anyMatch(
+                            taskInfo -> taskInfo.isVisible)) {
+                        isFirstVisibleTaskFound = true;
+                    }
                 }
                 continue;
             }
@@ -402,7 +453,7 @@
                                     tmpLockedUsers.get(task2Key.userId) /* isLocked */);
                 } else {
                     // Is fullscreen task
-                    if (numVisibleTasks > 0) {
+                    if (isFirstVisibleTaskFound) {
                         boolean isExcluded = (taskInfo1.baseIntent.getFlags()
                                 & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
                         if (taskInfo1.isTopActivityTransparent && isExcluded) {
@@ -413,7 +464,7 @@
                     }
                 }
                 if (taskInfo1.isVisible) {
-                    numVisibleTasks++;
+                    isFirstVisibleTaskFound = true;
                 }
                 if (task2 != null) {
                     Objects.requireNonNull(rawTask.getSplitBounds());
@@ -452,7 +503,8 @@
             Map<Integer, List<Task>> perDisplayTasks = new HashMap<>();
             for (TaskInfo taskInfo : recentTaskInfo.getTaskInfoList()) {
                 Task task = createTask(taskInfo, minimizedTaskIds);
-                List<Task> tasks = perDisplayTasks.computeIfAbsent(taskInfo.displayId,
+                List<Task> tasks = perDisplayTasks.computeIfAbsent(
+                        ExternalDisplaysKt.getDisplayId(task),
                         k -> new ArrayList<>());
                 tasks.add(task);
             }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index d7152b5..ecde37b 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -34,9 +34,9 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.UiThread;
 
-import com.android.launcher3.Flags;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
 import com.android.quickstep.util.ActiveGestureProtoLogProxy;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
@@ -113,8 +113,8 @@
         boolean isOpeningHome = Arrays.stream(appTargets).filter(app -> app.mode == MODE_OPENING
                         && app.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME)
                 .count() > 0;
-        if (appCount == 0 && (!(Flags.enableFallbackOverviewInWindow()
-                || Flags.enableLauncherOverviewInWindow()) || isOpeningHome)) {
+        if (appCount == 0 && (!RecentsWindowFlags.Companion.getEnableOverviewInWindow()
+                || isOpeningHome)) {
             ActiveGestureProtoLogProxy.logOnRecentsAnimationStartCancelled();
             // Edge case, if there are no closing app targets, then Launcher has nothing to handle
             notifyAnimationCanceled();
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 090ccdc..6710096 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -88,6 +88,9 @@
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 
 import java.io.PrintWriter;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 import javax.inject.Inject;
 
@@ -119,6 +122,7 @@
             InputMethodService.canImeRenderGesturalNavButtons();
 
     private @SystemUiStateFlags long mSystemUiStateFlags = QuickStepContract.SYSUI_STATE_AWAKE;
+    private final Map<Integer, Long> mSysUIStateFlagsPerDisplay = new ConcurrentHashMap<>();
     private NavigationMode mMode = THREE_BUTTONS;
     private NavBarPosition mNavBarPosition;
 
@@ -321,13 +325,6 @@
     }
 
     /**
-     * @return the display id for the display that Launcher is running on.
-     */
-    public int getDisplayId() {
-        return DEFAULT_DISPLAY;
-    }
-
-    /**
      * @return whether the user has completed setup wizard
      */
     public boolean isUserSetupComplete() {
@@ -353,22 +350,51 @@
     }
 
     /**
-     * Updates the system ui state flags from SystemUI.
+     * Updates the system ui state flags from SystemUI for a specific display.
+     *
+     * @param stateFlags the current {@link SystemUiStateFlags} for the display.
+     * @param displayId  the display's ID.
      */
-    public void setSystemUiFlags(@SystemUiStateFlags long stateFlags) {
-        mSystemUiStateFlags = stateFlags;
+    public void setSysUIStateFlagsForDisplay(@SystemUiStateFlags long stateFlags,
+            int displayId) {
+        mSysUIStateFlagsPerDisplay.put(displayId, stateFlags);
     }
 
     /**
-     * @return the system ui state flags.
+     * Clears the system ui state flags for a specific display. This is called when the display is
+     * destroyed.
+     *
+     * @param displayId the display's ID.
+     */
+    public void clearSysUIStateFlagsForDisplay(int displayId) {
+        mSysUIStateFlagsPerDisplay.remove(displayId);
+    }
+
+    /**
+     * @return the system ui state flags for the default display.
      */
     // TODO(141886704): See if we can remove this
     @SystemUiStateFlags
-    public long getSystemUiStateFlags() {
-        return mSystemUiStateFlags;
+    public long getSysuiStateFlag() {
+        return getSystemUiStateFlags(DEFAULT_DISPLAY);
     }
 
     /**
+     * @return the system ui state flags for a given display ID.
+     */
+    @SystemUiStateFlags
+    public long getSystemUiStateFlags(int displayId) {
+        return mSysUIStateFlagsPerDisplay.getOrDefault(displayId,
+                QuickStepContract.SYSUI_STATE_AWAKE);
+    }
+
+    /**
+     * @return the display ids that have sysui state.
+     */
+    public Set<Integer> getDisplaysWithSysUIState() {
+        return mSysUIStateFlagsPerDisplay.keySet();
+    }
+    /**
      * Sets the flag that indicates whether a predictive back-to-home animation is in progress
      */
     public void setPredictiveBackToHomeInProgress(boolean isInProgress) {
@@ -386,8 +412,8 @@
      * @return whether SystemUI is in a state where we can start a system gesture.
      */
     public boolean canStartSystemGesture() {
-        boolean canStartWithNavHidden = (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
-                || (mSystemUiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0
+        boolean canStartWithNavHidden = (getSysuiStateFlag() & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
+                || (getSysuiStateFlag() & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0
                 || mRotationTouchHelper.isTaskListFrozen();
         return canStartWithNavHidden && canStartAnyGesture();
     }
@@ -399,7 +425,7 @@
      */
     public boolean canStartTrackpadGesture() {
         boolean trackpadGesturesEnabled =
-                (mSystemUiStateFlags & SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED) == 0;
+                (getSysuiStateFlag() & SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED) == 0;
         return trackpadGesturesEnabled && canStartAnyGesture();
     }
 
@@ -407,8 +433,8 @@
      * Common logic to determine if either trackpad or finger gesture can be started
      */
     private boolean canStartAnyGesture() {
-        boolean homeOrOverviewEnabled = (mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0
-                || (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0;
+        boolean homeOrOverviewEnabled = (getSysuiStateFlag() & SYSUI_STATE_HOME_DISABLED) == 0
+                || (getSysuiStateFlag() & SYSUI_STATE_OVERVIEW_DISABLED) == 0;
         long gestureDisablingStates = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
                         | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
                         | SYSUI_STATE_QUICK_SETTINGS_EXPANDED
@@ -416,7 +442,7 @@
                         | SYSUI_STATE_DEVICE_DREAMING
                         | SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION
                         | SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING;
-        return (gestureDisablingStates & mSystemUiStateFlags) == 0 && homeOrOverviewEnabled;
+        return (gestureDisablingStates & getSysuiStateFlag()) == 0 && homeOrOverviewEnabled;
     }
 
     /**
@@ -424,35 +450,35 @@
      *         (like camera or maps)
      */
     public boolean isKeyguardShowingOccluded() {
-        return (mSystemUiStateFlags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0;
+        return (getSysuiStateFlag() & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0;
     }
 
     /**
      * @return whether screen pinning is enabled and active
      */
     public boolean isScreenPinningActive() {
-        return (mSystemUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0;
+        return (getSysuiStateFlag() & SYSUI_STATE_SCREEN_PINNING) != 0;
     }
 
     /**
      * @return whether assistant gesture is constraint
      */
     public boolean isAssistantGestureIsConstrained() {
-        return (mSystemUiStateFlags & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0;
+        return (getSysuiStateFlag() & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0;
     }
 
     /**
      * @return whether the bubble stack is expanded
      */
     public boolean isBubblesExpanded() {
-        return (mSystemUiStateFlags & SYSUI_STATE_BUBBLES_EXPANDED) != 0;
+        return (getSysuiStateFlag() & SYSUI_STATE_BUBBLES_EXPANDED) != 0;
     }
 
     /**
      * @return whether the global actions dialog is showing
      */
     public boolean isSystemUiDialogShowing() {
-        return (mSystemUiStateFlags & SYSUI_STATE_DIALOG_SHOWING) != 0;
+        return (getSysuiStateFlag() & SYSUI_STATE_DIALOG_SHOWING) != 0;
     }
 
     /**
@@ -466,35 +492,35 @@
      * @return whether the accessibility menu is available.
      */
     public boolean isAccessibilityMenuAvailable() {
-        return (mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
+        return (getSysuiStateFlag() & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
     }
 
     /**
      * @return whether the accessibility menu shortcut is available.
      */
     public boolean isAccessibilityMenuShortcutAvailable() {
-        return (mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
+        return (getSysuiStateFlag() & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
     }
 
     /**
      * @return whether home is disabled (either by SUW/SysUI/device policy)
      */
     public boolean isHomeDisabled() {
-        return (mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) != 0;
+        return (getSysuiStateFlag() & SYSUI_STATE_HOME_DISABLED) != 0;
     }
 
     /**
      * @return whether overview is disabled (either by SUW/SysUI/device policy)
      */
     public boolean isOverviewDisabled() {
-        return (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0;
+        return (getSysuiStateFlag() & SYSUI_STATE_OVERVIEW_DISABLED) != 0;
     }
 
     /**
      * @return whether one-handed mode is enabled and active
      */
     public boolean isOneHandedModeActive() {
-        return (mSystemUiStateFlags & SYSUI_STATE_ONE_HANDED_ACTIVE) != 0;
+        return (getSysuiStateFlag() & SYSUI_STATE_ONE_HANDED_ACTIVE) != 0;
     }
 
     /**
@@ -557,7 +583,7 @@
      */
     public boolean canTriggerAssistantAction(MotionEvent ev) {
         return mAssistantAvailable
-                && !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)
+                && !QuickStepContract.isAssistantGestureDisabled(getSysuiStateFlag())
                 && mRotationTouchHelper.touchInAssistantRegion(ev)
                 && !isTrackpadScroll(ev)
                 && !isLockToAppActive();
@@ -597,7 +623,7 @@
     /** Returns whether IME is rendering nav buttons, and IME is currently showing. */
     public boolean isImeRenderingNavButtons() {
         return mCanImeRenderGesturalNavButtons && mMode == NO_BUTTON
-                && ((mSystemUiStateFlags & SYSUI_STATE_IME_VISIBLE) != 0);
+                && ((getSysuiStateFlag() & SYSUI_STATE_IME_VISIBLE) != 0);
     }
 
     /**
@@ -629,24 +655,37 @@
         return touchSlop * touchSlop;
     }
 
+    /** Returns a string representation of the system ui state flags for the default display. */
     public String getSystemUiStateString() {
-        return  QuickStepContract.getSystemUiStateString(mSystemUiStateFlags);
+        return  getSystemUiStateString(getSysuiStateFlag());
+    }
+
+    /** Returns a string representation of the system ui state flags. */
+    public String getSystemUiStateString(long flags) {
+        return  QuickStepContract.getSystemUiStateString(flags);
     }
 
     public void dump(PrintWriter pw) {
         pw.println("DeviceState:");
         pw.println("  canStartSystemGesture=" + canStartSystemGesture());
-        pw.println("  systemUiFlags=" + mSystemUiStateFlags);
+        pw.println("  systemUiFlagsForDefaultDisplay=" + getSysuiStateFlag());
         pw.println("  systemUiFlagsDesc=" + getSystemUiStateString());
         pw.println("  assistantAvailable=" + mAssistantAvailable);
         pw.println("  assistantDisabled="
-                + QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
+                + QuickStepContract.isAssistantGestureDisabled(getSysuiStateFlag()));
         pw.println("  isOneHandedModeEnabled=" + mIsOneHandedModeEnabled);
         pw.println("  isSwipeToNotificationEnabled=" + mIsSwipeToNotificationEnabled);
         pw.println("  deferredGestureRegion=" + mDeferredGestureRegion.getBounds());
         pw.println("  exclusionRegion=" + mExclusionRegion.getBounds());
         pw.println("  pipIsActive=" + mPipIsActive);
         pw.println("  predictiveBackToHomeInProgress=" + mIsPredictiveBackToHomeInProgress);
+        for (int displayId : mSysUIStateFlagsPerDisplay.keySet()) {
+            pw.println("  systemUiFlagsForDisplay" + displayId + "=" + getSystemUiStateFlags(
+                    displayId));
+            pw.println("  systemUiFlagsForDisplay" + displayId + "Desc=" + getSystemUiStateString(
+                    getSystemUiStateFlags(displayId)));
+        }
+        pw.println("  RotationTouchHelper:");
         mRotationTouchHelper.dump(pw);
     }
 }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index cf7e499..0deb1ca 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -59,6 +59,10 @@
         if (!DesktopModeStatus.canEnterDesktopMode(context)) {
             return false;
         }
+        // TODO: b/400866688 - Check if we need to update this such that for an empty desk, we
+        //  receive a list of apps that contain only the Launcher and the `DesktopWallpaperActivity`
+        //  and both are fullscreen windowing mode. A desk can also have transparent modals and
+        //  immersive apps which may not have a "freeform" windowing mode.
         for (RemoteAnimationTarget target : apps) {
             if (target.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
                 return true;
diff --git a/quickstep/src/com/android/quickstep/RecentsFilterState.java b/quickstep/src/com/android/quickstep/RecentsFilterState.java
index c4b0f25..1808a97 100644
--- a/quickstep/src/com/android/quickstep/RecentsFilterState.java
+++ b/quickstep/src/com/android/quickstep/RecentsFilterState.java
@@ -18,6 +18,7 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.quickstep.util.DesksUtils;
 import com.android.quickstep.util.GroupTask;
 import com.android.quickstep.views.TaskViewType;
 import com.android.systemui.shared.recents.model.Task;
@@ -117,37 +118,43 @@
      * Returns a predicate for filtering out GroupTasks by package name.
      *
      * @param packageName package name to filter GroupTasks by
-     *                    if null, Predicate filters out desktop tasks with no non-minimized tasks.
+     *                    if null, Predicate filters out desktop tasks with no non-minimized tasks,
+     *                    unless the multiple desks feature is enabled, which allows empty desks.
      */
     public static Predicate<GroupTask> getFilter(@Nullable String packageName) {
         if (packageName == null) {
-            return getEmptyDesktopTaskFilter();
+            return getDesktopTaskFilter();
         }
 
         return (groupTask) -> (groupTask.containsPackage(packageName)
-                && !isDestopTaskWithMinimizedTasksOnly(groupTask));
+                && shouldKeepGroupTask(groupTask));
     }
 
     /**
-     * Returns a predicate that filters out desk tasks that contain no non-minimized desktop tasks.
+     * Returns a predicate that filters out desk tasks that contain no non-minimized desktop tasks,
+     * unless the multiple desks feature is enabled, which allows empty desks.
      */
-    public static Predicate<GroupTask> getEmptyDesktopTaskFilter() {
-        return (groupTask -> !isDestopTaskWithMinimizedTasksOnly(groupTask));
+    public static Predicate<GroupTask> getDesktopTaskFilter() {
+        return (groupTask -> shouldKeepGroupTask(groupTask));
     }
 
     /**
-     * Whether the provided task is a desktop task with no non-minimized tasks - returns true if the
-     * desktop task has no tasks at all.
+     * Returns true if the given `groupTask` should be kept, and false if it should be filtered out.
+     * Desks will be filtered out if they are empty unless the multiple desks feature is enabled.
      *
      * @param groupTask The group task to check.
      */
-    static boolean isDestopTaskWithMinimizedTasksOnly(GroupTask groupTask) {
+    private static boolean shouldKeepGroupTask(GroupTask groupTask) {
         if (groupTask.taskViewType != TaskViewType.DESKTOP) {
-            return false;
+            return true;
         }
+
+        if (DesksUtils.areMultiDesksFlagsEnabled()) {
+            return true;
+        }
+
         return groupTask.getTasks().stream()
-                .filter(task -> !task.isMinimized)
-                .toList().isEmpty();
+                .anyMatch(task -> !task.isMinimized);
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 1d83d42..e1adf3d 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -43,6 +43,7 @@
 import com.android.launcher3.graphics.ThemeManager;
 import com.android.launcher3.graphics.ThemeManager.ThemeChangeListener;
 import com.android.launcher3.icons.IconProvider;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
 import com.android.launcher3.util.DaggerSingletonObject;
 import com.android.launcher3.util.DaggerSingletonTracker;
 import com.android.launcher3.util.DisplayController;
@@ -61,6 +62,8 @@
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 
+import dagger.Lazy;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
@@ -72,8 +75,6 @@
 
 import javax.inject.Inject;
 
-import dagger.Lazy;
-
 /**
  * Singleton class to load and manage recents model.
  */
@@ -104,12 +105,14 @@
             DisplayController displayController,
             LockedUserState lockedUserState,
             Lazy<ThemeManager> themeManagerLazy,
+            DesktopVisibilityController desktopVisibilityController,
             DaggerSingletonTracker tracker
             ) {
         // Lazily inject the ThemeManager and access themeManager once the device is
         // unlocked. See b/393248495 for details.
         this(context, new IconProvider(context), systemUiProxy, topTaskTracker,
-                displayController, lockedUserState,themeManagerLazy, tracker);
+                displayController, lockedUserState, themeManagerLazy, desktopVisibilityController,
+                tracker);
     }
 
     @SuppressLint("VisibleForTests")
@@ -120,6 +123,7 @@
             DisplayController displayController,
             LockedUserState lockedUserState,
             Lazy<ThemeManager> themeManagerLazy,
+            DesktopVisibilityController desktopVisibilityController,
             DaggerSingletonTracker tracker) {
         this(context,
                 new RecentTasksList(
@@ -127,7 +131,7 @@
                         MAIN_EXECUTOR,
                         context.getSystemService(KeyguardManager.class),
                         systemUiProxy,
-                        topTaskTracker),
+                        topTaskTracker, desktopVisibilityController, tracker),
                 new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider, displayController),
                 new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR),
                 iconProvider,
@@ -205,7 +209,7 @@
     @Override
     public int getTasks(@Nullable Consumer<List<GroupTask>> callback) {
         return mTaskList.getTasks(false /* loadKeysOnly */, callback,
-                RecentsFilterState.getEmptyDesktopTaskFilter());
+                RecentsFilterState.getDesktopTaskFilter());
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index a614327..ae6cfa0 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -142,15 +142,17 @@
         mContext = context;
         mDisplayController = displayController;
         mSystemUiProxy = systemUiProxy;
+        // TODO (b/398195845): this needs updating so non-default displays do not rotate with the
+        //  default display.
         mDisplayId = DEFAULT_DISPLAY;
 
         Resources resources = mContext.getResources();
         mOrientationTouchTransformer = new OrientationTouchTransformer(resources, mMode,
                 () -> QuickStepContract.getWindowCornerRadius(mContext));
 
-        // Register for navigation mode changes
-        mDisplayController.addChangeListener(this);
-        DisplayController.Info info = mDisplayController.getInfo();
+        // Register for navigation mode and rotation changes
+        mDisplayController.addChangeListenerForDisplay(this, mDisplayId);
+        DisplayController.Info info = mDisplayController.getInfoForDisplay(mDisplayId);
         onDisplayInfoChanged(context, info, CHANGE_ALL);
 
         mOrientationListener = new OrientationEventListener(mContext) {
@@ -174,7 +176,7 @@
         };
 
         lifeCycle.addCloseable(() -> {
-            mDisplayController.removeChangeListener(this);
+            mDisplayController.removeChangeListenerForDisplay(this, mDisplayId);
             mOrientationListener.disable();
             TaskStackChangeListeners.getInstance()
                     .unregisterTaskStackListener(mFrozenTaskListener);
@@ -201,7 +203,8 @@
             return;
         }
 
-        mOrientationTouchTransformer.createOrAddTouchRegion(mDisplayController.getInfo(),
+        mOrientationTouchTransformer.createOrAddTouchRegion(
+                mDisplayController.getInfoForDisplay(mDisplayId),
                 "RTH.updateGestureTouchRegions");
     }
 
@@ -258,7 +261,8 @@
 
         if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
             NavigationMode newMode = info.getNavigationMode();
-            mOrientationTouchTransformer.setNavigationMode(newMode, mDisplayController.getInfo(),
+            mOrientationTouchTransformer.setNavigationMode(newMode,
+                    mDisplayController.getInfoForDisplay(mDisplayId),
                     mContext.getResources());
 
             TaskStackChangeListeners.getInstance()
@@ -280,7 +284,8 @@
      */
     void setGesturalHeight(int newGesturalHeight) {
         mOrientationTouchTransformer.setGesturalHeight(
-                newGesturalHeight, mDisplayController.getInfo(), mContext.getResources());
+                newGesturalHeight, mDisplayController.getInfoForDisplay(mDisplayId),
+                mContext.getResources());
     }
 
     /**
@@ -296,7 +301,8 @@
     }
 
     private void enableMultipleRegions(boolean enable) {
-        mOrientationTouchTransformer.enableMultipleRegions(enable, mDisplayController.getInfo());
+        mOrientationTouchTransformer.enableMultipleRegions(enable,
+                mDisplayController.getInfoForDisplay(mDisplayId));
         notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getQuickStepStartingRotation());
         if (enable && !mInOverview && !TestProtocol.sDisableSensorRotation) {
             // Clear any previous state from sensor manager
@@ -359,7 +365,8 @@
      * notifies system UI of the primary rotation the user is interacting with
      */
     private void toggleSecondaryNavBarsForRotation() {
-        mOrientationTouchTransformer.setSingleActiveRegion(mDisplayController.getInfo());
+        mOrientationTouchTransformer.setSingleActiveRegion(
+                mDisplayController.getInfoForDisplay(mDisplayId));
         notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
     }
 
diff --git a/quickstep/src/com/android/quickstep/SimpleOrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/SimpleOrientationTouchTransformer.java
index d2a491d..de7fb89 100644
--- a/quickstep/src/com/android/quickstep/SimpleOrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/SimpleOrientationTouchTransformer.java
@@ -15,6 +15,8 @@
  */
 package com.android.quickstep;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
 import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
 import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
@@ -42,15 +44,20 @@
     private OrientationRectF mOrientationRectF;
     private OrientationRectF mTouchingOrientationRectF;
     private int mViewRotation;
+    private final int mDisplayId;
 
     @Inject
     public SimpleOrientationTouchTransformer(@ApplicationContext Context context,
             DisplayController displayController,
             DaggerSingletonTracker tracker) {
-        displayController.addChangeListener(this);
-        tracker.addCloseable(() -> displayController.removeChangeListener(this));
+        // TODO (b/398195845): make sure non-default displays don't get affected by default display
+        // changes.
+        mDisplayId = DEFAULT_DISPLAY;
+        displayController.addChangeListenerForDisplay(this, mDisplayId);
+        tracker.addCloseable(
+                () -> displayController.removeChangeListenerForDisplay(this, mDisplayId));
 
-        onDisplayInfoChanged(context, displayController.getInfo(), CHANGE_ALL);
+        onDisplayInfoChanged(context, displayController.getInfoForDisplay(mDisplayId), CHANGE_ALL);
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.kt b/quickstep/src/com/android/quickstep/SystemUiProxy.kt
index 1f3eb2a..506f85d 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.kt
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.kt
@@ -655,26 +655,48 @@
      * Tells SysUI to show a shortcut bubble.
      *
      * @param info the shortcut info used to create or identify the bubble.
+     * @param bubbleBarLocation the optional location of the bubble bar.
      */
-    fun showShortcutBubble(info: ShortcutInfo?) =
+    @JvmOverloads
+    fun showShortcutBubble(info: ShortcutInfo?, bubbleBarLocation: BubbleBarLocation? = null) =
         executeWithErrorLog({ "Failed call showShortcutBubble" }) {
-            bubbles?.showShortcutBubble(info)
+            bubbles?.showShortcutBubble(info, bubbleBarLocation)
         }
 
     /**
      * Tells SysUI to show a bubble of an app.
      *
      * @param intent the intent used to create the bubble.
+     * @param bubbleBarLocation the optional location of the bubble bar.
      */
-    fun showAppBubble(intent: Intent?, user: UserHandle) =
+    @JvmOverloads
+    fun showAppBubble(
+        intent: Intent?,
+        user: UserHandle,
+        bubbleBarLocation: BubbleBarLocation? = null,
+    ) =
         executeWithErrorLog({ "Failed call showAppBubble" }) {
-            bubbles?.showAppBubble(intent, user)
+            bubbles?.showAppBubble(intent, user, bubbleBarLocation)
         }
 
     /** Tells SysUI to show the expanded view. */
     fun showExpandedView() =
         executeWithErrorLog({ "Failed call showExpandedView" }) { bubbles?.showExpandedView() }
 
+    /** Tells SysUI to show the bubble drop target. */
+    @JvmOverloads
+    fun showBubbleDropTarget(show: Boolean, bubbleBarLocation: BubbleBarLocation? = null) =
+        executeWithErrorLog({ "Failed call showDropTarget" }) {
+            bubbles?.showDropTarget(show, bubbleBarLocation)
+        }
+
+    /** Tells SysUI to move the bubble to full screen. */
+    fun moveBubbleToFullscreen(key: String) {
+        executeWithErrorLog({ "Failed to call moveBubbleToFullscreen"}) {
+            bubbles?.moveBubbleToFullscreen(key)
+        }
+    }
+
     //
     // Splitscreen
     //
@@ -1074,18 +1096,26 @@
     // Desktop Mode
     //
     /** Calls shell to create a new desk (if possible) on the display whose ID is `displayId`. */
-    fun createDesktop(displayId: Int) =
+    fun createDesk(displayId: Int) =
         executeWithErrorLog({ "Failed call createDesk" }) { desktopMode?.createDesk(displayId) }
 
     /**
      * Calls shell to activate the desk whose ID is `deskId` on whatever display it exists on. This
      * will bring all tasks on this desk to the front.
      */
-    fun activateDesktop(deskId: Int, transition: RemoteTransition?) =
+    fun activateDesk(deskId: Int, transition: RemoteTransition?) =
         executeWithErrorLog({ "Failed call activateDesk" }) {
             desktopMode?.activateDesk(deskId, transition)
         }
 
+    /** Calls shell to remove the desk whose ID is `deskId`. */
+    fun removeDesk(deskId: Int) =
+        executeWithErrorLog({ "Failed call removeDesk" }) { desktopMode?.removeDesk(deskId) }
+
+    /** Calls shell to remove all the available desks on all displays. */
+    fun removeAllDesks() =
+        executeWithErrorLog({ "Failed call removeAllDesks" }) { desktopMode?.removeAllDesks() }
+
     /** Call shell to show all apps active on the desktop */
     fun showDesktopApps(displayId: Int, transition: RemoteTransition?) =
         executeWithErrorLog({ "Failed call showDesktopApps" }) {
@@ -1137,9 +1167,9 @@
         }
 
     /** Call shell to remove the desktop that is on given `displayId` */
-    fun removeDesktop(displayId: Int) =
-        executeWithErrorLog({ "Failed call removeDesktop" }) {
-            desktopMode?.removeDesktop(displayId)
+    fun removeDefaultDeskInDisplay(displayId: Int) =
+        executeWithErrorLog({ "Failed call removeDefaultDeskInDisplay" }) {
+            desktopMode?.removeDefaultDeskInDisplay(displayId)
         }
 
     /** Call shell to move a task with given `taskId` to external display. */
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index cb11afa..1c7f23c 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -42,12 +42,12 @@
 import androidx.annotation.UiThread;
 
 import com.android.internal.util.ArrayUtils;
-import com.android.launcher3.Flags;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.taskbar.TaskbarUIController;
 import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
 import com.android.quickstep.fallback.window.RecentsWindowManager;
 import com.android.quickstep.util.ActiveGestureProtoLogProxy;
 import com.android.quickstep.util.SystemUiFlagUtils;
@@ -63,7 +63,6 @@
 public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
     public static final boolean SHELL_TRANSITIONS_ROTATION =
             SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
-
     private final Context mCtx;
     private RecentsAnimationController mController;
     private RecentsAnimationCallbacks mCallbacks;
@@ -313,12 +312,11 @@
         }
 
         if(containerInterface.getCreatedContainer() instanceof RecentsWindowManager
-                && (Flags.enableFallbackOverviewInWindow()
-                        || Flags.enableLauncherOverviewInWindow())) {
+                && RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
             mRecentsAnimationStartPending = getSystemUiProxy().startRecentsActivity(intent, options,
                     mCallbacks, gestureState.useSyntheticRecentsTransition());
             RecentsDisplayModel.getINSTANCE().get(mCtx)
-                    .getRecentsWindowManager(mDeviceState.getDisplayId())
+                    .getRecentsWindowManager(gestureState.getDisplayId())
                     .startRecentsWindow(mCallbacks);
         } else {
             mRecentsAnimationStartPending = getSystemUiProxy().startRecentsActivity(intent,
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.kt b/quickstep/src/com/android/quickstep/TaskIconCache.kt
index 6a7f1af..f0b9b7b 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.kt
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.kt
@@ -72,6 +72,8 @@
     var taskVisualsChangeListener: TaskVisualsChangeListener? = null
 
     init {
+        // TODO (b/397205964): this will need to be updated when we support caches for different
+        //  displays.
         displayController.addChangeListener(this)
     }
 
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index a594e49..df66a5e 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -42,7 +42,7 @@
 import com.android.launcher3.util.ResourceBasedOverride;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.Snackbar;
-import com.android.quickstep.task.util.TaskOverlayHelper;
+import com.android.quickstep.recents.domain.usecase.ThumbnailPosition;
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.views.DesktopTaskView;
 import com.android.quickstep.views.GroupedTaskView;
@@ -52,6 +52,7 @@
 import com.android.quickstep.views.TaskContainer;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.ThumbnailData;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -134,43 +135,50 @@
 
         private T mActionsView;
         protected ImageActionsApi mImageApi;
-        protected TaskOverlayHelper mHelper;
+        private ThumbnailData mThumbnailData = null;
 
         protected TaskOverlay(TaskContainer taskContainer) {
             mApplicationContext = taskContainer.getTaskView().getContext().getApplicationContext();
             mTaskContainer = taskContainer;
-            if (enableRefactorTaskThumbnail()) {
-                mHelper = new TaskOverlayHelper(mTaskContainer.getTask(), this);
-            }
             mImageApi = new ImageActionsApi(mApplicationContext, this::getThumbnail);
         }
 
-        /**
-         * Initialize the overlay when a Task is bound to the TaskView.
-         */
-        public void init() {
-            if (enableRefactorTaskThumbnail()) {
-                mHelper.init();
-            }
-        }
-
-        /**
-         * Destroy the overlay when the TaskView is recycled.
-         */
-        public void destroy() {
-            if (enableRefactorTaskThumbnail()) {
-                mHelper.destroy();
-            }
+        public void setThumbnailState(@Nullable ThumbnailData thumbnailData) {
+            mThumbnailData = thumbnailData;
         }
 
         protected @Nullable Bitmap getThumbnail() {
-            return enableRefactorTaskThumbnail() ? mHelper.getEnabledState().getThumbnail()
-                    : mTaskContainer.getThumbnailViewDeprecated().getThumbnail();
+            if (enableRefactorTaskThumbnail()) {
+                return mThumbnailData == null ? null : mThumbnailData.getThumbnail();
+            } else {
+                return mTaskContainer.getThumbnailViewDeprecated().getThumbnail();
+            }
+        }
+        /**
+         * Returns whether the snapshot is real. If the device is locked for the user of the task,
+         * the snapshot used will be an app-theme generated snapshot instead of a real snapshot.
+         */
+        protected boolean isRealSnapshot() {
+            if (enableRefactorTaskThumbnail()) {
+                if (mThumbnailData == null) return false;
+
+                return mThumbnailData.isRealSnapshot && !mTaskContainer.getTask().isLocked;
+            } else {
+                return mTaskContainer.getThumbnailViewDeprecated().isRealSnapshot();
+            }
         }
 
-        protected boolean isRealSnapshot() {
-            return enableRefactorTaskThumbnail() ? mHelper.getEnabledState().isRealSnapshot()
-                    : mTaskContainer.getThumbnailViewDeprecated().isRealSnapshot();
+        /**
+         * Returns whether the snapshot is rotated compared to the current task orientation.
+         */
+        public boolean isThumbnailRotationDifferentFromTask() {
+            if (enableRefactorTaskThumbnail()) {
+                ThumbnailPosition thumbnailPosition = mTaskContainer.getThumbnailPosition();
+                return thumbnailPosition != null && thumbnailPosition.isRotated();
+            }
+
+            return mTaskContainer.getThumbnailViewDeprecated()
+                    .isThumbnailRotationDifferentFromTask();
         }
 
         protected T getActionsView() {
@@ -316,9 +324,16 @@
             // inverse tells us where the view would be in the bitmaps coordinates. The insets are
             // the difference between the bitmap bounds and the projected view bounds.
             Matrix boundsToBitmapSpace = new Matrix();
-            Matrix thumbnailMatrix = enableRefactorTaskThumbnail()
-                    ? mHelper.getThumbnailMatrix()
-                    : mTaskContainer.getThumbnailViewDeprecated().getThumbnailMatrix();
+            Matrix thumbnailMatrix;
+            if (enableRefactorTaskThumbnail()) {
+                if (mTaskContainer.getThumbnailPosition() != null) {
+                    thumbnailMatrix = mTaskContainer.getThumbnailPosition().getMatrix();
+                } else {
+                    thumbnailMatrix = Matrix.IDENTITY_MATRIX;
+                }
+            } else {
+                thumbnailMatrix = mTaskContainer.getThumbnailViewDeprecated().getThumbnailMatrix();
+            }
             thumbnailMatrix.invert(boundsToBitmapSpace);
             RectF boundsInBitmapSpace = new RectF();
             boundsToBitmapSpace.mapRect(boundsInBitmapSpace, viewRect);
@@ -359,13 +374,10 @@
 
         private class ScreenshotSystemShortcut extends SystemShortcut {
 
-            private final RecentsViewContainer mContainer;
-
             ScreenshotSystemShortcut(RecentsViewContainer container, ItemInfo itemInfo,
                     View originalView) {
                 super(R.drawable.ic_screenshot, R.string.action_screenshot, container, itemInfo,
                         originalView);
-                mContainer = container;
             }
 
             @Override
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index f92581e..bc46ace 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -20,7 +20,9 @@
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.view.Surface.ROTATION_0;
 
+import static com.android.launcher3.Flags.enableGridOnlyOverview;
 import static com.android.launcher3.Flags.enableRefactorTaskThumbnail;
+import static com.android.launcher3.Flags.enableShowEnabledShortcutsInAccessibilityMenu;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_CLOSE_APP_TAP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
@@ -42,7 +44,6 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Flags;
 import com.android.launcher3.R;
 import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
 import com.android.launcher3.model.WellbeingModel;
@@ -345,15 +346,20 @@
             boolean isTaskSplitNotSupported = !task.isDockable ||
                     (intentFlags & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
             boolean hideForExistingMultiWindow = container.getDeviceProfile().isMultiWindowMode;
-            boolean isLargeTile = deviceProfile.isTablet && taskView.isLargeTile();
-            boolean isTaskInExpectedScrollPosition =
-                    recentsView.isTaskInExpectedScrollPosition(taskView);
 
-            if (notEnoughTasksToSplit || isTaskSplitNotSupported || hideForExistingMultiWindow
-                    || (isLargeTile && isTaskInExpectedScrollPosition)) {
+            if (notEnoughTasksToSplit || isTaskSplitNotSupported || hideForExistingMultiWindow) {
                 return null;
             }
 
+            if (!enableShowEnabledShortcutsInAccessibilityMenu()) {
+                boolean isLargeTile = deviceProfile.isTablet && taskView.isLargeTile();
+                boolean isTaskInExpectedScrollPosition =
+                        recentsView.isTaskInExpectedScrollPosition(taskView);
+                if (isLargeTile && isTaskInExpectedScrollPosition) {
+                    return null;
+                }
+            }
+
             return orientationHandler.getSplitPositionOptions(deviceProfile)
                     .stream()
                     .map((Function<SplitPositionOption, SystemShortcut>) option ->
@@ -500,16 +506,23 @@
         @Override
         public List<SystemShortcut> getShortcuts(RecentsViewContainer container,
                 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().getOrientedState();
-                boolean isFakeLandscape = !orientedState.isRecentsActivityRotationAllowed()
-                        && orientedState.getTouchRotation() != ROTATION_0;
-                if (!isFakeLandscape) {
+            if (enableShowEnabledShortcutsInAccessibilityMenu()) {
+                if (!taskContainer.getOverlay().isRealSnapshot()) {
                     return null;
                 }
+            } else {
+                boolean isTablet = container.getDeviceProfile().isTablet;
+                boolean isGridOnlyOverview = isTablet && enableGridOnlyOverview();
+                // Extra conditions if it's not grid-only overview
+                if (!isGridOnlyOverview) {
+                    RecentsOrientedState orientedState = taskContainer.getTaskView()
+                            .getOrientedState();
+                    boolean isFakeLandscape = !orientedState.isRecentsActivityRotationAllowed()
+                            && orientedState.getTouchRotation() != ROTATION_0;
+                    if (!isFakeLandscape) {
+                        return null;
+                    }
+                }
             }
 
             SystemShortcut screenshotShortcut = taskContainer.getOverlay().getScreenshotShortcut(
@@ -527,10 +540,39 @@
         @Override
         public List<SystemShortcut> getShortcuts(RecentsViewContainer container,
                 TaskContainer taskContainer) {
-            boolean isTablet = container.getDeviceProfile().isTablet;
-            boolean isGridOnlyOverview = isTablet && Flags.enableGridOnlyOverview();
-            if (!isGridOnlyOverview) {
-                return null;
+            if (enableShowEnabledShortcutsInAccessibilityMenu()) {
+                if (!taskContainer.getOverlay().isRealSnapshot()) {
+                    return null;
+                }
+
+                // Modal only works with grid size tiles with enableGridOnlyOverview enabled on
+                // tablets / foldables. With enableGridOnlyOverview off, for large tiles it works,
+                // but the tile needs to be in the center of Recents / Overview.
+                boolean isTablet = container.getDeviceProfile().isTablet;
+                RecentsView recentsView = container.getOverviewPanel();
+                boolean isLargeTileInCenterOfOverview = taskContainer.getTaskView().isLargeTile()
+                        && recentsView.isFocusedTaskInExpectedScrollPosition();
+                if (isTablet
+                        && !isLargeTileInCenterOfOverview
+                        && !enableGridOnlyOverview()) {
+                    return null;
+                }
+
+                boolean isFakeLandscape = !taskContainer.getTaskView().getPagedOrientationHandler()
+                        .isLayoutNaturalToLauncher();
+                if (isFakeLandscape) {
+                    return null;
+                }
+
+                if (taskContainer.getOverlay().isThumbnailRotationDifferentFromTask()) {
+                    return null;
+                }
+            } else {
+                boolean isTablet = container.getDeviceProfile().isTablet;
+                boolean isGridOnlyOverview = isTablet && enableGridOnlyOverview();
+                if (!isGridOnlyOverview) {
+                    return null;
+                }
             }
 
             SystemShortcut modalStateSystemShortcut =
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 37c2d1c..855ff98 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -22,6 +22,7 @@
 import static com.android.app.animation.Interpolators.LINEAR;
 import static com.android.app.animation.Interpolators.TOUCH_RESPONSE;
 import static com.android.app.animation.Interpolators.clampToProgress;
+import static com.android.launcher3.Flags.enableDesktopExplodedView;
 import static com.android.launcher3.Flags.enableGridOnlyOverview;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
@@ -201,6 +202,9 @@
                     recentsView.getSizeStrategy(), targets, forDesktop);
             if (forDesktop) {
                 remoteTargetHandles = gluer.assignTargetsForDesktop(targets, transitionInfo);
+                if (enableDesktopExplodedView()) {
+                    ((DesktopTaskView) taskView).setRemoteTargetHandles(remoteTargetHandles);
+                }
             } else if (taskView.containsMultipleTasks()) {
                 remoteTargetHandles = gluer.assignTargetsForSplitScreen(targets,
                         ((GroupedTaskView) taskView).getSplitBoundsConfig());
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index ba4c65a..741ae7d 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -15,6 +15,7 @@
  */
 package com.android.quickstep;
 
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_MOVE;
@@ -40,6 +41,7 @@
 
 import android.app.PendingIntent;
 import android.app.Service;
+import android.content.Context;
 import android.content.IIntentReceiver;
 import android.content.IIntentSender;
 import android.content.Intent;
@@ -53,9 +55,11 @@
 import android.os.SystemClock;
 import android.util.Log;
 import android.view.Choreographer;
+import android.view.Display;
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.MotionEvent;
+import android.window.DesktopModeFlags;
 
 import androidx.annotation.BinderThread;
 import androidx.annotation.NonNull;
@@ -89,6 +93,8 @@
 import com.android.quickstep.OverviewCommandHelper.CommandType;
 import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
 import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsDisplayModel.RecentsDisplayResource;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
 import com.android.quickstep.fallback.window.RecentsWindowSwipeHandler;
 import com.android.quickstep.inputconsumers.BubbleBarInputConsumer;
 import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer;
@@ -124,6 +130,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
+import java.util.Locale;
 import java.util.function.Consumer;
 import java.util.function.Function;
 
@@ -139,6 +146,9 @@
     private static final ConstantItem<Boolean> HAS_ENABLED_QUICKSTEP_ONCE = backedUpItem(
             "launcher.has_enabled_quickstep_once", false, EncryptionType.ENCRYPTED);
 
+    private static final DesktopModeFlags.DesktopModeFlag ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS =
+            new DesktopModeFlags.DesktopModeFlag(Flags::enableGestureNavOnConnectedDisplays, false);
+
     private final TISBinder mTISBinder = new TISBinder(this);
 
     /**
@@ -274,11 +284,12 @@
         }
 
         @BinderThread
-        public void onSystemUiStateChanged(@SystemUiStateFlags long stateFlags) {
+        public void onSystemUiStateChanged(@SystemUiStateFlags long stateFlags, int displayId) {
             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
-                long lastFlags = tis.mDeviceState.getSystemUiStateFlags();
-                tis.mDeviceState.setSystemUiFlags(stateFlags);
-                tis.onSystemUiFlagsChanged(lastFlags);
+                // Last flags is only used for the default display case.
+                long lastFlags = tis.mDeviceState.getSysuiStateFlag();
+                tis.mDeviceState.setSysUIStateFlagsForDisplay(stateFlags, displayId);
+                tis.onSystemUiFlagsChanged(lastFlags, displayId);
             }));
         }
 
@@ -292,8 +303,9 @@
         @Override
         public void enterStageSplitFromRunningApp(boolean leftOrTop) {
             executeForTouchInteractionService(tis -> {
+                // TODO (b/397942185): support external displays
                 RecentsViewContainer container = tis.mOverviewComponentObserver
-                        .getContainerInterface().getCreatedContainer();
+                        .getContainerInterface(DEFAULT_DISPLAY).getCreatedContainer();
                 if (container != null) {
                     container.enterStageSplitFromRunningApp(leftOrTop);
                 }
@@ -312,6 +324,9 @@
         public void onDisplayRemoved(int displayId) {
             executeForTaskbarManager(taskbarManager ->
                     taskbarManager.onDisplayRemoved(displayId));
+            executeForTouchInteractionService(tis -> {
+                tis.mDeviceState.clearSysUIStateFlagsForDisplay(displayId);
+            });
         }
 
         @BinderThread
@@ -550,6 +565,7 @@
     private @Nullable ResetGestureInputConsumer mResetGestureInputConsumer;
     private GestureState mGestureState = DEFAULT_STATE;
 
+    private InputMonitorDisplayModel mInputMonitorDisplayModel;
     private InputMonitorCompat mInputMonitorCompat;
     private InputEventReceiver mInputEventReceiver;
 
@@ -564,6 +580,8 @@
 
     private DisplayController.DisplayInfoChangeListener mDisplayInfoChangeListener;
 
+    private RecentsDisplayModel mRecentsDisplayModel;
+
     @Override
     public void onCreate() {
         super.onCreate();
@@ -574,6 +592,7 @@
         mMainChoreographer = Choreographer.getInstance();
         mDeviceState = RecentsAnimationDeviceState.INSTANCE.get(this);
         mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(this);
+        mRecentsDisplayModel = RecentsDisplayModel.getINSTANCE().get(this);
         mAllAppsActionManager = new AllAppsActionManager(
                 this, UI_HELPER_EXECUTOR, this::createAllAppsPendingIntent);
         mTrackpadsConnected = new ActiveTrackpadList(this, () -> {
@@ -586,7 +605,7 @@
         });
 
         mTaskbarManager = new TaskbarManager(this, mAllAppsActionManager, mNavCallbacks,
-                RecentsDisplayModel.getINSTANCE().get(this));
+                mRecentsDisplayModel);
         mDesktopAppLaunchTransitionManager =
                 new DesktopAppLaunchTransitionManager(this, SystemUiProxy.INSTANCE.get(this));
         mDesktopAppLaunchTransitionManager.registerTransitions();
@@ -599,9 +618,34 @@
         ScreenOnTracker.INSTANCE.get(this).addListener(mScreenOnListener);
     }
 
+    @Nullable
+    private InputEventReceiver getInputEventReceiver(int displayId) {
+        if (ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS.isTrue()) {
+            InputMonitorResource inputMonitorResource = mInputMonitorDisplayModel == null
+                    ? null : mInputMonitorDisplayModel.getDisplayResource(displayId);
+            return inputMonitorResource == null ? null : inputMonitorResource.inputEventReceiver;
+        }
+        return mInputEventReceiver;
+    }
+
+    @Nullable
+    private InputMonitorCompat getInputMonitorCompat(int displayId) {
+        if (ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS.isTrue()) {
+            InputMonitorResource inputMonitorResource = mInputMonitorDisplayModel == null
+                    ? null : mInputMonitorDisplayModel.getDisplayResource(displayId);
+            return inputMonitorResource == null ? null : inputMonitorResource.inputMonitorCompat;
+        }
+        return mInputMonitorCompat;
+    }
+
     private void disposeEventHandlers(String reason) {
         Log.d(TAG, "disposeEventHandlers: Reason: " + reason
                 + " instance=" + System.identityHashCode(this));
+        if (ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS.isTrue()) {
+            if (mInputMonitorDisplayModel == null) return;
+            mInputMonitorDisplayModel.destroy();
+            return;
+        }
         if (mInputEventReceiver != null) {
             mInputEventReceiver.dispose();
             mInputEventReceiver = null;
@@ -620,10 +664,13 @@
                 && (mTrackpadsConnected.isEmpty())) {
             return;
         }
-
-        mInputMonitorCompat = new InputMonitorCompat("swipe-up", mDeviceState.getDisplayId());
-        mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(),
-                mMainChoreographer, this::onInputEvent);
+        if (ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS.isTrue()) {
+            mInputMonitorDisplayModel = new InputMonitorDisplayModel(this);
+        } else {
+            mInputMonitorCompat = new InputMonitorCompat("swipe-up", Display.DEFAULT_DISPLAY);
+            mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(),
+                    mMainChoreographer, this::onInputEvent);
+        }
 
         mRotationTouchHelper.updateGestureTouchRegions();
     }
@@ -643,13 +690,14 @@
         mTaskAnimationManager = new TaskAnimationManager(this, mDeviceState);
         mOverviewComponentObserver = OverviewComponentObserver.INSTANCE.get(this);
         mOverviewCommandHelper = new OverviewCommandHelper(this,
-                mOverviewComponentObserver, mTaskAnimationManager,
-                RecentsDisplayModel.getINSTANCE().get(this),
+                mOverviewComponentObserver, mTaskAnimationManager, mRecentsDisplayModel,
                 SystemUiProxy.INSTANCE.get(this).getFocusState(), mTaskbarManager);
         mResetGestureInputConsumer = new ResetGestureInputConsumer(
                 mTaskAnimationManager, mTaskbarManager::getCurrentActivityContext);
         mInputConsumer.registerInputConsumer();
-        onSystemUiFlagsChanged(mDeviceState.getSystemUiStateFlags());
+        for (int displayId : mDeviceState.getDisplaysWithSysUIState()) {
+            onSystemUiFlagsChanged(mDeviceState.getSystemUiStateFlags(displayId), displayId);
+        }
         onAssistantVisibilityChanged();
 
         // Initialize the task tracker
@@ -687,8 +735,11 @@
 
     private void onOverviewTargetChanged(boolean isHomeAndOverviewSame) {
         mAllAppsActionManager.setHomeAndOverviewSame(isHomeAndOverviewSame);
+        // TODO (b/399089118): how will this work with per-display Taskbars? Is using the
+        //  default-display container ok?
         RecentsViewContainer newOverviewContainer =
-                mOverviewComponentObserver.getContainerInterface().getCreatedContainer();
+                mOverviewComponentObserver.getContainerInterface(
+                        DEFAULT_DISPLAY).getCreatedContainer();
         if (newOverviewContainer != null) {
             if (newOverviewContainer instanceof StatefulActivity activity) {
                 // This will also call setRecentsViewContainer() internally.
@@ -705,26 +756,33 @@
             public void send(int code, Intent intent, String resolvedType,
                     IBinder allowlistToken, IIntentReceiver finishedReceiver,
                     String requiredPermission, Bundle options) {
-                MAIN_EXECUTOR.execute(() -> mTaskbarManager.toggleAllApps());
+                MAIN_EXECUTOR.execute(() -> mTaskbarManager.toggleAllAppsSearch());
             }
         });
     }
 
     @UiThread
-    private void onSystemUiFlagsChanged(@SystemUiStateFlags long lastSysUIFlags) {
+    private void onSystemUiFlagsChanged(@SystemUiStateFlags long lastSysUIFlags, int displayId) {
         if (LockedUserState.get(this).isUserUnlocked()) {
-            long systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
-            SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
-            mOverviewComponentObserver.setHomeDisabled(mDeviceState.isHomeDisabled());
-            mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags);
-            mTaskAnimationManager.onSystemUiFlagsChanged(lastSysUIFlags, systemUiStateFlags);
+            long systemUiStateFlags = mDeviceState.getSystemUiStateFlags(displayId);
+            mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags, displayId);
+            if (displayId == Display.DEFAULT_DISPLAY) {
+                // The following don't care about non-default displays, at least for now. If they
+                // ever will, they should be taken care of.
+                SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
+                mOverviewComponentObserver.setHomeDisabled(mDeviceState.isHomeDisabled());
+                // TODO b/399371607 - Propagate to taskAnimationManager once overview is multi
+                //  display.
+                mTaskAnimationManager.onSystemUiFlagsChanged(lastSysUIFlags, systemUiStateFlags);
+            }
         }
     }
 
     @UiThread
     private void onAssistantVisibilityChanged() {
         if (LockedUserState.get(this).isUserUnlocked()) {
-            mOverviewComponentObserver.getContainerInterface().onAssistantVisibilityChanged(
+            mOverviewComponentObserver.getContainerInterface(
+                    DEFAULT_DISPLAY).onAssistantVisibilityChanged(
                     mDeviceState.getAssistantVisibility());
         }
     }
@@ -774,8 +832,9 @@
     }
 
     private void onInputEvent(InputEvent ev) {
+        int displayId = ev.getDisplayId();
         if (!(ev instanceof MotionEvent)) {
-            ActiveGestureProtoLogProxy.logUnknownInputEvent(ev.toString());
+            ActiveGestureProtoLogProxy.logUnknownInputEvent(displayId, ev.toString());
             return;
         }
         MotionEvent event = (MotionEvent) ev;
@@ -784,19 +843,19 @@
                 TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event);
 
         if (!LockedUserState.get(this).isUserUnlocked()) {
-            ActiveGestureProtoLogProxy.logOnInputEventUserLocked();
+            ActiveGestureProtoLogProxy.logOnInputEventUserLocked(displayId);
             return;
         }
 
         NavigationMode currentNavMode = mDeviceState.getMode();
         if (mGestureStartNavMode != null && mGestureStartNavMode != currentNavMode) {
             ActiveGestureProtoLogProxy.logOnInputEventNavModeSwitched(
-                    mGestureStartNavMode.name(), currentNavMode.name());
+                    displayId, mGestureStartNavMode.name(), currentNavMode.name());
             event.setAction(ACTION_CANCEL);
         } else if (mDeviceState.isButtonNavMode()
                 && !mDeviceState.supportsAssistantGestureInButtonNav()
                 && !isTrackpadMotionEvent(event)) {
-            ActiveGestureProtoLogProxy.logOnInputEventThreeButtonNav();
+            ActiveGestureProtoLogProxy.logOnInputEventThreeButtonNav(displayId);
             return;
         }
 
@@ -812,12 +871,15 @@
             }
             if (mTaskAnimationManager.shouldIgnoreMotionEvents()) {
                 if (action == ACTION_DOWN || isHoverActionWithoutConsumer) {
-                    ActiveGestureProtoLogProxy.logOnInputIgnoringFollowingEvents();
+                    ActiveGestureProtoLogProxy.logOnInputIgnoringFollowingEvents(displayId);
                 }
                 return;
             }
         }
 
+        InputMonitorCompat inputMonitorCompat = getInputMonitorCompat(displayId);
+        InputEventReceiver inputEventReceiver = getInputEventReceiver(displayId);
+
         if (action == ACTION_DOWN || isHoverActionWithoutConsumer) {
             mGestureStartNavMode = currentNavMode;
         } else if (action == ACTION_UP || action == ACTION_CANCEL) {
@@ -844,10 +906,14 @@
                 if (mDeviceState.canTriggerAssistantAction(event)) {
                     reasonString.append(" and event can trigger assistant action, "
                             + "consuming gesture for assistant action");
-                    mGestureState =
-                            createGestureState(mGestureState, getTrackpadGestureType(event));
+                    mGestureState = createGestureState(
+                            displayId, mGestureState, getTrackpadGestureType(event));
                     mUncheckedConsumer = tryCreateAssistantInputConsumer(
-                            this, mDeviceState, mInputMonitorCompat, mGestureState, event);
+                            this,
+                            mDeviceState,
+                            inputMonitorCompat,
+                            mGestureState,
+                            event);
                 } else {
                     reasonString.append(" but event cannot trigger Assistant, "
                             + "consuming gesture as no-op");
@@ -862,8 +928,8 @@
                 // Clone the previous gesture state since onConsumerAboutToBeSwitched might trigger
                 // onConsumerInactive and wipe the previous gesture state
                 GestureState prevGestureState = new GestureState(mGestureState);
-                GestureState newGestureState = createGestureState(mGestureState,
-                        getTrackpadGestureType(event));
+                GestureState newGestureState = createGestureState(
+                        displayId, mGestureState, getTrackpadGestureType(event));
                 mConsumer.onConsumerAboutToBeSwitched();
                 mGestureState = newGestureState;
                 mConsumer = newConsumer(
@@ -874,10 +940,10 @@
                         prevGestureState,
                         mGestureState,
                         mTaskAnimationManager,
-                        mInputMonitorCompat,
+                        inputMonitorCompat,
                         getSwipeUpHandlerFactory(),
                         this::onConsumerInactive,
-                        mInputEventReceiver,
+                        inputEventReceiver,
                         mTaskbarManager,
                         mSwipeUpProxyProvider,
                         mOverviewCommandHelper,
@@ -890,18 +956,19 @@
                                 + "consuming gesture for assistant action"
                         : "event is a trackpad multi-finger swipe and event can trigger assistant "
                                 + "action, consuming gesture for assistant action");
-                mGestureState = createGestureState(mGestureState, getTrackpadGestureType(event));
+                mGestureState = createGestureState(
+                        displayId, mGestureState, getTrackpadGestureType(event));
                 // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we
                 // should not interrupt it. QuickSwitch assumes that interruption can only
                 // happen if the next gesture is also quick switch.
                 mUncheckedConsumer = tryCreateAssistantInputConsumer(
-                        this, mDeviceState, mInputMonitorCompat, mGestureState, event);
+                        this, mDeviceState, inputMonitorCompat, mGestureState, event);
             } else if (mDeviceState.canTriggerOneHandedAction(event)) {
                 reasonString.append("event can trigger one-handed action, "
                         + "consuming gesture for one-handed action");
                 // Consume gesture event for triggering one handed feature.
-                mUncheckedConsumer = new OneHandedModeInputConsumer(this, mDeviceState,
-                        InputConsumer.NO_OP, mInputMonitorCompat);
+                mUncheckedConsumer = new OneHandedModeInputConsumer(
+                        this, displayId, mDeviceState, InputConsumer.NO_OP, inputMonitorCompat);
             } else {
                 mUncheckedConsumer = InputConsumer.NO_OP;
             }
@@ -916,25 +983,28 @@
         if (mUncheckedConsumer != InputConsumer.NO_OP) {
             switch (action) {
                 case ACTION_DOWN:
-                    ActiveGestureProtoLogProxy.logOnInputEventActionDown(reasonString);
+                    ActiveGestureProtoLogProxy.logOnInputEventActionDown(displayId, reasonString);
                     // fall through
                 case ACTION_UP:
                     ActiveGestureProtoLogProxy.logOnInputEventActionUp(
                             (int) event.getRawX(),
                             (int) event.getRawY(),
                             action,
-                            MotionEvent.classificationToString(event.getClassification()));
+                            MotionEvent.classificationToString(event.getClassification()),
+                            displayId);
                     break;
                 case ACTION_MOVE:
                     ActiveGestureProtoLogProxy.logOnInputEventActionMove(
                             MotionEvent.actionToString(action),
                             MotionEvent.classificationToString(event.getClassification()),
-                            event.getPointerCount());
+                            event.getPointerCount(),
+                            displayId);
                     break;
                 default: {
                     ActiveGestureProtoLogProxy.logOnInputEventGenericAction(
                             MotionEvent.actionToString(action),
-                            MotionEvent.classificationToString(event.getClassification()));
+                            MotionEvent.classificationToString(event.getClassification()),
+                            displayId);
                 }
             }
         }
@@ -958,7 +1028,7 @@
         }
 
         if (cleanUpConsumer) {
-            reset();
+            reset(displayId);
         }
         traceToken.close();
     }
@@ -977,13 +1047,15 @@
         return event.isHoverEvent() && event.getSource() == InputDevice.SOURCE_MOUSE;
     }
 
-    public GestureState createGestureState(GestureState previousGestureState,
+    public GestureState createGestureState(
+            int displayId,
+            GestureState previousGestureState,
             GestureState.TrackpadGestureType trackpadGestureType) {
         final GestureState gestureState;
         TopTaskTracker.CachedTaskInfo taskInfo;
         if (mTaskAnimationManager.isRecentsAnimationRunning()) {
-            gestureState = new GestureState(mOverviewComponentObserver,
-                    ActiveGestureLog.INSTANCE.getLogId());
+            gestureState = new GestureState(
+                    mOverviewComponentObserver, displayId, ActiveGestureLog.INSTANCE.getLogId());
             TopTaskTracker.CachedTaskInfo previousTaskInfo = previousGestureState.getRunningTask();
             // previousTaskInfo can be null iff previousGestureState == GestureState.DEFAULT_STATE
             taskInfo = previousTaskInfo != null
@@ -994,7 +1066,9 @@
             gestureState.updatePreviouslyAppearedTaskIds(
                     previousGestureState.getPreviouslyAppearedTaskIds());
         } else {
-            gestureState = new GestureState(mOverviewComponentObserver,
+            gestureState = new GestureState(
+                    mOverviewComponentObserver,
+                    displayId,
                     ActiveGestureLog.INSTANCE.incrementLogId());
             taskInfo = TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false);
             gestureState.updateRunningTask(taskInfo);
@@ -1008,10 +1082,9 @@
     }
 
     public AbsSwipeUpHandler.Factory getSwipeUpHandlerFactory() {
-        boolean recentsInWindow =
-                Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow();
         return mOverviewComponentObserver.isHomeAndOverviewSame()
-                ? mLauncherSwipeHandlerFactory : (recentsInWindow
+                ? mLauncherSwipeHandlerFactory
+                : (RecentsWindowFlags.Companion.getEnableOverviewInWindow()
                 ? mRecentsWindowSwipeHandlerFactory : mFallbackSwipeHandlerFactory);
     }
 
@@ -1022,17 +1095,18 @@
      */
     private void onConsumerInactive(InputConsumer caller) {
         if (mConsumer != null && mConsumer.getActiveConsumerInHierarchy() == caller) {
-            reset();
+            reset(caller.getDisplayId());
         }
     }
 
-    private void reset() {
+    private void reset(int displayId) {
         mConsumer = mUncheckedConsumer = getDefaultInputConsumer();
         mGestureState = DEFAULT_STATE;
         // By default, use batching of the input events, but check receiver before using in the rare
         // case that the monitor was disposed before the swipe settled
-        if (mInputEventReceiver != null) {
-            mInputEventReceiver.setBatchingEnabled(true);
+        InputEventReceiver inputEventReceiver = getInputEventReceiver(displayId);
+        if (inputEventReceiver != null) {
+            inputEventReceiver.setBatchingEnabled(true);
         }
     }
 
@@ -1064,8 +1138,10 @@
         if (!LockedUserState.get(this).isUserUnlocked()) {
             return;
         }
+        // TODO (b/399094853): handle config updates for all connected displays (relevant only for
+        // gestures on external displays)
         final BaseContainerInterface containerInterface =
-                mOverviewComponentObserver.getContainerInterface();
+                mOverviewComponentObserver.getContainerInterface(DEFAULT_DISPLAY);
         final RecentsViewContainer container = containerInterface.getCreatedContainer();
         if (container == null || container.isStarted()) {
             // We only care about the existing background activity.
@@ -1112,23 +1188,33 @@
         pw.println("Input state:");
         pw.println("\tmInputMonitorCompat=" + mInputMonitorCompat);
         pw.println("\tmInputEventReceiver=" + mInputEventReceiver);
+        if (mInputMonitorDisplayModel == null) {
+            pw.println("\tmInputMonitorDisplayModel=null");
+        } else {
+            mInputMonitorDisplayModel.dump("\t", pw);
+        }
         DisplayController.INSTANCE.get(this).dump(pw);
-        pw.println("TouchState:");
-        RecentsViewContainer createdOverviewContainer = mOverviewComponentObserver == null ? null
-                : mOverviewComponentObserver.getContainerInterface().getCreatedContainer();
-        boolean resumed = mOverviewComponentObserver != null
-                && mOverviewComponentObserver.getContainerInterface().isResumed();
-        pw.println("\tcreatedOverviewActivity=" + createdOverviewContainer);
-        pw.println("\tresumed=" + resumed);
+        for (RecentsDisplayResource resource : mRecentsDisplayModel.getActiveDisplayResources()) {
+            int displayId = resource.getDisplayId();
+            pw.println(String.format(Locale.ENGLISH, "TouchState (displayId %d):", displayId));
+            RecentsViewContainer createdOverviewContainer =
+                    mOverviewComponentObserver == null ? null
+                            : mOverviewComponentObserver.getContainerInterface(
+                                    displayId).getCreatedContainer();
+            boolean resumed = mOverviewComponentObserver != null
+                    && mOverviewComponentObserver.getContainerInterface(displayId).isResumed();
+            pw.println("\tcreatedOverviewActivity=" + createdOverviewContainer);
+            pw.println("\tresumed=" + resumed);
+            if (createdOverviewContainer != null) {
+                createdOverviewContainer.getDeviceProfile().dump(this, "", pw);
+            }
+        }
         pw.println("\tmConsumer=" + mConsumer.getName());
         ActiveGestureLog.INSTANCE.dump("", pw);
         RecentsModel.INSTANCE.get(this).dump("", pw);
         if (mTaskAnimationManager != null) {
             mTaskAnimationManager.dump("", pw);
         }
-        if (createdOverviewContainer != null) {
-            createdOverviewContainer.getDeviceProfile().dump(this, "", pw);
-        }
         mTaskbarManager.dumpLogs("", pw);
         DesktopVisibilityController.INSTANCE.get(this).dumpLogs("", pw);
         pw.println("ContextualSearchStateManager:");
@@ -1158,4 +1244,53 @@
                 gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
                 mInputConsumer, MSDLPlayerWrapper.INSTANCE.get(this));
     }
+
+    /**
+     * Helper class that keeps track of external displays and prepares input monitors for each.
+     */
+    private class InputMonitorDisplayModel extends DisplayModel<InputMonitorResource> {
+
+        private InputMonitorDisplayModel(Context context) {
+            super(context);
+            registerDisplayListener();
+        }
+
+        @NonNull
+        @Override
+        public InputMonitorResource createDisplayResource(@NonNull Display display) {
+            return new InputMonitorResource(display.getDisplayId());
+        }
+    }
+
+    private class InputMonitorResource extends DisplayModel.DisplayResource {
+
+        private final int displayId;
+
+        private final InputMonitorCompat inputMonitorCompat;
+        private final InputEventReceiver inputEventReceiver;
+
+        private InputMonitorResource(int displayId) {
+            this.displayId = displayId;
+            inputMonitorCompat = new InputMonitorCompat("swipe-up", displayId);
+            inputEventReceiver = inputMonitorCompat.getInputReceiver(
+                    Looper.getMainLooper(),
+                    TouchInteractionService.this.mMainChoreographer,
+                    TouchInteractionService.this::onInputEvent);
+        }
+
+        @Override
+        public void cleanup() {
+            inputEventReceiver.dispose();
+            inputMonitorCompat.dispose();
+        }
+
+        @Override
+        public void dump(String prefix , PrintWriter writer) {
+            writer.println(prefix + "InputMonitorResource:");
+
+            writer.println(prefix + "\tdisplayId=" + displayId);
+            writer.println(prefix + "\tinputMonitorCompat=" + inputMonitorCompat);
+            writer.println(prefix + "\tinputEventReceiver=" + inputEventReceiver);
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index 56dd696..554cea2 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -51,6 +51,7 @@
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
+import com.android.quickstep.views.AddDesktopButton;
 import com.android.quickstep.views.ClearAllButton;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.RecentsViewContainer;
@@ -97,8 +98,13 @@
     private void setProperties(RecentsState state, StateAnimationConfig config,
             PropertySetter setter) {
         float clearAllButtonAlpha = state.hasClearAllButton() ? 1 : 0;
-        setter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
-                clearAllButtonAlpha, LINEAR);
+        setter.setFloat(mRecentsView.getClearAllButton(),
+                ClearAllButton.VISIBILITY_ALPHA, clearAllButtonAlpha, LINEAR);
+        if (mRecentsView.getAddDeskButton() != null) {
+            float addDeskButtonAlpha = state.hasAddDeskButton() ? 1 : 0;
+            setter.setFloat(mRecentsView.getAddDeskButton(), AddDesktopButton.VISIBILITY_ALPHA,
+                    addDeskButtonAlpha, LINEAR);
+        }
         float overviewButtonAlpha = state.hasOverviewActions() ? 1 : 0;
         setter.setFloat(mRecentsViewContainer.getActionsView().getVisibilityAlpha(),
                 AnimatedFloat.VALUE, overviewButtonAlpha, LINEAR);
@@ -126,7 +132,8 @@
                     getOverviewInterpolator(state));
         }
         if (enableDesktopExplodedView()) {
-            setter.setFloat(mRecentsView, DESK_EXPLODE_PROGRESS, showAsGrid ? 1f : 0f,
+            setter.setFloat(mRecentsView, DESK_EXPLODE_PROGRESS,
+                    state.showExplodedDesktopView() ? 1f : 0f,
                     getOverviewInterpolator(state));
         }
 
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index f426bf5..8ec97ed 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -30,7 +30,6 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Flags;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.desktop.DesktopRecentsTransitionController;
@@ -46,6 +45,7 @@
 import com.android.quickstep.GestureState;
 import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
 import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
 import com.android.quickstep.util.GroupTask;
 import com.android.quickstep.util.SingleTask;
 import com.android.quickstep.util.SplitSelectStateController;
@@ -80,9 +80,10 @@
 
     @Override
     public BaseContainerInterface<RecentsState, ?> getContainerInterface(int displayId) {
-        return (Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow())
+        return RecentsWindowFlags.Companion.getEnableOverviewInWindow()
                 ? RecentsDisplayModel.getINSTANCE().get(mContext)
-                .getFallbackWindowInterface(displayId) : FallbackActivityInterface.INSTANCE;
+                        .getFallbackWindowInterface(displayId)
+                : FallbackActivityInterface.INSTANCE;
     }
 
     @Override
@@ -136,7 +137,8 @@
             if (tv != null) {
                 PendingAnimation pa = new PendingAnimation(TASK_DISMISS_DURATION);
                 createTaskDismissAnimation(pa, tv, true, false,
-                        TASK_DISMISS_DURATION, false /* dismissingForSplitSelection*/);
+                        TASK_DISMISS_DURATION, false /* dismissingForSplitSelection*/,
+                        false /* isExpressiveDismiss */);
                 pa.addEndListener(e -> setCurrentTask(-1));
                 AnimatorPlaybackController controller = pa.createPlaybackController();
                 controller.dispatchOnStart();
@@ -289,8 +291,7 @@
         }
 
         // disabling this so app icons aren't drawn on top of recent tasks.
-        if (isOverlayEnabled && !(Flags.enableFallbackOverviewInWindow()
-                || Flags.enableLauncherOverviewInWindow())) {
+        if (isOverlayEnabled && !RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
             runActionOnRemoteHandles(remoteTargetHandle ->
                     remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
         }
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index f27b60c..2c1a4eb 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -15,6 +15,7 @@
  */
 package com.android.quickstep.fallback;
 
+import static com.android.launcher3.Flags.enableDesktopExplodedView;
 import static com.android.launcher3.Flags.enableDesktopWindowingCarouselDetach;
 import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
 import static com.android.launcher3.uioverrides.states.BackgroundAppState.getOverviewScaleAndOffsetForBackgroundState;
@@ -45,15 +46,19 @@
     private static final int FLAG_RECENTS_VIEW_VISIBLE = BaseState.getFlag(7);
     private static final int FLAG_TASK_THUMBNAIL_SPLASH = BaseState.getFlag(8);
     private static final int FLAG_DETACH_DESKTOP_CAROUSEL = BaseState.getFlag(9);
+    private static final int FLAG_ADD_DESK_BUTTON = BaseState.getFlag(10);
+    private static final int FLAG_SHOW_EXPLODED_DESKTOP_VIEW = BaseState.getFlag(11);
 
     private static final RecentsState[] sAllStates = new RecentsState[6];
 
     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_RECENTS_VIEW_VISIBLE);
+                    | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_RECENTS_VIEW_VISIBLE
+                    | FLAG_ADD_DESK_BUTTON | FLAG_SHOW_EXPLODED_DESKTOP_VIEW);
     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_RECENTS_VIEW_VISIBLE);
+            FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_ACTIONS | FLAG_MODAL
+                    | FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_RECENTS_VIEW_VISIBLE
+                    | FLAG_SHOW_EXPLODED_DESKTOP_VIEW);
     public static final RecentsState BACKGROUND_APP = new BackgroundAppState(2,
             FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN
                     | FLAG_RECENTS_VIEW_VISIBLE | FLAG_TASK_THUMBNAIL_SPLASH
@@ -62,7 +67,7 @@
     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_RECENTS_VIEW_VISIBLE | FLAG_CLOSE_POPUPS
-                    | FLAG_DISABLE_RESTORE);
+                    | FLAG_DISABLE_RESTORE | FLAG_SHOW_EXPLODED_DESKTOP_VIEW);
 
     /** Returns the corresponding RecentsState from ordinal provided */
     public static RecentsState stateFromOrdinal(int ordinal) {
@@ -122,6 +127,13 @@
     }
 
     /**
+     * For this state, whether add desk button should be shown.
+     */
+    public boolean hasAddDeskButton() {
+        return hasFlag(FLAG_ADD_DESK_BUTTON);
+    }
+
+    /**
      * For this state, whether overview actions should be shown.
      */
     public boolean hasOverviewActions() {
@@ -165,6 +177,11 @@
         return hasFlag(FLAG_DETACH_DESKTOP_CAROUSEL) && enableDesktopWindowingCarouselDetach();
     }
 
+    @Override
+    public boolean showExplodedDesktopView() {
+        return hasFlag(FLAG_SHOW_EXPLODED_DESKTOP_VIEW) && enableDesktopExplodedView();
+    }
+
     /**
      * True if the state has overview panel visible.
      */
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
index 95a3ec2..12dc177 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
@@ -17,19 +17,19 @@
 package com.android.quickstep.fallback.window
 
 import android.content.Context
-import android.util.Log
 import android.view.Display
-import com.android.launcher3.Flags
+import androidx.core.util.valueIterator
 import com.android.launcher3.dagger.ApplicationContext
 import com.android.launcher3.dagger.LauncherAppSingleton
 import com.android.launcher3.util.DaggerSingletonObject
 import com.android.launcher3.util.DaggerSingletonTracker
-import com.android.launcher3.util.Executors
 import com.android.launcher3.util.WallpaperColorHints
 import com.android.quickstep.DisplayModel
 import com.android.quickstep.FallbackWindowInterface
 import com.android.quickstep.dagger.QuickstepBaseAppComponent
 import com.android.quickstep.fallback.window.RecentsDisplayModel.RecentsDisplayResource
+import com.android.quickstep.fallback.window.RecentsWindowFlags.Companion.enableOverviewInWindow
+import java.io.PrintWriter
 import javax.inject.Inject
 
 @LauncherAppSingleton
@@ -50,50 +50,21 @@
             DaggerSingletonObject<RecentsDisplayModel>(
                 QuickstepBaseAppComponent::getRecentsDisplayModel
             )
-
-        @JvmStatic
-        fun enableOverviewInWindow() =
-            Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow()
     }
 
     init {
-        if (enableOverviewInWindow()) {
-            displayManager.registerDisplayListener(displayListener, Executors.MAIN_EXECUTOR.handler)
-            // In the scenario where displays were added before this display listener was
-            // registered, we should store the RecentsDisplayResources for those displays
-            // directly.
-            displayManager.displays
-                .filter { getDisplayResource(it.displayId) == null }
-                .forEach { storeRecentsDisplayResource(it.displayId, it) }
+        if (enableOverviewInWindow) {
+            registerDisplayListener()
             tracker.addCloseable { destroy() }
         }
     }
 
-    override fun createDisplayResource(displayId: Int) {
-        if (DEBUG) Log.d(TAG, "createDisplayResource: displayId=$displayId")
-        getDisplayResource(displayId)?.let {
-            return
-        }
-        val display = displayManager.getDisplay(displayId)
-        if (display == null) {
-            if (DEBUG)
-                Log.w(
-                    TAG,
-                    "createDisplayResource: could not create display for displayId=$displayId",
-                    Exception(),
-                )
-            return
-        }
-        storeRecentsDisplayResource(displayId, display)
-    }
-
-    private fun storeRecentsDisplayResource(displayId: Int, display: Display) {
-        displayResourceArray[displayId] =
-            RecentsDisplayResource(
-                displayId,
-                context.createDisplayContext(display),
-                wallpaperColorHints.hints,
-            )
+    override fun createDisplayResource(display: Display): RecentsDisplayResource {
+        return RecentsDisplayResource(
+            display.displayId,
+            context.createDisplayContext(display),
+            wallpaperColorHints.hints,
+        )
     }
 
     fun getRecentsWindowManager(displayId: Int): RecentsWindowManager? {
@@ -104,9 +75,15 @@
         return getDisplayResource(displayId)?.fallbackWindowInterface
     }
 
+    val activeDisplayResources: Iterable<RecentsDisplayResource>
+        get() =
+            object : Iterable<RecentsDisplayResource> {
+                override fun iterator() = displayResourceArray.valueIterator()
+            }
+
     data class RecentsDisplayResource(
-        var displayId: Int,
-        var displayContext: Context,
+        val displayId: Int,
+        val displayContext: Context,
         val wallpaperColorHints: Int,
     ) : DisplayResource() {
         val recentsWindowManager = RecentsWindowManager(displayContext, wallpaperColorHints)
@@ -116,5 +93,15 @@
         override fun cleanup() {
             recentsWindowManager.destroy()
         }
+
+        override fun dump(prefix: String, writer: PrintWriter) {
+            writer.println("${prefix}RecentsDisplayResource:")
+
+            writer.println("${prefix}\tdisplayId=${displayId}")
+            writer.println("${prefix}\tdisplayContext=${displayContext}")
+            writer.println("${prefix}\twallpaperColorHints=${wallpaperColorHints}")
+            writer.println("${prefix}\trecentsWindowManager=${recentsWindowManager}")
+            writer.println("${prefix}\tfallbackWindowInterface=${fallbackWindowInterface}")
+        }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt
index cd48136..d70d7eb 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt
@@ -18,17 +18,15 @@
 
 import android.content.Context
 import android.graphics.PixelFormat
-import android.view.ContextThemeWrapper
+import android.view.Display
 import android.view.ViewGroup
 import android.view.WindowManager
 import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
 import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_CONSUME_IME_INSETS
 import com.android.launcher3.DeviceProfile
 import com.android.launcher3.InvariantDeviceProfile
+import com.android.launcher3.util.BaseContext
 import com.android.launcher3.util.Themes
-import com.android.launcher3.views.ActivityContext
-import com.android.launcher3.views.BaseDragLayer
-import com.android.quickstep.fallback.RecentsDragLayer
 
 /**
  * Window context for the Overview overlays.
@@ -36,18 +34,14 @@
  * <p>
  * Overlays have their own window and need a window context.
  */
-open class RecentsWindowContext(windowContext: Context, wallpaperColorHints: Int) :
-    ContextThemeWrapper(
-        windowContext,
-        Themes.getActivityThemeRes(windowContext, wallpaperColorHints),
-    ),
-    ActivityContext {
+abstract class RecentsWindowContext(windowContext: Context, wallpaperColorHints: Int) :
+    BaseContext(
+        base = windowContext,
+        themeResId = Themes.getActivityThemeRes(windowContext, wallpaperColorHints),
+        destroyOnDetach = false,
+    ) {
 
     private var deviceProfile: DeviceProfile? = null
-    private var dragLayer: RecentsDragLayer<RecentsWindowManager> = RecentsDragLayer(this, null)
-    private val deviceProfileChangeListeners:
-        MutableList<DeviceProfile.OnDeviceProfileChangeListener> =
-        ArrayList()
 
     private val windowTitle: String = "RecentsWindow"
 
@@ -57,12 +51,11 @@
             windowTitle,
         )
 
-    override fun getDragLayer(): BaseDragLayer<RecentsWindowManager> {
-        return dragLayer
-    }
-
     fun initDeviceProfile() {
-        deviceProfile = InvariantDeviceProfile.INSTANCE[this].getDeviceProfile(this)
+        deviceProfile =
+            if (displayId == Display.DEFAULT_DISPLAY)
+                InvariantDeviceProfile.INSTANCE[this].getDeviceProfile(this)
+            else InvariantDeviceProfile.INSTANCE[this].createDeviceProfileForSecondaryDisplay(this)
     }
 
     override fun getDeviceProfile(): DeviceProfile {
@@ -72,11 +65,6 @@
         return deviceProfile!!
     }
 
-    override fun getOnDeviceProfileChangeListeners():
-        List<DeviceProfile.OnDeviceProfileChangeListener> {
-        return deviceProfileChangeListeners
-    }
-
     /**
      * Creates LayoutParams for adding a view directly to WindowManager as a new window.
      *
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowFlags.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowFlags.kt
new file mode 100644
index 0000000..9953154
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowFlags.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.fallback.window
+
+import android.window.DesktopModeFlags.DesktopModeFlag
+import com.android.launcher3.Flags
+
+class RecentsWindowFlags {
+    companion object {
+        @JvmField
+        val enableLauncherOverviewInWindow: DesktopModeFlag =
+            DesktopModeFlag(Flags::enableLauncherOverviewInWindow, false)
+
+        @JvmField
+        val enableFallbackOverviewInWindow: DesktopModeFlag =
+            DesktopModeFlag(Flags::enableFallbackOverviewInWindow, false)
+
+        @JvmStatic
+        val enableOverviewInWindow
+            get() = enableLauncherOverviewInWindow.isTrue || enableFallbackOverviewInWindow.isTrue
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
index 1f4961a..1a3a2e3 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
@@ -50,6 +50,7 @@
 import com.android.launcher3.testing.shared.TestProtocol.SEQUENCE_MAIN
 import com.android.launcher3.util.ContextTracker
 import com.android.launcher3.util.DisplayController
+import com.android.launcher3.util.Executors
 import com.android.launcher3.util.RunnableList
 import com.android.launcher3.util.SystemUiController
 import com.android.launcher3.views.BaseDragLayer
@@ -81,7 +82,6 @@
 import com.android.systemui.shared.recents.model.ThumbnailData
 import com.android.systemui.shared.system.TaskStackChangeListener
 import com.android.systemui.shared.system.TaskStackChangeListeners
-import java.util.function.Predicate
 
 /**
  * Class that will manage RecentsView lifecycle within a window and interface correctly where
@@ -135,7 +135,6 @@
     // Callback array that corresponds to events defined in @ActivityEvent
     private val eventCallbacks =
         listOf(RunnableList(), RunnableList(), RunnableList(), RunnableList())
-    private var onInitListener: Predicate<Boolean>? = null
 
     private val animationToHomeFactory =
         RemoteAnimationFactory {
@@ -215,6 +214,7 @@
 
     override fun destroy() {
         super.destroy()
+        Executors.MAIN_EXECUTOR.execute { onViewDestroyed() }
         cleanupRecentsWindow()
         TaskStackChangeListeners.getInstance().unregisterTaskStackListener(taskStackChangeListener)
         callbacks?.removeListener(recentsAnimationListener)
@@ -266,6 +266,7 @@
 
         this.callbacks = callbacks
         callbacks?.addListener(recentsAnimationListener)
+        onViewCreated()
     }
 
     override fun startHome() {
@@ -336,10 +337,6 @@
         return taskbarUIController
     }
 
-    fun registerInitListener(onInitListener: Predicate<Boolean>) {
-        this.onInitListener = onInitListener
-    }
-
     override fun collectStateHandlers(out: MutableList<StateManager.StateHandler<RecentsState?>>?) {
         out!!.add(FallbackRecentsStateController(this))
     }
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
index 5adc960..1d85feb 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
@@ -163,7 +163,7 @@
                 && endTarget == GestureState.GestureEndTarget.HOME;
         if (fromHomeToHome) {
             RecentsWindowManager manager =
-                    mRecentsDisplayModel.getRecentsWindowManager(mDeviceState.getDisplayId());
+                    mRecentsDisplayModel.getRecentsWindowManager(mGestureState.getDisplayId());
             if (manager != null) {
                 manager.startHome(/* finishRecentsAnimation= */ false);
             }
@@ -228,7 +228,7 @@
             recentsCallback = () -> {
                 callback.run();
                 RecentsWindowManager manager =
-                        mRecentsDisplayModel.getRecentsWindowManager(mDeviceState.getDisplayId());
+                        mRecentsDisplayModel.getRecentsWindowManager(mGestureState.getDisplayId());
                 if (manager != null) {
                     manager.startHome();
                 }
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
index 4e5d037..365c80c 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
@@ -56,9 +56,13 @@
     private float mDownY;
     private float mTotalY;
 
-    public AccessibilityInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
-            InputConsumer delegate, InputMonitorCompat inputMonitor) {
-        super(delegate, inputMonitor);
+    public AccessibilityInputConsumer(
+            Context context,
+            int displayId,
+            RecentsAnimationDeviceState deviceState,
+            InputConsumer delegate,
+            InputMonitorCompat inputMonitor) {
+        super(displayId, delegate, inputMonitor);
         mContext = context;
         mVelocityTracker = VelocityTracker.obtain();
         mMinGestureDistance = context.getResources()
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
index 222ccd3..365014d 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
@@ -95,7 +95,7 @@
             InputMonitorCompat inputMonitor,
             RecentsAnimationDeviceState deviceState,
             MotionEvent startEvent) {
-        super(delegate, inputMonitor);
+        super(gestureState.getDisplayId(), delegate, inputMonitor);
         final Resources res = context.getResources();
         mContext = context;
         mDragDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java
index b2e7015..86d7190 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java
@@ -57,12 +57,19 @@
     private final int mTouchSlop;
     private final PointF mDownPos = new PointF();
     private final PointF mLastPos = new PointF();
+
+    private final int mDisplayId;
+
     private long mDownTime;
     private final long mTimeForLongPress;
     private int mActivePointerId = INVALID_POINTER_ID;
 
-    public BubbleBarInputConsumer(Context context, BubbleControllers bubbleControllers,
+    public BubbleBarInputConsumer(
+            Context context,
+            int displayId,
+            BubbleControllers bubbleControllers,
             InputMonitorCompat inputMonitorCompat) {
+        mDisplayId = displayId;
         mBubbleStashController = bubbleControllers.bubbleStashController;
         mBubbleBarViewController = bubbleControllers.bubbleBarViewController;
         mBubbleBarSwipeController = bubbleControllers.bubbleBarSwipeController.orElse(null);
@@ -78,6 +85,11 @@
     }
 
     @Override
+    public int getDisplayId() {
+        return mDisplayId;
+    }
+
+    @Override
     public void onMotionEvent(MotionEvent ev) {
         final int action = ev.getAction();
         switch (action) {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
index 4afd92a..0b1a6c4 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
@@ -17,15 +17,24 @@
     protected final InputConsumer mDelegate;
     protected final InputMonitorCompat mInputMonitor;
 
+    private final int mDisplayId;
+
     protected int mState;
 
-    public DelegateInputConsumer(InputConsumer delegate, InputMonitorCompat inputMonitor) {
+    public DelegateInputConsumer(
+            int displayId, InputConsumer delegate, InputMonitorCompat inputMonitor) {
+        mDisplayId = displayId;
         mDelegate = delegate;
         mInputMonitor = inputMonitor;
         mState = STATE_INACTIVE;
     }
 
     @Override
+    public int getDisplayId() {
+        return mDisplayId;
+    }
+
+    @Override
     public InputConsumer getActiveConsumerInHierarchy() {
         if (mState == STATE_ACTIVE) {
             return this;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 503b900..e192702 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -108,8 +108,11 @@
 
     private RecentsAnimationController mRecentsAnimationController;
 
-    public DeviceLockedInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
-            TaskAnimationManager taskAnimationManager, GestureState gestureState,
+    public DeviceLockedInputConsumer(
+            Context context,
+            RecentsAnimationDeviceState deviceState,
+            TaskAnimationManager taskAnimationManager,
+            GestureState gestureState,
             InputMonitorCompat inputMonitorCompat) {
         mContext = context;
         mTaskAnimationManager = taskAnimationManager;
@@ -138,6 +141,11 @@
     }
 
     @Override
+    public int getDisplayId() {
+        return mGestureState.getDisplayId();
+    }
+
+    @Override
     public void onMotionEvent(MotionEvent ev) {
         if (mVelocityTracker == null) {
             return;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
index a703c23..baabde8 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
@@ -74,10 +74,14 @@
     private MotionEvent mCurrentMotionEvent;  // Most recent motion event.
     private boolean mDeepPressLogged;  // Whether deep press has been logged for the current touch.
 
-    public NavHandleLongPressInputConsumer(Context context, InputConsumer delegate,
-            InputMonitorCompat inputMonitor, RecentsAnimationDeviceState deviceState,
-            NavHandle navHandle, GestureState gestureState) {
-        super(delegate, inputMonitor);
+    public NavHandleLongPressInputConsumer(
+            Context context,
+            InputConsumer delegate,
+            InputMonitorCompat inputMonitor,
+            RecentsAnimationDeviceState deviceState,
+            NavHandle navHandle,
+            GestureState gestureState) {
+        super(gestureState.getDisplayId(), delegate, inputMonitor);
         mScreenWidth = DisplayController.INSTANCE.get(context).getInfo().currentSize.x;
         mDeepPressEnabled = DeviceConfigWrapper.get().getEnableLpnhDeepPress();
         ContextualSearchStateManager contextualSearchStateManager =
@@ -254,13 +258,15 @@
             Log.d(TAG, "cancelLongPress: " + reason);
         }
         // Log LPNH abandon latency if we didn't trigger but were still prepared to.
-        long latencyMs = mCurrentMotionEvent.getEventTime() - mCurrentDownEvent.getEventTime();
-        if (mState != STATE_ACTIVE && MAIN_EXECUTOR.getHandler().hasCallbacks(mTriggerLongPress)
-                && latencyMs >= MIN_TIME_TO_LOG_ABANDON_MS) {
-            mStatsLogManager.latencyLogger()
-                    .withInstanceId(new InstanceIdSequence().newInstanceId())
-                    .withLatency(latencyMs)
-                    .log(LAUNCHER_LATENCY_CONTEXTUAL_SEARCH_LPNH_ABANDON);
+        if (mCurrentMotionEvent != null && mCurrentDownEvent != null) {
+            long latencyMs = mCurrentMotionEvent.getEventTime() - mCurrentDownEvent.getEventTime();
+            if (mState != STATE_ACTIVE && MAIN_EXECUTOR.getHandler().hasCallbacks(mTriggerLongPress)
+                    && latencyMs >= MIN_TIME_TO_LOG_ABANDON_MS) {
+                mStatsLogManager.latencyLogger()
+                        .withInstanceId(new InstanceIdSequence().newInstanceId())
+                        .withLatency(latencyMs)
+                        .log(LAUNCHER_LATENCY_CONTEXTUAL_SEARCH_LPNH_ABANDON);
+            }
         }
         mGestureState.setIsInExtendedSlopRegion(false);
         MAIN_EXECUTOR.getHandler().removeCallbacks(mTriggerLongPress);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
index 83b556d..67cb992 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
@@ -61,9 +61,13 @@
     private boolean mPassedSlop;
     private boolean mIsStopGesture;
 
-    public OneHandedModeInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
-            InputConsumer delegate, InputMonitorCompat inputMonitor) {
-        super(delegate, inputMonitor);
+    public OneHandedModeInputConsumer(
+            Context context,
+            int displayId,
+            RecentsAnimationDeviceState deviceState,
+            InputConsumer delegate,
+            InputMonitorCompat inputMonitor) {
+        super(displayId, delegate, inputMonitor);
         mContext = context;
         mDeviceState = deviceState;
         mDragDistThreshold = context.getResources().getDimensionPixelSize(
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index dd2b2be..c8cf58c 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -42,7 +42,6 @@
 
 import androidx.annotation.UiThread;
 
-import com.android.launcher3.Flags;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.testing.TestLogging;
@@ -59,6 +58,7 @@
 import com.android.quickstep.RecentsAnimationTargets;
 import com.android.quickstep.RotationTouchHelper;
 import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
 import com.android.quickstep.util.CachedEventDispatcher;
 import com.android.quickstep.util.MotionPauseDetector;
 import com.android.quickstep.util.NavBarPosition;
@@ -126,11 +126,17 @@
     // The callback called upon finishing the recents transition if it was force-canceled
     private Runnable mForceFinishRecentsTransitionCallback;
 
-    public OtherActivityInputConsumer(Context base, RecentsAnimationDeviceState deviceState,
-            TaskAnimationManager taskAnimationManager, GestureState gestureState,
-            boolean isDeferredDownTarget, Consumer<OtherActivityInputConsumer> onCompleteCallback,
-            InputMonitorCompat inputMonitorCompat, InputEventReceiver inputEventReceiver,
-            boolean disableHorizontalSwipe, Factory handlerFactory) {
+    public OtherActivityInputConsumer(
+            Context base,
+            RecentsAnimationDeviceState deviceState,
+            TaskAnimationManager taskAnimationManager,
+            GestureState gestureState,
+            boolean isDeferredDownTarget,
+            Consumer<OtherActivityInputConsumer> onCompleteCallback,
+            InputMonitorCompat inputMonitorCompat,
+            InputEventReceiver inputEventReceiver,
+            boolean disableHorizontalSwipe,
+            Factory handlerFactory) {
         super(base);
         mDeviceState = deviceState;
         mNavBarPosition = mDeviceState.getNavBarPosition();
@@ -166,6 +172,11 @@
     }
 
     @Override
+    public int getDisplayId() {
+        return mGestureState.getDisplayId();
+    }
+
+    @Override
     public boolean isConsumerDetachedFromGesture() {
         return true;
     }
@@ -427,10 +438,8 @@
             notifyGestureStarted(true /*isLikelyToStartNewTask*/);
         } else {
             // todo differentiate intent based on if we are on home or in app for overview in window
-            boolean useHomeIntentForWindow = Flags.enableFallbackOverviewInWindow()
-                    || Flags.enableLauncherOverviewInWindow();
-            Intent intent = new Intent(useHomeIntentForWindow ? mInteractionHandler.getHomeIntent()
-                : mInteractionHandler.getLaunchIntent());
+            Intent intent = new Intent(RecentsWindowFlags.Companion.getEnableOverviewInWindow()
+                    ? mInteractionHandler.getHomeIntent() : mInteractionHandler.getLaunchIntent());
             intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mGestureState.getGestureId());
             mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(mGestureState, intent,
                     mInteractionHandler);
@@ -472,7 +481,8 @@
                                 : velocityYPxPerMs;
                 mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
                 mInteractionHandler.onGestureEnded(velocityPxPerMs,
-                        new PointF(velocityXPxPerMs, velocityYPxPerMs));
+                        new PointF(velocityXPxPerMs, velocityYPxPerMs),
+                        Math.abs(mDownPos.x - mLastPos.x) > mTouchSlop);
             }
         } else {
             // Since we start touch tracking on DOWN, we may reach this state without actually
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index a236eca..4658cb0 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -55,12 +55,16 @@
     private final int[] mLocationOnScreen = new int[2];
 
     private final boolean mStartingInActivityBounds;
+
     private boolean mTargetHandledTouch;
     private boolean mHasSetTouchModeForFirstDPadEvent;
     private boolean mIsWaitingForAttachToWindow;
 
-    public OverviewInputConsumer(GestureState gestureState, T container,
-            @Nullable InputMonitorCompat inputMonitor, boolean startingInActivityBounds) {
+    public OverviewInputConsumer(
+            GestureState gestureState,
+            T container,
+            @Nullable InputMonitorCompat inputMonitor,
+            boolean startingInActivityBounds) {
         mContainer = container;
         mInputMonitor = inputMonitor;
         mStartingInActivityBounds = startingInActivityBounds;
@@ -77,6 +81,11 @@
     }
 
     @Override
+    public int getDisplayId() {
+        return mGestureState.getDisplayId();
+    }
+
+    @Override
     public boolean allowInterceptByParent() {
         return !mTargetHandledTouch;
     }
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index be47df9..7838e86 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -43,9 +43,12 @@
     private final TriggerSwipeUpTouchTracker mTriggerSwipeUpTracker;
     private final GestureState mGestureState;
 
-    public OverviewWithoutFocusInputConsumer(Context context,
-            RecentsAnimationDeviceState deviceState, GestureState gestureState,
-            InputMonitorCompat inputMonitor, boolean disableHorizontalSwipe) {
+    public OverviewWithoutFocusInputConsumer(
+            Context context,
+            RecentsAnimationDeviceState deviceState,
+            GestureState gestureState,
+            InputMonitorCompat inputMonitor,
+            boolean disableHorizontalSwipe) {
         mContext = context;
         mGestureState = gestureState;
         mInputMonitor = inputMonitor;
@@ -59,6 +62,11 @@
     }
 
     @Override
+    public int getDisplayId() {
+        return mGestureState.getDisplayId();
+    }
+
+    @Override
     public boolean allowInterceptByParent() {
         return !mTriggerSwipeUpTracker.interceptedTouch();
     }
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
index c91bebe..52aaa03 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
@@ -89,9 +89,12 @@
     private RecentsAnimationController mRecentsAnimationController;
     private Boolean mFlingEndsOnHome;
 
-    public ProgressDelegateInputConsumer(Context context,
-            TaskAnimationManager taskAnimationManager, GestureState gestureState,
-            InputMonitorCompat inputMonitorCompat, AnimatedFloat progress) {
+    public ProgressDelegateInputConsumer(
+            Context context,
+            TaskAnimationManager taskAnimationManager,
+            GestureState gestureState,
+            InputMonitorCompat inputMonitorCompat,
+            AnimatedFloat progress) {
         mContext = context;
         mTaskAnimationManager = taskAnimationManager;
         mGestureState = gestureState;
@@ -118,6 +121,11 @@
     }
 
     @Override
+    public int getDisplayId() {
+        return mGestureState.getDisplayId();
+    }
+
+    @Override
     public void onMotionEvent(MotionEvent ev) {
         if (mFlingEndsOnHome == null) {
             mSwipeDetector.onTouchEvent(ev);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
index d73c23f..9dc27de 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
@@ -36,9 +36,12 @@
     private final float mMotionPauseMinDisplacement;
     private final MotionPauseDetector mMotionPauseDetector;
 
+    private final int mDisplayId;
+
     private float mTouchDownY;
 
     public ScreenPinnedInputConsumer(Context context, GestureState gestureState) {
+        mDisplayId = gestureState.getDisplayId();
         mMotionPauseMinDisplacement = context.getResources().getDimension(
                 R.dimen.motion_pause_detector_min_displacement_from_app);
         mMotionPauseDetector = new MotionPauseDetector(context, true /* makePauseHarderToTrigger*/);
@@ -61,6 +64,11 @@
     }
 
     @Override
+    public int getDisplayId() {
+        return mDisplayId;
+    }
+
+    @Override
     public void onMotionEvent(MotionEvent ev) {
         float y = ev.getY();
         switch (ev.getAction()) {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
index 871d075..ad1a01b 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
@@ -47,11 +47,15 @@
     private final InputMonitorCompat mInputMonitor;
     private final TriggerSwipeUpTouchTracker mTriggerSwipeUpTracker;
 
+    private final int mDisplayId;
+
     public SysUiOverlayInputConsumer(
             Context context,
+            int displayId,
             RecentsAnimationDeviceState deviceState,
             InputMonitorCompat inputMonitor) {
         mContext = context;
+        mDisplayId = displayId;
         mInputMonitor = inputMonitor;
         mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(context, true,
                 deviceState.getNavBarPosition(), this);
@@ -63,6 +67,11 @@
     }
 
     @Override
+    public int getDisplayId() {
+        return mDisplayId;
+    }
+
+    @Override
     public boolean allowInterceptByParent() {
         return !mTriggerSwipeUpTracker.interceptedTouch();
     }
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
index 49bff8d..dbe6e14 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
@@ -89,10 +89,14 @@
     // Velocity defined as dp per s
     private float mTaskbarSlowVelocityYThreshold;
 
-    public TaskbarUnstashInputConsumer(Context context, InputConsumer delegate,
-            InputMonitorCompat inputMonitor, TaskbarActivityContext taskbarActivityContext,
-            OverviewCommandHelper overviewCommandHelper, GestureState gestureState) {
-        super(delegate, inputMonitor);
+    public TaskbarUnstashInputConsumer(
+            Context context,
+            InputConsumer delegate,
+            InputMonitorCompat inputMonitor,
+            TaskbarActivityContext taskbarActivityContext,
+            OverviewCommandHelper overviewCommandHelper,
+            GestureState gestureState) {
+        super(gestureState.getDisplayId(), delegate, inputMonitor);
         mTaskbarActivityContext = taskbarActivityContext;
         mOverviewCommandHelper = overviewCommandHelper;
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TrackpadStatusBarInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TrackpadStatusBarInputConsumer.java
index f3e21e1..a53a395 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TrackpadStatusBarInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TrackpadStatusBarInputConsumer.java
@@ -35,9 +35,12 @@
     private final PointF mDown = new PointF();
     private boolean mHasPassedTouchSlop;
 
-    public TrackpadStatusBarInputConsumer(Context context, InputConsumer delegate,
+    public TrackpadStatusBarInputConsumer(
+            Context context,
+            int displayId,
+            InputConsumer delegate,
             InputMonitorCompat inputMonitor) {
-        super(delegate, inputMonitor);
+        super(displayId, delegate, inputMonitor);
 
         mSystemUiProxy = SystemUiProxy.INSTANCE.get(context);
         mTouchSlop = 2 * ViewConfiguration.get(context).getScaledTouchSlop();
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index e265e61..c63cddf 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -34,6 +34,7 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.view.Display;
 import android.view.View;
 import android.view.ViewOutlineProvider;
 
@@ -84,8 +85,8 @@
 
     SwipeUpGestureTutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) {
         super(tutorialFragment, tutorialType);
-        mTaskViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext,
-                new GestureState(OverviewComponentObserver.INSTANCE.get(mContext), -1));
+        mTaskViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, new GestureState(
+                OverviewComponentObserver.INSTANCE.get(mContext), Display.DEFAULT_DISPLAY, -1));
 
         DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext)
                 .getDeviceProfile(mContext)
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index e73fb3b..5995ca2 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -36,7 +36,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
-import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.widget.Button;
 import android.widget.FrameLayout;
@@ -57,11 +56,12 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorListeners;
-import com.android.launcher3.taskbar.TypefaceUtils;
 import com.android.launcher3.views.ClipIconView;
 import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureAttemptCallback;
 import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureAttemptCallback;
 import com.android.systemui.shared.system.QuickStepContract;
+import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
 
 import com.airbnb.lottie.LottieAnimationView;
 import com.airbnb.lottie.LottieComposition;
@@ -110,7 +110,6 @@
     final AnimatedTaskView mFakePreviousTaskView;
     final View mRippleView;
     final RippleDrawable mRippleDrawable;
-    final TutorialStepIndicator mTutorialStepView;
     final ImageView mFingerDotView;
     private final Rect mExitingAppRect = new Rect();
     protected View mExitingAppView;
@@ -124,13 +123,10 @@
 
     // These runnables  should be used when posting callbacks to their views and cleared from their
     // views before posting new callbacks.
-    private final Runnable mTitleViewCallback;
-    private final Runnable mSubtitleViewCallback;
     @Nullable private Runnable mFeedbackViewCallback;
     @Nullable private Runnable mFakeTaskViewCallback;
     @Nullable private Runnable mFakeTaskbarViewCallback;
     private final Runnable mShowFeedbackRunnable;
-    private final AccessibilityManager mAccessibilityManager;
 
     TutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) {
         mTutorialFragment = tutorialFragment;
@@ -156,8 +152,6 @@
         mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view);
         mRippleDrawable = (RippleDrawable) mRippleView.getBackground();
         mDoneButton = rootView.findViewById(R.id.gesture_tutorial_fragment_action_button);
-        mTutorialStepView =
-                rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_tutorial_step);
         mFingerDotView = rootView.findViewById(R.id.gesture_tutorial_finger_dot);
         mSkipTutorialDialog = createSkipTutorialDialog();
 
@@ -187,17 +181,6 @@
                 outline.setRoundRect(mExitingAppRect, mExitingAppRadius);
             }
         });
-
-        mAccessibilityManager = AccessibilityManager.getInstance(mContext);
-        mTitleViewCallback = () -> {
-            mFeedbackTitleView.requestFocus();
-            mFeedbackTitleView.sendAccessibilityEvent(
-                    AccessibilityEvent.TYPE_VIEW_FOCUSED);
-        };
-        mSubtitleViewCallback = () -> {
-            mFeedbackSubtitleView.requestFocus();
-            mFeedbackSubtitleView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
-        };
         mShowFeedbackRunnable = () -> {
             mFeedbackView.setAlpha(0f);
             mFeedbackView.setScaleX(0.95f);
@@ -216,14 +199,14 @@
                             mFeedbackViewCallback = mTutorialFragment::continueTutorial;
                             mFeedbackView.postDelayed(
                                     mFeedbackViewCallback,
-                                    mAccessibilityManager.getRecommendedTimeoutMillis(
-                                            ADVANCE_TUTORIAL_TIMEOUT_MS,
-                                            AccessibilityManager.FLAG_CONTENT_TEXT
+                                    AccessibilityManager.getInstance(mContext)
+                                            .getRecommendedTimeoutMillis(
+                                                    ADVANCE_TUTORIAL_TIMEOUT_MS,
+                                                    AccessibilityManager.FLAG_CONTENT_TEXT
                                                     | AccessibilityManager.FLAG_CONTENT_CONTROLS));
                         }
                     })
                     .start();
-            mFeedbackTitleView.postDelayed(mTitleViewCallback, FEEDBACK_ANIMATION_MS);
         };
     }
 
@@ -416,8 +399,6 @@
             int titleResId,
             int subtitleResId,
             boolean isGestureSuccessful) {
-        mFeedbackTitleView.removeCallbacks(mTitleViewCallback);
-        mFeedbackSubtitleView.removeCallbacks(mSubtitleViewCallback);
         if (mFeedbackViewCallback != null) {
             mFeedbackView.removeCallbacks(mFeedbackViewCallback);
             mFeedbackViewCallback = null;
@@ -425,20 +406,11 @@
 
         mFeedbackTitleView.setText(titleResId);
         mFeedbackSubtitleView.setText(subtitleResId);
-        mFeedbackTitleView.postDelayed(mTitleViewCallback, mAccessibilityManager
-                .getRecommendedTimeoutMillis(
-                        FEEDBACK_ANIMATION_MS,
-                        AccessibilityManager.FLAG_CONTENT_TEXT));
-        mFeedbackSubtitleView.postDelayed(mSubtitleViewCallback, mAccessibilityManager
-                .getRecommendedTimeoutMillis(
-                        SUBTITLE_ANNOUNCE_DELAY_MS,
-                        AccessibilityManager.FLAG_CONTENT_TEXT));
-
         if (isGestureSuccessful) {
             if (mTutorialFragment.isAtFinalStep()) {
                 TypefaceUtils.setTypeface(
                         mDoneButton,
-                        TypefaceUtils.FONT_FAMILY_LABEL_LARGE_BASELINE
+                        FontFamily.GSF_LABEL_LARGE
                 );
                 showActionButton();
             }
@@ -494,8 +466,6 @@
             mFakeTaskbarView.removeCallbacks(mFakeTaskbarViewCallback);
             mFakeTaskbarViewCallback = null;
         }
-        mFeedbackTitleView.removeCallbacks(mTitleViewCallback);
-        mFeedbackSubtitleView.removeCallbacks(mSubtitleViewCallback);
     }
 
     private void playFeedbackAnimation() {
@@ -516,7 +486,6 @@
     @CallSuper
     void transitToController() {
         updateCloseButton();
-        updateSubtext();
         updateDrawables();
         updateLayout();
 
@@ -549,11 +518,11 @@
         TypefaceUtils.setTypeface(
                 mFeedbackTitleView,
                 mTutorialFragment.isLargeScreen()
-                        ? TypefaceUtils.FONT_FAMILY_DISPLAY_MEDIUM_EMPHASIZED
-                        : TypefaceUtils.FONT_FAMILY_DISPLAY_SMALL_EMPHASIZED);
+                        ? FontFamily.GSF_DISPLAY_MEDIUM_EMPHASIZED
+                        : FontFamily.GSF_DISPLAY_SMALL_EMPHASIZED);
         TypefaceUtils.setTypeface(
                 mFeedbackSubtitleView,
-                TypefaceUtils.FONT_FAMILY_BODY_LARGE_BASELINE
+                FontFamily.GSF_BODY_LARGE
         );
     }
 
@@ -588,13 +557,6 @@
         mSkipButton.setVisibility(GONE);
         mDoneButton.setVisibility(View.VISIBLE);
         mDoneButton.setOnClickListener(this::onActionButtonClicked);
-        mDoneButton.postDelayed(() -> {
-            mDoneButton.requestFocus();
-            mDoneButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
-        }, mAccessibilityManager
-                .getRecommendedTimeoutMillis(
-                        DONE_BUTTON_ANNOUNCE_DELAY_MS,
-                        AccessibilityManager.FLAG_CONTENT_CONTROLS));
     }
 
     void hideFakeTaskbar(boolean animateToHotseat) {
@@ -640,11 +602,6 @@
         }
     }
 
-    private void updateSubtext() {
-        mTutorialStepView.setTutorialProgress(
-                mTutorialFragment.getCurrentStep(), mTutorialFragment.getNumSteps());
-    }
-
     private void updateHotseatChildViewColor(@Nullable View child) {
         if (child == null) return;
         child.getBackground().setTint(getHotseatIconColor());
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java b/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java
deleted file mode 100644
index f1fc179..0000000
--- a/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2021 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.interaction;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.icons.GraphicsUtils;
-
-/** Indicator displaying the current progress through the gesture navigation tutorial. */
-public class TutorialStepIndicator extends LinearLayout {
-
-    private static final String LOG_TAG = "TutorialStepIndicator";
-
-    private int mCurrentStep = -1;
-    private int mTotalSteps = -1;
-
-    public TutorialStepIndicator(Context context) {
-        super(context);
-    }
-
-    public TutorialStepIndicator(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public TutorialStepIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public TutorialStepIndicator(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    /**
-     * Updates this indicator to display totalSteps indicator pills, with the first currentStep
-     * pills highlighted.
-     */
-    public void setTutorialProgress(int currentStep, int totalSteps) {
-        if (currentStep <= 0) {
-            Log.w(LOG_TAG, "Current step number invalid: " + currentStep + ". Assuming step 1.");
-            currentStep = 1;
-        }
-        if (totalSteps <= 0) {
-            Log.w(LOG_TAG, "Total number of steps invalid: " + totalSteps + ". Assuming 1 step.");
-            totalSteps = 1;
-        }
-        if (currentStep > totalSteps) {
-            Log.w(LOG_TAG, "Current step number greater than the total number of steps. Assuming"
-                    + " final step.");
-            currentStep = totalSteps;
-        }
-        if (totalSteps < 2) {
-            setVisibility(GONE);
-            return;
-        }
-        setVisibility(VISIBLE);
-        mCurrentStep = currentStep;
-        mTotalSteps = totalSteps;
-
-        initializeStepIndicators();
-    }
-
-    private void initializeStepIndicators() {
-        for (int i = mTotalSteps; i < getChildCount(); i++) {
-            removeViewAt(i);
-        }
-        int activeStepIndicatorColor = GraphicsUtils.getAttrColor(
-                getContext(), android.R.attr.textColorPrimary);
-        int inactiveStepIndicatorColor = GraphicsUtils.getAttrColor(
-                getContext(), android.R.attr.textColorSecondaryInverse);
-        for (int i = 0; i < mTotalSteps; i++) {
-            Drawable pageIndicatorPillDrawable =
-                    getContext().getDrawable(R.drawable.tutorial_step_indicator_pill);
-            if (i >= getChildCount()) {
-                ImageView pageIndicatorPill = new ImageView(getContext());
-                pageIndicatorPill.setImageDrawable(pageIndicatorPillDrawable);
-
-                LinearLayout.LayoutParams lp = new LayoutParams(
-                        LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
-
-                lp.setMarginStart(Utilities.dpToPx(3));
-                lp.setMarginEnd(Utilities.dpToPx(3));
-
-                addView(pageIndicatorPill, lp);
-            }
-            if (pageIndicatorPillDrawable != null) {
-                if (i < mCurrentStep) {
-                    pageIndicatorPillDrawable.setTint(activeStepIndicatorColor);
-                } else {
-                    pageIndicatorPillDrawable.setTint(inactiveStepIndicatorColor);
-                }
-            }
-        }
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
index 17f861d..c4e343e 100644
--- a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
@@ -103,7 +103,7 @@
         target: T,
         action: Int2DAction<T>,
         primaryParam: Int,
-        secondaryParam: Int
+        secondaryParam: Int,
     ) = action.call(target, secondaryParam, primaryParam)
 
     override fun getPrimaryDirection(event: MotionEvent, pointerIndex: Int): Float =
@@ -171,7 +171,7 @@
 
     override fun getSplitTranslationDirectionFactor(
         stagePosition: Int,
-        deviceProfile: DeviceProfile
+        deviceProfile: DeviceProfile,
     ): Int = if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) -1 else 1
 
     override fun getTaskMenuX(
@@ -179,7 +179,7 @@
         thumbnailView: View,
         deviceProfile: DeviceProfile,
         taskInsetMargin: Float,
-        taskViewIcon: View
+        taskViewIcon: View,
     ): Float = thumbnailView.measuredWidth + x - taskInsetMargin
 
     override fun getTaskMenuY(
@@ -188,7 +188,7 @@
         stagePosition: Int,
         taskMenuView: View,
         taskInsetMargin: Float,
-        taskViewIcon: View
+        taskViewIcon: View,
     ): Float {
         val layoutParams = taskMenuView.layoutParams as BaseDragLayer.LayoutParams
         var taskMenuY = y + taskInsetMargin
@@ -203,7 +203,7 @@
     override fun getTaskMenuWidth(
         thumbnailView: View,
         deviceProfile: DeviceProfile,
-        @StagePosition stagePosition: Int
+        @StagePosition stagePosition: Int,
     ): Int =
         when {
             Flags.enableOverviewIconMenu() ->
@@ -218,14 +218,14 @@
         taskInsetMargin: Float,
         deviceProfile: DeviceProfile,
         taskMenuX: Float,
-        taskMenuY: Float
+        taskMenuY: Float,
     ): Int = (taskMenuX - taskInsetMargin).toInt()
 
     override fun setTaskOptionsMenuLayoutOrientation(
         deviceProfile: DeviceProfile,
         taskMenuLayout: LinearLayout,
         dividerSpacing: Int,
-        dividerDrawable: ShapeDrawable
+        dividerDrawable: ShapeDrawable,
     ) {
         taskMenuLayout.orientation = LinearLayout.VERTICAL
         dividerDrawable.intrinsicHeight = dividerSpacing
@@ -235,7 +235,7 @@
     override fun setLayoutParamsForTaskMenuOptionItem(
         lp: LinearLayout.LayoutParams,
         viewGroup: LinearLayout,
-        deviceProfile: DeviceProfile
+        deviceProfile: DeviceProfile,
     ) {
         // Phone fake landscape
         viewGroup.orientation = LinearLayout.HORIZONTAL
@@ -250,7 +250,7 @@
         deviceProfile: DeviceProfile,
         snapshotViewWidth: Int,
         snapshotViewHeight: Int,
-        banner: View
+        banner: View,
     ) {
         banner.pivotX = 0f
         banner.pivotY = 0f
@@ -273,9 +273,9 @@
         deviceProfile: DeviceProfile,
         thumbnailViews: Array<View>,
         desiredTaskId: Int,
-        banner: View
+        banner: View,
     ): Pair<Float, Float> {
-        val snapshotParams = thumbnailViews[0].layoutParams as FrameLayout.LayoutParams
+        val snapshotParams = thumbnailViews[0].layoutParams as LinearLayout.LayoutParams
         val translationX = banner.height.toFloat()
         val translationY: Float
         if (splitBounds == null) {
@@ -311,13 +311,21 @@
 
     override fun getTaskDragDisplacementFactor(isRtl: Boolean): Int = if (isRtl) 1 else -1
 
+    override fun getTaskDismissVerticalDirection(): Int = 1
+
+    override fun getTaskDismissLength(secondaryDimension: Int, taskThumbnailBounds: Rect): Int =
+        secondaryDimension - taskThumbnailBounds.left
+
+    override fun getTaskLaunchLength(secondaryDimension: Int, taskThumbnailBounds: Rect): Int =
+        taskThumbnailBounds.left
+
     /* -------------------- */
 
     override fun getChildBounds(
         child: View,
         childStart: Int,
         pageCenter: Int,
-        layoutChild: Boolean
+        layoutChild: Boolean,
     ): ChildBounds {
         val childHeight = child.measuredHeight
         val childWidth = child.measuredWidth
@@ -338,7 +346,7 @@
                 R.drawable.ic_split_horizontal,
                 R.string.recent_task_option_split_screen,
                 STAGE_POSITION_TOP_OR_LEFT,
-                STAGE_TYPE_MAIN
+                STAGE_TYPE_MAIN,
             )
         )
 
@@ -347,7 +355,7 @@
         placeholderInset: Int,
         dp: DeviceProfile,
         @StagePosition stagePosition: Int,
-        out: Rect
+        out: Rect,
     ) {
         // In fake land/seascape, the placeholder always needs to go to the "top" of the device,
         // which is the same bounds as 0 rotation.
@@ -374,7 +382,7 @@
         drawableWidth: Int,
         drawableHeight: Int,
         dp: DeviceProfile,
-        @StagePosition stagePosition: Int
+        @StagePosition stagePosition: Int,
     ) {
         val insetAdjustment = getPlaceholderSizeAdjustment(dp) / 2f
         out.x = (onScreenRectCenterX / fullscreenScaleX - 1.0f * drawableWidth / 2)
@@ -393,7 +401,7 @@
         out: View,
         dp: DeviceProfile,
         splitInstructionsHeight: Int,
-        splitInstructionsWidth: Int
+        splitInstructionsWidth: Int,
     ) {
         out.pivotX = 0f
         out.pivotY = splitInstructionsHeight.toFloat()
@@ -421,7 +429,7 @@
         dp: DeviceProfile,
         @StagePosition stagePosition: Int,
         out1: Rect,
-        out2: Rect
+        out2: Rect,
     ) {
         // In fake land/seascape, the window bounds are always top and bottom half
         val screenHeight = dp.heightPx
@@ -434,7 +442,7 @@
         dp: DeviceProfile,
         outRect: Rect,
         splitInfo: SplitBounds,
-        desiredStagePosition: Int
+        desiredStagePosition: Int,
     ) {
         val topLeftTaskPercent = splitInfo.leftTopTaskPercent
         val dividerBarPercent = splitInfo.dividerPercent
@@ -448,7 +456,7 @@
 
     /**
      * @param inSplitSelection Whether user currently has a task from this task group staged for
-     * split screen. Currently this state is not reachable in fake landscape.
+     *   split screen. Currently this state is not reachable in fake landscape.
      */
     override fun measureGroupedTaskViewThumbnailBounds(
         primarySnapshot: View,
@@ -458,7 +466,7 @@
         splitBoundsConfig: SplitBounds,
         dp: DeviceProfile,
         isRtl: Boolean,
-        inSplitSelection: Boolean
+        inSplitSelection: Boolean,
     ) {
         val primaryParams = primarySnapshot.layoutParams as FrameLayout.LayoutParams
         val secondaryParams = secondarySnapshot.layoutParams as FrameLayout.LayoutParams
@@ -479,13 +487,13 @@
         primarySnapshot.translationY = spaceAboveSnapshot.toFloat()
         primarySnapshot.measure(
             MeasureSpec.makeMeasureSpec(taskViewFirst.x, MeasureSpec.EXACTLY),
-            MeasureSpec.makeMeasureSpec(taskViewFirst.y, MeasureSpec.EXACTLY)
+            MeasureSpec.makeMeasureSpec(taskViewFirst.y, MeasureSpec.EXACTLY),
         )
         val translationY = taskViewFirst.y + spaceAboveSnapshot + dividerBar
         secondarySnapshot.translationY = (translationY - spaceAboveSnapshot).toFloat()
         secondarySnapshot.measure(
             MeasureSpec.makeMeasureSpec(taskViewSecond.x, MeasureSpec.EXACTLY),
-            MeasureSpec.makeMeasureSpec(taskViewSecond.y, MeasureSpec.EXACTLY)
+            MeasureSpec.makeMeasureSpec(taskViewSecond.y, MeasureSpec.EXACTLY),
         )
     }
 
@@ -493,7 +501,7 @@
         dp: DeviceProfile,
         splitBoundsConfig: SplitBounds,
         parentWidth: Int,
-        parentHeight: Int
+        parentHeight: Int,
     ): Pair<Point, Point> {
         val spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx
         val totalThumbnailHeight = parentHeight - spaceAboveSnapshot
@@ -511,7 +519,7 @@
         taskIconMargin: Int,
         taskIconHeight: Int,
         thumbnailTopMargin: Int,
-        isRtl: Boolean
+        isRtl: Boolean,
     ) {
         iconParams.gravity =
             if (isRtl) {
@@ -527,7 +535,7 @@
 
     override fun setIconAppChipChildrenParams(
         iconParams: FrameLayout.LayoutParams,
-        chipChildMarginStart: Int
+        chipChildMarginStart: Int,
     ) {
         iconParams.gravity = Gravity.START or Gravity.CENTER_VERTICAL
         iconParams.marginStart = chipChildMarginStart
@@ -538,7 +546,7 @@
         iconAppChipView: IconAppChipView,
         iconMenuParams: FrameLayout.LayoutParams,
         iconMenuMargin: Int,
-        thumbnailTopMargin: Int
+        thumbnailTopMargin: Int,
     ) {
         val isRtl = iconAppChipView.layoutDirection == View.LAYOUT_DIRECTION_RTL
 
@@ -564,7 +572,7 @@
 
     /**
      * @param inSplitSelection Whether user currently has a task from this task group staged for
-     * split screen. Currently this state is not reachable in fake landscape.
+     *   split screen. Currently this state is not reachable in fake landscape.
      */
     override fun setSplitIconParams(
         primaryIconView: View,
@@ -606,20 +614,20 @@
     override fun <T> getSplitSelectTaskOffset(
         primary: FloatProperty<T>,
         secondary: FloatProperty<T>,
-        deviceProfile: DeviceProfile
+        deviceProfile: DeviceProfile,
     ): Pair<FloatProperty<T>, FloatProperty<T>> = Pair(primary, secondary)
 
     override fun getFloatingTaskOffscreenTranslationTarget(
         floatingTask: View,
         onScreenRect: RectF,
         @StagePosition stagePosition: Int,
-        dp: DeviceProfile
+        dp: DeviceProfile,
     ): Float = floatingTask.translationY - onScreenRect.height()
 
     override fun setFloatingTaskPrimaryTranslation(
         floatingTask: View,
         translation: Float,
-        dp: DeviceProfile
+        dp: DeviceProfile,
     ) {
         floatingTask.translationY = translation
     }
@@ -660,12 +668,11 @@
         } else {
             if (oneIconHiddenDueToSmallWidth) {
                 // Center both icons
-                val centerY = primarySnapshotHeight + overviewTaskMarginPx +
+                val centerY =
+                    primarySnapshotHeight +
+                        overviewTaskMarginPx +
                         ((taskIconHeight + dividerSize) / 2)
-                SplitIconPositions(
-                    topLeftY = centerY,
-                    bottomRightY = centerY,
-                )
+                SplitIconPositions(topLeftY = centerY, bottomRightY = centerY)
             } else {
                 val topLeftY = primarySnapshotHeight + overviewTaskMarginPx
                 SplitIconPositions(
diff --git a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
deleted file mode 100644
index c4e82d6..0000000
--- a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
+++ /dev/null
@@ -1,872 +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.orientation;
-
-import static android.view.Gravity.BOTTOM;
-import static android.view.Gravity.CENTER_HORIZONTAL;
-import static android.view.Gravity.END;
-import static android.view.Gravity.START;
-import static android.view.Gravity.TOP;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
-import static com.android.launcher3.Flags.enableOverviewIconMenu;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
-import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
-
-import android.graphics.Matrix;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.ShapeDrawable;
-import android.util.FloatProperty;
-import android.util.Pair;
-import android.view.Gravity;
-import android.view.Surface;
-import android.view.View;
-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;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
-import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
-import com.android.quickstep.views.IconAppChipView;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class PortraitPagedViewHandler extends DefaultPagedViewHandler implements
-        RecentsPagedOrientationHandler {
-
-    private final Matrix mTmpMatrix = new Matrix();
-    private final RectF mTmpRectF = new RectF();
-
-    @Override
-    public <T> T getPrimaryValue(T x, T y) {
-        return x;
-    }
-
-    @Override
-    public <T> T getSecondaryValue(T x, T y) {
-        return y;
-    }
-
-    @Override
-    public boolean isLayoutNaturalToLauncher() {
-        return true;
-    }
-
-    @Override
-    public void adjustFloatingIconStartVelocity(PointF velocity) {
-        //no-op
-    }
-
-    @Override
-    public void fixBoundsForHomeAnimStartRect(RectF outStartRect, DeviceProfile deviceProfile) {
-        if (outStartRect.left > deviceProfile.widthPx) {
-            outStartRect.offsetTo(0, outStartRect.top);
-        } else if (outStartRect.left < -deviceProfile.widthPx) {
-            outStartRect.offsetTo(0, outStartRect.top);
-        }
-    }
-
-    @Override
-    public <T> void setSecondary(T target, Float2DAction<T> action, float param) {
-        action.call(target, 0, param);
-    }
-
-    @Override
-    public <T> void set(T target, Int2DAction<T> action, int primaryParam,
-            int secondaryParam) {
-        action.call(target, primaryParam, secondaryParam);
-    }
-
-    @Override
-    public int getPrimarySize(View view) {
-        return view.getWidth();
-    }
-
-    @Override
-    public float getPrimarySize(RectF rect) {
-        return rect.width();
-    }
-
-    @Override
-    public float getStart(RectF rect) {
-        return rect.left;
-    }
-
-    @Override
-    public float getEnd(RectF rect) {
-        return rect.right;
-    }
-
-    @Override
-    public void rotateInsets(@NonNull Rect insets, @NonNull Rect outInsets) {
-        outInsets.set(insets);
-    }
-
-    @Override
-    public int getClearAllSidePadding(View view, boolean isRtl) {
-        return (isRtl ? view.getPaddingRight() : - view.getPaddingLeft()) / 2;
-    }
-
-    @Override
-    public int getSecondaryDimension(View view) {
-        return view.getHeight();
-    }
-
-    @Override
-    public FloatProperty<View> getPrimaryViewTranslate() {
-        return VIEW_TRANSLATE_X;
-    }
-
-    @Override
-    public FloatProperty<View> getSecondaryViewTranslate() {
-        return VIEW_TRANSLATE_Y;
-    }
-
-    @Override
-    public float getDegreesRotated() {
-        return 0;
-    }
-
-    @Override
-    public int getRotation() {
-        return Surface.ROTATION_0;
-    }
-
-    @Override
-    public void setPrimaryScale(View view, float scale) {
-        view.setScaleX(scale);
-    }
-
-    @Override
-    public void setSecondaryScale(View view, float scale) {
-        view.setScaleY(scale);
-    }
-
-    public int getSecondaryTranslationDirectionFactor() {
-        return -1;
-    }
-
-    @Override
-    public int getSplitTranslationDirectionFactor(int stagePosition, DeviceProfile deviceProfile) {
-        if (deviceProfile.isLeftRightSplit && stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
-            return -1;
-        } else {
-            return 1;
-        }
-    }
-
-    @Override
-    public float getTaskMenuX(float x, View thumbnailView,
-            DeviceProfile deviceProfile, float taskInsetMargin, View taskViewIcon) {
-        if (deviceProfile.isLandscape) {
-            return x + taskInsetMargin
-                    + (thumbnailView.getMeasuredWidth() - thumbnailView.getMeasuredHeight()) / 2f;
-        } else {
-            return x + taskInsetMargin;
-        }
-    }
-
-    @Override
-    public float getTaskMenuY(float y, View thumbnailView, int stagePosition,
-            View taskMenuView, float taskInsetMargin, View taskViewIcon) {
-        return y + taskInsetMargin;
-    }
-
-    @Override
-    public int getTaskMenuWidth(View thumbnailView, DeviceProfile deviceProfile,
-            @StagePosition int stagePosition) {
-        if (enableOverviewIconMenu()) {
-            return thumbnailView.getResources().getDimensionPixelSize(
-                    R.dimen.task_thumbnail_icon_menu_expanded_width);
-        }
-        int padding = thumbnailView.getResources()
-                .getDimensionPixelSize(R.dimen.task_menu_edge_padding);
-        return (deviceProfile.isLandscape && !deviceProfile.isTablet
-                ? thumbnailView.getMeasuredHeight()
-                : thumbnailView.getMeasuredWidth()) - (2 * padding);
-    }
-
-    @Override
-    public int getTaskMenuHeight(float taskInsetMargin, DeviceProfile deviceProfile,
-            float taskMenuX, float taskMenuY) {
-        return (int) (deviceProfile.heightPx - deviceProfile.getInsets().top - taskMenuY
-                    - deviceProfile.getOverviewActionsClaimedSpaceBelow());
-    }
-
-    @Override
-    public void setTaskOptionsMenuLayoutOrientation(DeviceProfile deviceProfile,
-            LinearLayout taskMenuLayout, int dividerSpacing,
-            ShapeDrawable dividerDrawable) {
-        taskMenuLayout.setOrientation(LinearLayout.VERTICAL);
-        dividerDrawable.setIntrinsicHeight(dividerSpacing);
-        taskMenuLayout.setDividerDrawable(dividerDrawable);
-    }
-
-    @Override
-    public void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp,
-            LinearLayout viewGroup, DeviceProfile deviceProfile) {
-        viewGroup.setOrientation(LinearLayout.HORIZONTAL);
-        lp.width = LinearLayout.LayoutParams.MATCH_PARENT;
-        lp.height = WRAP_CONTENT;
-    }
-
-    @Override
-    public void updateDwbBannerLayout(int taskViewWidth, int taskViewHeight,
-            boolean isGroupedTaskView, @NonNull DeviceProfile deviceProfile,
-            int snapshotViewWidth, int snapshotViewHeight, @NonNull View banner) {
-        FrameLayout.LayoutParams bannerParams = (FrameLayout.LayoutParams) banner.getLayoutParams();
-        banner.setPivotX(0);
-        banner.setPivotY(0);
-        banner.setRotation(getDegreesRotated());
-        if (isGroupedTaskView) {
-            bannerParams.gravity =
-                    BOTTOM | (deviceProfile.isLeftRightSplit ? START : CENTER_HORIZONTAL);
-            bannerParams.width = snapshotViewWidth;
-        } else {
-            bannerParams.width = MATCH_PARENT;
-            bannerParams.gravity = BOTTOM | CENTER_HORIZONTAL;
-        }
-        banner.setLayoutParams(bannerParams);
-    }
-
-    @NonNull
-    @Override
-    public Pair<Float, Float> getDwbBannerTranslations(int taskViewWidth,
-            int taskViewHeight, SplitBounds splitBounds, @NonNull DeviceProfile deviceProfile,
-            @NonNull View[] thumbnailViews, int desiredTaskId, @NonNull View banner) {
-        float translationX = 0;
-        float translationY = 0;
-        if (splitBounds != null) {
-            if (deviceProfile.isLeftRightSplit) {
-                if (desiredTaskId == splitBounds.rightBottomTaskId) {
-                    float leftTopTaskPercent = splitBounds.getLeftTopTaskPercent();
-                    float dividerThicknessPercent = splitBounds.getDividerPercent();
-                    translationX = ((taskViewWidth * leftTopTaskPercent)
-                            + (taskViewWidth * dividerThicknessPercent));
-                }
-            } else {
-                if (desiredTaskId == splitBounds.leftTopTaskId) {
-                    FrameLayout.LayoutParams snapshotParams =
-                            (FrameLayout.LayoutParams) thumbnailViews[0]
-                                    .getLayoutParams();
-                    float bottomRightTaskPlusDividerPercent =
-                            splitBounds.getRightBottomTaskPercent()
-                                    + splitBounds.getDividerPercent();
-                    translationY = -((taskViewHeight - snapshotParams.topMargin)
-                            * bottomRightTaskPlusDividerPercent);
-                }
-            }
-        }
-        return new Pair<>(translationX, translationY);
-    }
-
-    /* ---------- The following are only used by TaskViewTouchHandler. ---------- */
-
-    @Override
-    public SingleAxisSwipeDetector.Direction getUpDownSwipeDirection() {
-        return VERTICAL;
-    }
-
-    @Override
-    public int getUpDirection(boolean isRtl) {
-        // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
-        return SingleAxisSwipeDetector.DIRECTION_POSITIVE;
-    }
-
-    @Override
-    public int getDownDirection(boolean isRtl) {
-        // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
-        return SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
-    }
-
-    @Override
-    public boolean isGoingUp(float displacement, boolean isRtl) {
-        // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
-        return displacement < 0;
-    }
-
-    @Override
-    public int getTaskDragDisplacementFactor(boolean isRtl) {
-        // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
-        return 1;
-    }
-
-    /* -------------------- */
-    @Override
-    public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
-        return dp.heightPx - rect.bottom;
-    }
-
-    @Override
-    public List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp) {
-        if (dp.isTablet) {
-            return Utilities.getSplitPositionOptions(dp);
-        }
-
-        List<SplitPositionOption> options = new ArrayList<>();
-        if (dp.isSeascape()) {
-            options.add(new SplitPositionOption(
-                    R.drawable.ic_split_horizontal, R.string.recent_task_option_split_screen,
-                    STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_MAIN));
-        } else if (dp.isLeftRightSplit) {
-            options.add(new SplitPositionOption(
-                    R.drawable.ic_split_horizontal, R.string.recent_task_option_split_screen,
-                    STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
-        } else {
-            // Only add top option
-            options.add(new SplitPositionOption(
-                    R.drawable.ic_split_vertical, R.string.recent_task_option_split_screen,
-                    STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
-        }
-        return options;
-    }
-
-    @Override
-    public void getInitialSplitPlaceholderBounds(int placeholderHeight, int placeholderInset,
-            DeviceProfile dp, @StagePosition int stagePosition, Rect out) {
-        int screenWidth = dp.widthPx;
-        int screenHeight = dp.heightPx;
-        boolean pinToRight = stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
-        int insetSizeAdjustment = getPlaceholderSizeAdjustment(dp, pinToRight);
-
-        out.set(0, 0, screenWidth, placeholderHeight + insetSizeAdjustment);
-        if (!dp.isLeftRightSplit) {
-            // portrait, phone or tablet - spans width of screen, nothing else to do
-            out.inset(placeholderInset, 0);
-
-            // Adjust the top to account for content off screen. This will help to animate the view
-            // in with rounded corners.
-            int totalHeight = (int) (1.0f * screenHeight / 2 * (screenWidth - 2 * placeholderInset)
-                    / screenWidth);
-            out.top -= (totalHeight - placeholderHeight);
-            return;
-        }
-
-        // Now we rotate the portrait rect depending on what side we want pinned
-
-        float postRotateScale = (float) screenHeight / screenWidth;
-        mTmpMatrix.reset();
-        mTmpMatrix.postRotate(pinToRight ? 90 : 270);
-        mTmpMatrix.postTranslate(pinToRight ? screenWidth : 0, pinToRight ? 0 : screenWidth);
-        // The placeholder height stays constant after rotation, so we don't change width scale
-        mTmpMatrix.postScale(1, postRotateScale);
-
-        mTmpRectF.set(out);
-        mTmpMatrix.mapRect(mTmpRectF);
-        mTmpRectF.inset(0, placeholderInset);
-        mTmpRectF.roundOut(out);
-
-        // Adjust the top to account for content off screen. This will help to animate the view in
-        // with rounded corners.
-        int totalWidth = (int) (1.0f * screenWidth / 2 * (screenHeight - 2 * placeholderInset)
-                / screenHeight);
-        int width = out.width();
-        if (pinToRight) {
-            out.right += totalWidth - width;
-        } else {
-            out.left -= totalWidth - width;
-        }
-    }
-
-    @Override
-    public void updateSplitIconParams(View out, float onScreenRectCenterX,
-            float onScreenRectCenterY, float fullscreenScaleX, float fullscreenScaleY,
-            int drawableWidth, int drawableHeight, DeviceProfile dp,
-            @StagePosition int stagePosition) {
-        boolean pinToRight = stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
-        float insetAdjustment = getPlaceholderSizeAdjustment(dp, pinToRight) / 2f;
-        if (!dp.isLeftRightSplit) {
-            out.setX(onScreenRectCenterX / fullscreenScaleX
-                    - 1.0f * drawableWidth / 2);
-            out.setY((onScreenRectCenterY + insetAdjustment) / fullscreenScaleY
-                    - 1.0f * drawableHeight / 2);
-        } else {
-            if (pinToRight) {
-                out.setX((onScreenRectCenterX - insetAdjustment) / fullscreenScaleX
-                        - 1.0f * drawableWidth / 2);
-            } else {
-                out.setX((onScreenRectCenterX + insetAdjustment) / fullscreenScaleX
-                        - 1.0f * drawableWidth / 2);
-            }
-            out.setY(onScreenRectCenterY / fullscreenScaleY
-                    - 1.0f * drawableHeight / 2);
-        }
-    }
-
-    /**
-     * The split placeholder comes with a default inset to buffer the icon from the top of the
-     * screen. But if the device already has a large inset (from cutouts etc), use that instead.
-     */
-    private int getPlaceholderSizeAdjustment(DeviceProfile dp, boolean pinToRight) {
-        int insetThickness;
-        if (!dp.isLandscape) {
-            insetThickness = dp.getInsets().top;
-        } else {
-            insetThickness = pinToRight ? dp.getInsets().right : dp.getInsets().left;
-        }
-        return Math.max(insetThickness - dp.splitPlaceholderInset, 0);
-    }
-
-    @Override
-    public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
-            int splitInstructionsWidth) {
-        out.setPivotX(0);
-        out.setPivotY(splitInstructionsHeight);
-        out.setRotation(getDegreesRotated());
-        int distanceToEdge;
-        if (dp.isPhone) {
-            if (dp.isLandscape) {
-                distanceToEdge = out.getResources().getDimensionPixelSize(
-                        R.dimen.split_instructions_bottom_margin_phone_landscape);
-            } else {
-                distanceToEdge = out.getResources().getDimensionPixelSize(
-                        R.dimen.split_instructions_bottom_margin_phone_portrait);
-            }
-        } else {
-            distanceToEdge = dp.getOverviewActionsClaimedSpaceBelow();
-        }
-
-        // Center the view in case of unbalanced insets on left or right of screen
-        int insetCorrectionX = (dp.getInsets().right - dp.getInsets().left) / 2;
-        // Adjust for any insets on the bottom edge
-        int insetCorrectionY = dp.getInsets().bottom;
-        out.setTranslationX(insetCorrectionX);
-        out.setTranslationY(-distanceToEdge + insetCorrectionY);
-        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
-        lp.gravity = CENTER_HORIZONTAL | BOTTOM;
-        out.setLayoutParams(lp);
-    }
-
-    @Override
-    public void getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp,
-            @StagePosition int stagePosition, Rect out1, Rect out2) {
-        int screenHeight = dp.heightPx;
-        int screenWidth = dp.widthPx;
-        out1.set(0, 0, screenWidth, screenHeight / 2 - splitDividerSize);
-        out2.set(0, screenHeight / 2 + splitDividerSize, screenWidth, screenHeight);
-        if (!dp.isLeftRightSplit) {
-            // Portrait - the window bounds are always top and bottom half
-            return;
-        }
-
-        // Now we rotate the portrait rect depending on what side we want pinned
-        boolean pinToRight = stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
-        float postRotateScale = (float) screenHeight / screenWidth;
-
-        mTmpMatrix.reset();
-        mTmpMatrix.postRotate(pinToRight ? 90 : 270);
-        mTmpMatrix.postTranslate(pinToRight ? screenHeight : 0, pinToRight ? 0 : screenWidth);
-        mTmpMatrix.postScale(1 / postRotateScale, postRotateScale);
-
-        mTmpRectF.set(out1);
-        mTmpMatrix.mapRect(mTmpRectF);
-        mTmpRectF.roundOut(out1);
-
-        mTmpRectF.set(out2);
-        mTmpMatrix.mapRect(mTmpRectF);
-        mTmpRectF.roundOut(out2);
-    }
-
-    @Override
-    public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect,
-            SplitBounds splitInfo, int desiredStagePosition) {
-        float topLeftTaskPercent = splitInfo.getLeftTopTaskPercent();
-        float dividerBarPercent = splitInfo.getDividerPercent();
-
-        int taskbarHeight = dp.isTransientTaskbar ? 0 : dp.taskbarHeight;
-        float scale = (float) outRect.height() / (dp.availableHeightPx - taskbarHeight);
-        float topTaskHeight = dp.availableHeightPx * topLeftTaskPercent;
-        float scaledTopTaskHeight = topTaskHeight * scale;
-        float dividerHeight = dp.availableHeightPx * dividerBarPercent;
-        float scaledDividerHeight = dividerHeight * scale;
-
-        if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
-            if (dp.isLeftRightSplit) {
-                outRect.right = outRect.left + Math.round(outRect.width() * topLeftTaskPercent);
-            } else {
-                outRect.bottom = Math.round(outRect.top + scaledTopTaskHeight);
-            }
-        } else {
-            if (dp.isLeftRightSplit) {
-                outRect.left += Math.round(outRect.width()
-                        * (topLeftTaskPercent + dividerBarPercent));
-            } else {
-                outRect.top += Math.round(scaledTopTaskHeight + scaledDividerHeight);
-            }
-        }
-    }
-
-    /**
-     * @param inSplitSelection Whether user currently has a task from this task group staged for
-     *                         split screen. If true, we have custom translations/scaling in place
-     *                         for the remaining snapshot, so we'll skip setting translation/scale
-     *                         here.
-     */
-    @Override
-    public void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot,
-            int parentWidth, int parentHeight, SplitBounds splitBoundsConfig,
-            DeviceProfile dp, boolean isRtl, boolean inSplitSelection) {
-        int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
-
-        FrameLayout.LayoutParams primaryParams =
-                (FrameLayout.LayoutParams) primarySnapshot.getLayoutParams();
-        FrameLayout.LayoutParams secondaryParams =
-                (FrameLayout.LayoutParams) secondarySnapshot.getLayoutParams();
-
-        // Reset margins that aren't used in this method, but are used in other
-        // `RecentsPagedOrientationHandler` variants.
-        secondaryParams.topMargin = 0;
-        primaryParams.topMargin = spaceAboveSnapshot;
-
-        int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
-        float dividerScale = splitBoundsConfig.getDividerPercent();
-        Pair<Point, Point> taskViewSizes =
-                getGroupedTaskViewSizes(dp, splitBoundsConfig, parentWidth, parentHeight);
-        if (!inSplitSelection) {
-            // Reset translations that aren't used in this method, but are used in other
-            // `RecentsPagedOrientationHandler` variants.
-            primarySnapshot.setTranslationY(0);
-
-            if (dp.isLeftRightSplit) {
-                int scaledDividerBar = Math.round(parentWidth * dividerScale);
-                if (isRtl) {
-                    int translationX = taskViewSizes.second.x + scaledDividerBar;
-                    primarySnapshot.setTranslationX(-translationX);
-                    secondarySnapshot.setTranslationX(0);
-                } else {
-                    int translationX = taskViewSizes.first.x + scaledDividerBar;
-                    secondarySnapshot.setTranslationX(translationX);
-                    primarySnapshot.setTranslationX(0);
-                }
-                secondarySnapshot.setTranslationY(spaceAboveSnapshot);
-            } else {
-                float finalDividerHeight = Math.round(totalThumbnailHeight * dividerScale);
-                float translationY =
-                        taskViewSizes.first.y + spaceAboveSnapshot + finalDividerHeight;
-                secondarySnapshot.setTranslationY(translationY);
-
-                // Reset unused translations.
-                secondarySnapshot.setTranslationX(0);
-                primarySnapshot.setTranslationX(0);
-            }
-        }
-
-        primarySnapshot.measure(
-                View.MeasureSpec.makeMeasureSpec(taskViewSizes.first.x, View.MeasureSpec.EXACTLY),
-                View.MeasureSpec.makeMeasureSpec(taskViewSizes.first.y, View.MeasureSpec.EXACTLY));
-        secondarySnapshot.measure(
-                View.MeasureSpec.makeMeasureSpec(taskViewSizes.second.x, View.MeasureSpec.EXACTLY),
-                View.MeasureSpec.makeMeasureSpec(taskViewSizes.second.y,
-                        View.MeasureSpec.EXACTLY));
-    }
-
-    @Override
-    public Pair<Point, Point> getGroupedTaskViewSizes(
-            DeviceProfile dp,
-            SplitBounds splitBoundsConfig,
-            int parentWidth,
-            int parentHeight) {
-        int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
-        int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
-        float dividerScale = splitBoundsConfig.getDividerPercent();
-        float taskPercent = splitBoundsConfig.getLeftTopTaskPercent();
-
-        Point firstTaskViewSize = new Point();
-        Point secondTaskViewSize = new Point();
-
-        if (dp.isLeftRightSplit) {
-            int scaledDividerBar = Math.round(parentWidth * dividerScale);
-            firstTaskViewSize.x = Math.round(parentWidth * taskPercent);
-            firstTaskViewSize.y = totalThumbnailHeight;
-
-            secondTaskViewSize.x = parentWidth - firstTaskViewSize.x - scaledDividerBar;
-            secondTaskViewSize.y = totalThumbnailHeight;
-        } else {
-            int taskbarHeight = dp.isTransientTaskbar ? 0 : dp.taskbarHeight;
-            float scale = (float) totalThumbnailHeight / (dp.availableHeightPx - taskbarHeight);
-            float topTaskHeight = dp.availableHeightPx * taskPercent;
-            float finalDividerHeight = Math.round(totalThumbnailHeight * dividerScale);
-            float scaledTopTaskHeight = topTaskHeight * scale;
-            firstTaskViewSize.x = parentWidth;
-            firstTaskViewSize.y = Math.round(scaledTopTaskHeight);
-
-            secondTaskViewSize.x = parentWidth;
-            secondTaskViewSize.y = Math.round(totalThumbnailHeight - firstTaskViewSize.y
-                    - finalDividerHeight);
-        }
-
-        return new Pair<>(firstTaskViewSize, secondTaskViewSize);
-    }
-
-    @Override
-    public void setTaskIconParams(FrameLayout.LayoutParams iconParams, int taskIconMargin,
-            int taskIconHeight, int thumbnailTopMargin, boolean isRtl) {
-        iconParams.gravity = TOP | CENTER_HORIZONTAL;
-        // Reset margins, since they may have been set on rotation
-        iconParams.leftMargin = iconParams.rightMargin = 0;
-        iconParams.topMargin = iconParams.bottomMargin = 0;
-    }
-
-    @Override
-    public void setIconAppChipChildrenParams(FrameLayout.LayoutParams iconParams,
-            int chipChildMarginStart) {
-        iconParams.setMarginStart(chipChildMarginStart);
-        iconParams.gravity = Gravity.START | Gravity.CENTER_VERTICAL;
-        iconParams.topMargin = 0;
-    }
-
-    @Override
-    public void setIconAppChipMenuParams(IconAppChipView iconAppChipView,
-            FrameLayout.LayoutParams iconMenuParams, int iconMenuMargin, int thumbnailTopMargin) {
-        iconMenuParams.gravity = TOP | START;
-        iconMenuParams.setMarginStart(iconMenuMargin);
-        iconMenuParams.topMargin = thumbnailTopMargin;
-        iconMenuParams.bottomMargin = 0;
-        iconMenuParams.setMarginEnd(0);
-
-        iconAppChipView.setPivotX(0);
-        iconAppChipView.setPivotY(0);
-        iconAppChipView.setSplitTranslationY(0);
-        iconAppChipView.setRotation(getDegreesRotated());
-    }
-
-    /**
-     * @param inSplitSelection Whether user currently has a task from this task group staged for
-     *                         split screen. If true, we have custom translations in place for the
-     *                         remaining icon, so we'll skip setting translations here.
-     */
-    @Override
-    public void setSplitIconParams(View primaryIconView, View secondaryIconView,
-            int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight,
-            int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl,
-            DeviceProfile deviceProfile, SplitBounds splitConfig, boolean inSplitSelection,
-            boolean oneIconHiddenDueToSmallWidth) {
-        FrameLayout.LayoutParams primaryIconParams =
-                (FrameLayout.LayoutParams) primaryIconView.getLayoutParams();
-        FrameLayout.LayoutParams secondaryIconParams = enableOverviewIconMenu()
-                ? (FrameLayout.LayoutParams) secondaryIconView.getLayoutParams()
-                : new FrameLayout.LayoutParams(primaryIconParams);
-
-        if (enableOverviewIconMenu()) {
-            IconAppChipView primaryAppChipView = (IconAppChipView) primaryIconView;
-            IconAppChipView secondaryAppChipView = (IconAppChipView) secondaryIconView;
-            primaryIconParams.gravity = TOP | START;
-            secondaryIconParams.gravity = TOP | START;
-            secondaryIconParams.topMargin = primaryIconParams.topMargin;
-            secondaryIconParams.setMarginStart(primaryIconParams.getMarginStart());
-            if (!inSplitSelection) {
-                if (deviceProfile.isLeftRightSplit) {
-                    if (isRtl) {
-                        int secondarySnapshotWidth = groupedTaskViewWidth - primarySnapshotWidth;
-                        primaryAppChipView.setSplitTranslationX(-secondarySnapshotWidth);
-                    } else {
-                        secondaryAppChipView.setSplitTranslationX(primarySnapshotWidth);
-                    }
-                } else {
-                    primaryAppChipView.setSplitTranslationX(0);
-                    secondaryAppChipView.setSplitTranslationX(0);
-                    int dividerThickness = Math.min(splitConfig.visualDividerBounds.width(),
-                            splitConfig.visualDividerBounds.height());
-                    secondaryAppChipView.setSplitTranslationY(
-                            primarySnapshotHeight + (deviceProfile.isTablet ? 0
-                                    : dividerThickness));
-                }
-            }
-        } else if (deviceProfile.isLeftRightSplit) {
-            // We calculate the "midpoint" of the thumbnail area, and place the icons there.
-            // This is the place where the thumbnail area splits by default, in a near-50/50 split.
-            // It is usually not exactly 50/50, due to insets/screen cutouts.
-            int fullscreenInsetThickness = deviceProfile.isSeascape()
-                    ? deviceProfile.getInsets().right
-                    : deviceProfile.getInsets().left;
-            int fullscreenMidpointFromBottom = ((deviceProfile.widthPx
-                    - fullscreenInsetThickness) / 2);
-            float midpointFromEndPct = (float) fullscreenMidpointFromBottom
-                    / deviceProfile.widthPx;
-            float insetPct = (float) fullscreenInsetThickness / deviceProfile.widthPx;
-            int spaceAboveSnapshots = 0;
-            int overviewThumbnailAreaThickness = groupedTaskViewWidth - spaceAboveSnapshots;
-            int bottomToMidpointOffset = (int) (overviewThumbnailAreaThickness
-                    * midpointFromEndPct);
-            int insetOffset = (int) (overviewThumbnailAreaThickness * insetPct);
-
-            if (deviceProfile.isSeascape()) {
-                primaryIconParams.gravity = TOP | (isRtl ? END : START);
-                secondaryIconParams.gravity = TOP | (isRtl ? END : START);
-                if (!inSplitSelection) {
-                    if (splitConfig.initiatedFromSeascape) {
-                        if (oneIconHiddenDueToSmallWidth) {
-                            // Center both icons
-                            float centerX = bottomToMidpointOffset - (taskIconHeight / 2f);
-                            primaryIconView.setTranslationX(centerX);
-                            secondaryIconView.setTranslationX(centerX);
-                        } else {
-                            // the task on the right (secondary) is slightly larger
-                            primaryIconView.setTranslationX(
-                                    bottomToMidpointOffset - taskIconHeight);
-                            secondaryIconView.setTranslationX(bottomToMidpointOffset);
-                        }
-                    } else {
-                        if (oneIconHiddenDueToSmallWidth) {
-                            // Center both icons
-                            float centerX =
-                                    bottomToMidpointOffset + insetOffset - (taskIconHeight / 2f);
-                            primaryIconView.setTranslationX(centerX);
-                            secondaryIconView.setTranslationX(centerX);
-                        } else {
-                            // the task on the left (primary) is slightly larger
-                            primaryIconView.setTranslationX(bottomToMidpointOffset + insetOffset
-                                    - taskIconHeight);
-                            secondaryIconView.setTranslationX(bottomToMidpointOffset + insetOffset);
-                        }
-                    }
-                }
-            } else {
-                primaryIconParams.gravity = TOP | (isRtl ? START : END);
-                secondaryIconParams.gravity = TOP | (isRtl ? START : END);
-                if (!inSplitSelection) {
-                    if (!splitConfig.initiatedFromSeascape) {
-                        if (oneIconHiddenDueToSmallWidth) {
-                            // Center both icons
-                            float centerX = -bottomToMidpointOffset + (taskIconHeight / 2f);
-                            primaryIconView.setTranslationX(centerX);
-                            secondaryIconView.setTranslationX(centerX);
-                        } else {
-                            // the task on the left (primary) is slightly larger
-                            primaryIconView.setTranslationX(-bottomToMidpointOffset);
-                            secondaryIconView.setTranslationX(
-                                    -bottomToMidpointOffset + taskIconHeight);
-                        }
-                    } else {
-                        if (oneIconHiddenDueToSmallWidth) {
-                            // Center both icons
-                            float centerX =
-                                    -bottomToMidpointOffset - insetOffset + (taskIconHeight / 2f);
-                            primaryIconView.setTranslationX(centerX);
-                            secondaryIconView.setTranslationX(centerX);
-                        } else {
-                            // the task on the right (secondary) is slightly larger
-                            primaryIconView.setTranslationX(-bottomToMidpointOffset - insetOffset);
-                            secondaryIconView.setTranslationX(-bottomToMidpointOffset - insetOffset
-                                    + taskIconHeight);
-                        }
-                    }
-                }
-            }
-        } else {
-            primaryIconParams.gravity = TOP | CENTER_HORIZONTAL;
-            secondaryIconParams.gravity = TOP | CENTER_HORIZONTAL;
-            if (!inSplitSelection) {
-                if (oneIconHiddenDueToSmallWidth) {
-                    // Center both icons
-                    primaryIconView.setTranslationX(0);
-                    secondaryIconView.setTranslationX(0);
-                } else {
-                    // shifts icon half a width left (height is used here since icons are square)
-                    primaryIconView.setTranslationX(-(taskIconHeight / 2f));
-                    secondaryIconView.setTranslationX(taskIconHeight / 2f);
-                }
-            }
-        }
-        if (!enableOverviewIconMenu() && !inSplitSelection) {
-            primaryIconView.setTranslationY(0);
-            secondaryIconView.setTranslationY(0);
-        }
-
-
-        primaryIconView.setLayoutParams(primaryIconParams);
-        secondaryIconView.setLayoutParams(secondaryIconParams);
-    }
-
-    @Override
-    public int getDefaultSplitPosition(DeviceProfile deviceProfile) {
-        if (!deviceProfile.isTablet) {
-            throw new IllegalStateException("Default position available only for large screens");
-        }
-        if (deviceProfile.isLeftRightSplit) {
-            return STAGE_POSITION_BOTTOM_OR_RIGHT;
-        } else {
-            return STAGE_POSITION_TOP_OR_LEFT;
-        }
-    }
-
-    @Override
-    public Pair<FloatProperty, FloatProperty> getSplitSelectTaskOffset(FloatProperty primary,
-            FloatProperty secondary, DeviceProfile deviceProfile) {
-        if (deviceProfile.isLeftRightSplit) { // or seascape
-            return new Pair<>(primary, secondary);
-        } else {
-            return new Pair<>(secondary, primary);
-        }
-    }
-
-    @Override
-    public float getFloatingTaskOffscreenTranslationTarget(View floatingTask, RectF onScreenRect,
-            @StagePosition int stagePosition, DeviceProfile dp) {
-        if (dp.isLeftRightSplit) {
-            float currentTranslationX = floatingTask.getTranslationX();
-            return stagePosition == STAGE_POSITION_TOP_OR_LEFT
-                    ? currentTranslationX - onScreenRect.width()
-                    : currentTranslationX + onScreenRect.width();
-        } else {
-            float currentTranslationY = floatingTask.getTranslationY();
-            return currentTranslationY - onScreenRect.height();
-        }
-    }
-
-    @Override
-    public void setFloatingTaskPrimaryTranslation(View floatingTask, float translation,
-            DeviceProfile dp) {
-        if (dp.isLeftRightSplit) {
-            floatingTask.setTranslationX(translation);
-        } else {
-            floatingTask.setTranslationY(translation);
-        }
-
-    }
-
-    @Override
-    public float getFloatingTaskPrimaryTranslation(View floatingTask, DeviceProfile dp) {
-        return dp.isLeftRightSplit
-                ? 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/PortraitPagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.kt
new file mode 100644
index 0000000..15eb69e
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.kt
@@ -0,0 +1,915 @@
+/*
+ * Copyright (C) 2025 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.orientation
+
+import android.graphics.Matrix
+import android.graphics.Point
+import android.graphics.PointF
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.drawable.ShapeDrawable
+import android.util.FloatProperty
+import android.util.Pair
+import android.view.Gravity
+import android.view.Surface
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import androidx.core.view.updateLayoutParams
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.Flags.enableOverviewIconMenu
+import com.android.launcher3.LauncherAnimUtils
+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.PagedOrientationHandler.Float2DAction
+import com.android.launcher3.touch.PagedOrientationHandler.Int2DAction
+import com.android.launcher3.touch.SingleAxisSwipeDetector
+import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition
+import com.android.quickstep.views.IconAppChipView
+import kotlin.math.max
+import kotlin.math.min
+
+class PortraitPagedViewHandler : DefaultPagedViewHandler(), RecentsPagedOrientationHandler {
+    private val tmpMatrix = Matrix()
+    private val tmpRectF = RectF()
+
+    override fun <T> getPrimaryValue(x: T, y: T): T = x
+
+    override fun <T> getSecondaryValue(x: T, y: T): T = y
+
+    override val isLayoutNaturalToLauncher: Boolean = true
+
+    override fun adjustFloatingIconStartVelocity(velocity: PointF) {
+        // no-op
+    }
+
+    override fun fixBoundsForHomeAnimStartRect(outStartRect: RectF, deviceProfile: DeviceProfile) {
+        if (outStartRect.left > deviceProfile.widthPx) {
+            outStartRect.offsetTo(0f, outStartRect.top)
+        } else if (outStartRect.left < -deviceProfile.widthPx) {
+            outStartRect.offsetTo(0f, outStartRect.top)
+        }
+    }
+
+    override fun <T> setSecondary(target: T, action: Float2DAction<T>, param: Float) =
+        action.call(target, 0f, param)
+
+    override fun <T> set(
+        target: T,
+        action: Int2DAction<T>,
+        primaryParam: Int,
+        secondaryParam: Int,
+    ) = action.call(target, primaryParam, secondaryParam)
+
+    override fun getPrimarySize(view: View): Int = view.width
+
+    override fun getPrimarySize(rect: RectF): Float = rect.width()
+
+    override fun getStart(rect: RectF): Float = rect.left
+
+    override fun getEnd(rect: RectF): Float = rect.right
+
+    override fun rotateInsets(insets: Rect, outInsets: Rect) = outInsets.set(insets)
+
+    override fun getClearAllSidePadding(view: View, isRtl: Boolean): Int =
+        (if (isRtl) view.paddingRight else -view.paddingLeft) / 2
+
+    override fun getSecondaryDimension(view: View): Int = view.height
+
+    override val primaryViewTranslate: FloatProperty<View> = LauncherAnimUtils.VIEW_TRANSLATE_X
+
+    override val secondaryViewTranslate: FloatProperty<View> = LauncherAnimUtils.VIEW_TRANSLATE_Y
+
+    override val degreesRotated: Float = 0f
+
+    override val rotation: Int = Surface.ROTATION_0
+
+    override fun setPrimaryScale(view: View, scale: Float) {
+        view.scaleX = scale
+    }
+
+    override fun setSecondaryScale(view: View, scale: Float) {
+        view.scaleY = scale
+    }
+
+    override val secondaryTranslationDirectionFactor: Int
+        get() = -1
+
+    override fun getSplitTranslationDirectionFactor(
+        stagePosition: Int,
+        deviceProfile: DeviceProfile,
+    ): Int =
+        if (
+            deviceProfile.isLeftRightSplit &&
+                stagePosition == SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
+        ) {
+            -1
+        } else {
+            1
+        }
+
+    override fun getTaskMenuX(
+        x: Float,
+        thumbnailView: View,
+        deviceProfile: DeviceProfile,
+        taskInsetMargin: Float,
+        taskViewIcon: View,
+    ): Float =
+        if (deviceProfile.isLandscape) {
+            (x +
+                taskInsetMargin +
+                (thumbnailView.measuredWidth - thumbnailView.measuredHeight) / 2f)
+        } else {
+            x + taskInsetMargin
+        }
+
+    override fun getTaskMenuY(
+        y: Float,
+        thumbnailView: View,
+        stagePosition: Int,
+        taskMenuView: View,
+        taskInsetMargin: Float,
+        taskViewIcon: View,
+    ): Float = y + taskInsetMargin
+
+    override fun getTaskMenuWidth(
+        thumbnailView: View,
+        deviceProfile: DeviceProfile,
+        @StagePosition stagePosition: Int,
+    ): Int =
+        when {
+            enableOverviewIconMenu() -> {
+                thumbnailView.resources.getDimensionPixelSize(
+                    R.dimen.task_thumbnail_icon_menu_expanded_width
+                )
+            }
+
+            (deviceProfile.isLandscape && !deviceProfile.isTablet) -> {
+                val padding =
+                    thumbnailView.resources.getDimensionPixelSize(R.dimen.task_menu_edge_padding)
+                thumbnailView.measuredHeight - (2 * padding)
+            }
+
+            else -> {
+                val padding =
+                    thumbnailView.resources.getDimensionPixelSize(R.dimen.task_menu_edge_padding)
+                thumbnailView.measuredWidth - (2 * padding)
+            }
+        }
+
+    override fun getTaskMenuHeight(
+        taskInsetMargin: Float,
+        deviceProfile: DeviceProfile,
+        taskMenuX: Float,
+        taskMenuY: Float,
+    ): Int =
+        deviceProfile.heightPx -
+            deviceProfile.insets.top -
+            taskMenuY.toInt() -
+            deviceProfile.overviewActionsClaimedSpaceBelow
+
+    override fun setTaskOptionsMenuLayoutOrientation(
+        deviceProfile: DeviceProfile,
+        taskMenuLayout: LinearLayout,
+        dividerSpacing: Int,
+        dividerDrawable: ShapeDrawable,
+    ) {
+        taskMenuLayout.orientation = LinearLayout.VERTICAL
+        dividerDrawable.intrinsicHeight = dividerSpacing
+        taskMenuLayout.dividerDrawable = dividerDrawable
+    }
+
+    override fun setLayoutParamsForTaskMenuOptionItem(
+        lp: LinearLayout.LayoutParams,
+        viewGroup: LinearLayout,
+        deviceProfile: DeviceProfile,
+    ) {
+        viewGroup.orientation = LinearLayout.HORIZONTAL
+        lp.width = LinearLayout.LayoutParams.MATCH_PARENT
+        lp.height = ViewGroup.LayoutParams.WRAP_CONTENT
+    }
+
+    override fun updateDwbBannerLayout(
+        taskViewWidth: Int,
+        taskViewHeight: Int,
+        isGroupedTaskView: Boolean,
+        deviceProfile: DeviceProfile,
+        snapshotViewWidth: Int,
+        snapshotViewHeight: Int,
+        banner: View,
+    ) {
+        banner.pivotX = 0f
+        banner.pivotY = 0f
+        banner.rotation = degreesRotated
+        banner.updateLayoutParams<FrameLayout.LayoutParams> {
+            if (isGroupedTaskView) {
+                gravity =
+                    Gravity.BOTTOM or
+                        (if (deviceProfile.isLeftRightSplit) Gravity.START
+                        else Gravity.CENTER_HORIZONTAL)
+                width = snapshotViewWidth
+            } else {
+                width = ViewGroup.LayoutParams.MATCH_PARENT
+                gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
+            }
+        }
+    }
+
+    override fun getDwbBannerTranslations(
+        taskViewWidth: Int,
+        taskViewHeight: Int,
+        splitBounds: SplitConfigurationOptions.SplitBounds?,
+        deviceProfile: DeviceProfile,
+        thumbnailViews: Array<View>,
+        desiredTaskId: Int,
+        banner: View,
+    ): Pair<Float, Float> {
+        var translationX = 0f
+        var translationY = 0f
+        if (splitBounds != null) {
+            if (deviceProfile.isLeftRightSplit) {
+                if (desiredTaskId == splitBounds.rightBottomTaskId) {
+                    val leftTopTaskPercent = splitBounds.leftTopTaskPercent
+                    val dividerThicknessPercent = splitBounds.dividerPercent
+                    translationX =
+                        ((taskViewWidth * leftTopTaskPercent) +
+                            (taskViewWidth * dividerThicknessPercent))
+                }
+            } else {
+                if (desiredTaskId == splitBounds.leftTopTaskId) {
+                    val snapshotParams = thumbnailViews[0].layoutParams as LinearLayout.LayoutParams
+                    val bottomRightTaskPlusDividerPercent =
+                        (splitBounds.rightBottomTaskPercent + splitBounds.dividerPercent)
+                    translationY =
+                        -((taskViewHeight - snapshotParams.topMargin) *
+                            bottomRightTaskPlusDividerPercent)
+                }
+            }
+        }
+        return Pair(translationX, translationY)
+    }
+
+    /* ---------- The following are only used by TaskViewTouchHandler. ---------- */
+
+    override val upDownSwipeDirection: SingleAxisSwipeDetector.Direction =
+        SingleAxisSwipeDetector.VERTICAL
+
+    // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
+    override fun getUpDirection(isRtl: Boolean): Int = SingleAxisSwipeDetector.DIRECTION_POSITIVE
+
+    // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
+    override fun getDownDirection(isRtl: Boolean): Int = SingleAxisSwipeDetector.DIRECTION_NEGATIVE
+
+    // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
+    override fun isGoingUp(displacement: Float, isRtl: Boolean): Boolean = displacement < 0
+
+    // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
+    override fun getTaskDragDisplacementFactor(isRtl: Boolean): Int = 1
+
+    override fun getTaskDismissVerticalDirection(): Int = -1
+
+    override fun getTaskDismissLength(secondaryDimension: Int, taskThumbnailBounds: Rect): Int =
+        taskThumbnailBounds.bottom
+
+    override fun getTaskLaunchLength(secondaryDimension: Int, taskThumbnailBounds: Rect): Int =
+        secondaryDimension - taskThumbnailBounds.bottom
+
+    /* -------------------- */
+
+    override fun getDistanceToBottomOfRect(dp: DeviceProfile, rect: Rect): Int =
+        dp.heightPx - rect.bottom
+
+    override fun getSplitPositionOptions(dp: DeviceProfile): List<SplitPositionOption> =
+        when {
+            dp.isTablet -> {
+                Utilities.getSplitPositionOptions(dp)
+            }
+
+            dp.isSeascape -> {
+                listOf(
+                    SplitPositionOption(
+                        R.drawable.ic_split_horizontal,
+                        R.string.recent_task_option_split_screen,
+                        SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT,
+                        SplitConfigurationOptions.STAGE_TYPE_MAIN,
+                    )
+                )
+            }
+
+            dp.isLeftRightSplit -> {
+                listOf(
+                    SplitPositionOption(
+                        R.drawable.ic_split_horizontal,
+                        R.string.recent_task_option_split_screen,
+                        SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT,
+                        SplitConfigurationOptions.STAGE_TYPE_MAIN,
+                    )
+                )
+            }
+
+            else -> {
+                // Only add top option
+                listOf(
+                    SplitPositionOption(
+                        R.drawable.ic_split_vertical,
+                        R.string.recent_task_option_split_screen,
+                        SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT,
+                        SplitConfigurationOptions.STAGE_TYPE_MAIN,
+                    )
+                )
+            }
+        }
+
+    override fun getInitialSplitPlaceholderBounds(
+        placeholderHeight: Int,
+        placeholderInset: Int,
+        dp: DeviceProfile,
+        @StagePosition stagePosition: Int,
+        out: Rect,
+    ) {
+        val screenWidth = dp.widthPx
+        val screenHeight = dp.heightPx
+        val pinToRight = stagePosition == SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
+        val insetSizeAdjustment = getPlaceholderSizeAdjustment(dp, pinToRight)
+
+        out.set(0, 0, screenWidth, placeholderHeight + insetSizeAdjustment)
+        if (!dp.isLeftRightSplit) {
+            // portrait, phone or tablet - spans width of screen, nothing else to do
+            out.inset(placeholderInset, 0)
+
+            // Adjust the top to account for content off screen. This will help to animate the view
+            // in with rounded corners.
+            val totalHeight =
+                (1.0f * screenHeight / 2 * (screenWidth - 2 * placeholderInset) / screenWidth)
+                    .toInt()
+            out.top -= (totalHeight - placeholderHeight)
+            return
+        }
+
+        // Now we rotate the portrait rect depending on what side we want pinned
+        val postRotateScale = screenHeight.toFloat() / screenWidth
+        tmpMatrix.reset()
+        tmpMatrix.postRotate(if (pinToRight) 90f else 270f)
+        tmpMatrix.postTranslate(
+            (if (pinToRight) screenWidth else 0).toFloat(),
+            (if (pinToRight) 0 else screenWidth).toFloat(),
+        )
+        // The placeholder height stays constant after rotation, so we don't change width scale
+        tmpMatrix.postScale(1f, postRotateScale)
+
+        tmpRectF.set(out)
+        tmpMatrix.mapRect(tmpRectF)
+        tmpRectF.inset(0f, placeholderInset.toFloat())
+        tmpRectF.roundOut(out)
+
+        // Adjust the top to account for content off screen. This will help to animate the view in
+        // with rounded corners.
+        val totalWidth =
+            (1.0f * screenWidth / 2 * (screenHeight - 2 * placeholderInset) / screenHeight).toInt()
+        val width = out.width()
+        if (pinToRight) {
+            out.right += totalWidth - width
+        } else {
+            out.left -= totalWidth - width
+        }
+    }
+
+    override fun updateSplitIconParams(
+        out: View,
+        onScreenRectCenterX: Float,
+        onScreenRectCenterY: Float,
+        fullscreenScaleX: Float,
+        fullscreenScaleY: Float,
+        drawableWidth: Int,
+        drawableHeight: Int,
+        dp: DeviceProfile,
+        @StagePosition stagePosition: Int,
+    ) {
+        val pinToRight = stagePosition == SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
+        val insetAdjustment = getPlaceholderSizeAdjustment(dp, pinToRight) / 2f
+        if (!dp.isLeftRightSplit) {
+            out.x = (onScreenRectCenterX / fullscreenScaleX - 1.0f * drawableWidth / 2)
+            out.y =
+                ((onScreenRectCenterY + insetAdjustment) / fullscreenScaleY -
+                    1.0f * drawableHeight / 2)
+        } else {
+            if (pinToRight) {
+                out.x =
+                    ((onScreenRectCenterX - insetAdjustment) / fullscreenScaleX -
+                        1.0f * drawableWidth / 2)
+            } else {
+                out.x =
+                    ((onScreenRectCenterX + insetAdjustment) / fullscreenScaleX -
+                        1.0f * drawableWidth / 2)
+            }
+            out.y = (onScreenRectCenterY / fullscreenScaleY - 1.0f * drawableHeight / 2)
+        }
+    }
+
+    /**
+     * The split placeholder comes with a default inset to buffer the icon from the top of the
+     * screen. But if the device already has a large inset (from cutouts etc), use that instead.
+     */
+    private fun getPlaceholderSizeAdjustment(dp: DeviceProfile, pinToRight: Boolean): Int {
+        val insetThickness =
+            if (!dp.isLandscape) {
+                dp.insets.top
+            } else {
+                if (pinToRight) dp.insets.right else dp.insets.left
+            }
+        return max((insetThickness - dp.splitPlaceholderInset).toDouble(), 0.0).toInt()
+    }
+
+    override fun setSplitInstructionsParams(
+        out: View,
+        dp: DeviceProfile,
+        splitInstructionsHeight: Int,
+        splitInstructionsWidth: Int,
+    ) {
+        out.pivotX = 0f
+        out.pivotY = splitInstructionsHeight.toFloat()
+        out.rotation = degreesRotated
+        val distanceToEdge =
+            if (dp.isPhone) {
+                if (dp.isLandscape) {
+                    out.resources.getDimensionPixelSize(
+                        R.dimen.split_instructions_bottom_margin_phone_landscape
+                    )
+                } else {
+                    out.resources.getDimensionPixelSize(
+                        R.dimen.split_instructions_bottom_margin_phone_portrait
+                    )
+                }
+            } else {
+                dp.overviewActionsClaimedSpaceBelow
+            }
+
+        // Center the view in case of unbalanced insets on left or right of screen
+        val insetCorrectionX = (dp.insets.right - dp.insets.left) / 2
+        // Adjust for any insets on the bottom edge
+        val insetCorrectionY = dp.insets.bottom
+        out.translationX = insetCorrectionX.toFloat()
+        out.translationY = (-distanceToEdge + insetCorrectionY).toFloat()
+        val lp = out.layoutParams as FrameLayout.LayoutParams
+        lp.gravity = Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM
+        out.layoutParams = lp
+    }
+
+    override fun getFinalSplitPlaceholderBounds(
+        splitDividerSize: Int,
+        dp: DeviceProfile,
+        @StagePosition stagePosition: Int,
+        out1: Rect,
+        out2: Rect,
+    ) {
+        val screenHeight = dp.heightPx
+        val screenWidth = dp.widthPx
+        out1.set(0, 0, screenWidth, screenHeight / 2 - splitDividerSize)
+        out2.set(0, screenHeight / 2 + splitDividerSize, screenWidth, screenHeight)
+        if (!dp.isLeftRightSplit) {
+            // Portrait - the window bounds are always top and bottom half
+            return
+        }
+
+        // Now we rotate the portrait rect depending on what side we want pinned
+        val pinToRight = stagePosition == SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
+        val postRotateScale = screenHeight.toFloat() / screenWidth
+
+        tmpMatrix.reset()
+        tmpMatrix.postRotate(if (pinToRight) 90f else 270f)
+        tmpMatrix.postTranslate(
+            (if (pinToRight) screenHeight else 0).toFloat(),
+            (if (pinToRight) 0 else screenWidth).toFloat(),
+        )
+        tmpMatrix.postScale(1 / postRotateScale, postRotateScale)
+
+        tmpRectF.set(out1)
+        tmpMatrix.mapRect(tmpRectF)
+        tmpRectF.roundOut(out1)
+
+        tmpRectF.set(out2)
+        tmpMatrix.mapRect(tmpRectF)
+        tmpRectF.roundOut(out2)
+    }
+
+    override fun setSplitTaskSwipeRect(
+        dp: DeviceProfile,
+        outRect: Rect,
+        splitInfo: SplitConfigurationOptions.SplitBounds,
+        desiredStagePosition: Int,
+    ) {
+        val topLeftTaskPercent = splitInfo.leftTopTaskPercent
+        val dividerBarPercent = splitInfo.dividerPercent
+
+        val taskbarHeight = if (dp.isTransientTaskbar) 0 else dp.taskbarHeight
+        val scale = outRect.height().toFloat() / (dp.availableHeightPx - taskbarHeight)
+        val topTaskHeight = dp.availableHeightPx * topLeftTaskPercent
+        val scaledTopTaskHeight = topTaskHeight * scale
+        val dividerHeight = dp.availableHeightPx * dividerBarPercent
+        val scaledDividerHeight = dividerHeight * scale
+
+        if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
+            if (dp.isLeftRightSplit) {
+                outRect.right = outRect.left + Math.round(outRect.width() * topLeftTaskPercent)
+            } else {
+                outRect.bottom = Math.round(outRect.top + scaledTopTaskHeight)
+            }
+        } else {
+            if (dp.isLeftRightSplit) {
+                outRect.left +=
+                    Math.round(outRect.width() * (topLeftTaskPercent + dividerBarPercent))
+            } else {
+                outRect.top += Math.round(scaledTopTaskHeight + scaledDividerHeight)
+            }
+        }
+    }
+
+    /**
+     * @param inSplitSelection Whether user currently has a task from this task group staged for
+     *   split screen. If true, we have custom translations/scaling in place for the remaining
+     *   snapshot, so we'll skip setting translation/scale here.
+     */
+    override fun measureGroupedTaskViewThumbnailBounds(
+        primarySnapshot: View,
+        secondarySnapshot: View,
+        parentWidth: Int,
+        parentHeight: Int,
+        splitBoundsConfig: SplitConfigurationOptions.SplitBounds,
+        dp: DeviceProfile,
+        isRtl: Boolean,
+        inSplitSelection: Boolean,
+    ) {
+        val spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx
+
+        val primaryParams = primarySnapshot.layoutParams as FrameLayout.LayoutParams
+        val secondaryParams = secondarySnapshot.layoutParams as FrameLayout.LayoutParams
+
+        // Reset margins that aren't used in this method, but are used in other
+        // `RecentsPagedOrientationHandler` variants.
+        secondaryParams.topMargin = 0
+        primaryParams.topMargin = spaceAboveSnapshot
+
+        val totalThumbnailHeight = parentHeight - spaceAboveSnapshot
+        val dividerScale = splitBoundsConfig.dividerPercent
+        val taskViewSizes =
+            getGroupedTaskViewSizes(dp, splitBoundsConfig, parentWidth, parentHeight)
+        if (!inSplitSelection) {
+            // Reset translations that aren't used in this method, but are used in other
+            // `RecentsPagedOrientationHandler` variants.
+            primarySnapshot.translationY = 0f
+
+            if (dp.isLeftRightSplit) {
+                val scaledDividerBar = Math.round(parentWidth * dividerScale)
+                if (isRtl) {
+                    val translationX = taskViewSizes.second.x + scaledDividerBar
+                    primarySnapshot.translationX = -translationX.toFloat()
+                    secondarySnapshot.translationX = 0f
+                } else {
+                    val translationX = taskViewSizes.first.x + scaledDividerBar
+                    secondarySnapshot.translationX = translationX.toFloat()
+                    primarySnapshot.translationX = 0f
+                }
+                secondarySnapshot.translationY = spaceAboveSnapshot.toFloat()
+            } else {
+                val finalDividerHeight = Math.round(totalThumbnailHeight * dividerScale).toFloat()
+                val translationY = taskViewSizes.first.y + spaceAboveSnapshot + finalDividerHeight
+                secondarySnapshot.translationY = translationY
+
+                // Reset unused translations.
+                secondarySnapshot.translationX = 0f
+                primarySnapshot.translationX = 0f
+            }
+        }
+
+        primarySnapshot.measure(
+            View.MeasureSpec.makeMeasureSpec(taskViewSizes.first.x, View.MeasureSpec.EXACTLY),
+            View.MeasureSpec.makeMeasureSpec(taskViewSizes.first.y, View.MeasureSpec.EXACTLY),
+        )
+        secondarySnapshot.measure(
+            View.MeasureSpec.makeMeasureSpec(taskViewSizes.second.x, View.MeasureSpec.EXACTLY),
+            View.MeasureSpec.makeMeasureSpec(taskViewSizes.second.y, View.MeasureSpec.EXACTLY),
+        )
+    }
+
+    override fun getGroupedTaskViewSizes(
+        dp: DeviceProfile,
+        splitBoundsConfig: SplitConfigurationOptions.SplitBounds,
+        parentWidth: Int,
+        parentHeight: Int,
+    ): Pair<Point, Point> {
+        val spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx
+        val totalThumbnailHeight = parentHeight - spaceAboveSnapshot
+        val dividerScale = splitBoundsConfig.dividerPercent
+        val taskPercent = splitBoundsConfig.leftTopTaskPercent
+
+        val firstTaskViewSize = Point()
+        val secondTaskViewSize = Point()
+
+        if (dp.isLeftRightSplit) {
+            val scaledDividerBar = Math.round(parentWidth * dividerScale)
+            firstTaskViewSize.x = Math.round(parentWidth * taskPercent)
+            firstTaskViewSize.y = totalThumbnailHeight
+
+            secondTaskViewSize.x = parentWidth - firstTaskViewSize.x - scaledDividerBar
+            secondTaskViewSize.y = totalThumbnailHeight
+        } else {
+            val taskbarHeight = if (dp.isTransientTaskbar) 0 else dp.taskbarHeight
+            val scale = totalThumbnailHeight.toFloat() / (dp.availableHeightPx - taskbarHeight)
+            val topTaskHeight = dp.availableHeightPx * taskPercent
+            val finalDividerHeight = Math.round(totalThumbnailHeight * dividerScale).toFloat()
+            val scaledTopTaskHeight = topTaskHeight * scale
+            firstTaskViewSize.x = parentWidth
+            firstTaskViewSize.y = Math.round(scaledTopTaskHeight)
+
+            secondTaskViewSize.x = parentWidth
+            secondTaskViewSize.y =
+                Math.round((totalThumbnailHeight - firstTaskViewSize.y - finalDividerHeight))
+        }
+
+        return Pair(firstTaskViewSize, secondTaskViewSize)
+    }
+
+    override fun setTaskIconParams(
+        iconParams: FrameLayout.LayoutParams,
+        taskIconMargin: Int,
+        taskIconHeight: Int,
+        thumbnailTopMargin: Int,
+        isRtl: Boolean,
+    ) {
+        iconParams.gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
+        // Reset margins, since they may have been set on rotation
+        iconParams.rightMargin = 0
+        iconParams.leftMargin = iconParams.rightMargin
+        iconParams.bottomMargin = 0
+        iconParams.topMargin = iconParams.bottomMargin
+    }
+
+    override fun setIconAppChipChildrenParams(
+        iconParams: FrameLayout.LayoutParams,
+        chipChildMarginStart: Int,
+    ) {
+        iconParams.marginStart = chipChildMarginStart
+        iconParams.gravity = Gravity.START or Gravity.CENTER_VERTICAL
+        iconParams.topMargin = 0
+    }
+
+    override fun setIconAppChipMenuParams(
+        iconAppChipView: IconAppChipView,
+        iconMenuParams: FrameLayout.LayoutParams,
+        iconMenuMargin: Int,
+        thumbnailTopMargin: Int,
+    ) {
+        iconMenuParams.gravity = Gravity.TOP or Gravity.START
+        iconMenuParams.marginStart = iconMenuMargin
+        iconMenuParams.topMargin = thumbnailTopMargin
+        iconMenuParams.bottomMargin = 0
+        iconMenuParams.marginEnd = 0
+
+        iconAppChipView.pivotX = 0f
+        iconAppChipView.pivotY = 0f
+        iconAppChipView.setSplitTranslationY(0f)
+        iconAppChipView.rotation = degreesRotated
+    }
+
+    /**
+     * @param inSplitSelection Whether user currently has a task from this task group staged for
+     *   split screen. If true, we have custom translations in place for the remaining icon, so
+     *   we'll skip setting translations here.
+     */
+    override fun setSplitIconParams(
+        primaryIconView: View,
+        secondaryIconView: View,
+        taskIconHeight: Int,
+        primarySnapshotWidth: Int,
+        primarySnapshotHeight: Int,
+        groupedTaskViewHeight: Int,
+        groupedTaskViewWidth: Int,
+        isRtl: Boolean,
+        deviceProfile: DeviceProfile,
+        splitConfig: SplitConfigurationOptions.SplitBounds,
+        inSplitSelection: Boolean,
+        oneIconHiddenDueToSmallWidth: Boolean,
+    ) {
+        val primaryIconParams = primaryIconView.layoutParams as FrameLayout.LayoutParams
+        val secondaryIconParams =
+            if (enableOverviewIconMenu()) secondaryIconView.layoutParams as FrameLayout.LayoutParams
+            else FrameLayout.LayoutParams(primaryIconParams)
+
+        if (enableOverviewIconMenu()) {
+            val primaryAppChipView = primaryIconView as IconAppChipView
+            val secondaryAppChipView = secondaryIconView as IconAppChipView
+            primaryIconParams.gravity = Gravity.TOP or Gravity.START
+            secondaryIconParams.gravity = Gravity.TOP or Gravity.START
+            secondaryIconParams.topMargin = primaryIconParams.topMargin
+            secondaryIconParams.marginStart = primaryIconParams.marginStart
+            if (!inSplitSelection) {
+                if (deviceProfile.isLeftRightSplit) {
+                    if (isRtl) {
+                        val secondarySnapshotWidth = groupedTaskViewWidth - primarySnapshotWidth
+                        primaryAppChipView.setSplitTranslationX(-secondarySnapshotWidth.toFloat())
+                    } else {
+                        val dividerSize =
+                            Math.round(groupedTaskViewWidth * splitConfig.dividerPercent)
+                        secondaryAppChipView.setSplitTranslationX(
+                            primarySnapshotWidth.toFloat() + dividerSize
+                        )
+                    }
+                } else {
+                    primaryAppChipView.setSplitTranslationX(0f)
+                    secondaryAppChipView.setSplitTranslationX(0f)
+                    val dividerThickness =
+                        min(
+                                splitConfig.visualDividerBounds.width().toDouble(),
+                                splitConfig.visualDividerBounds.height().toDouble(),
+                            )
+                            .toInt()
+                    secondaryAppChipView.setSplitTranslationY(
+                        (primarySnapshotHeight +
+                                (if (deviceProfile.isTablet) 0 else dividerThickness))
+                            .toFloat()
+                    )
+                }
+            }
+        } else if (deviceProfile.isLeftRightSplit) {
+            // We calculate the "midpoint" of the thumbnail area, and place the icons there.
+            // This is the place where the thumbnail area splits by default, in a near-50/50 split.
+            // It is usually not exactly 50/50, due to insets/screen cutouts.
+            val fullscreenInsetThickness =
+                if (deviceProfile.isSeascape) deviceProfile.insets.right
+                else deviceProfile.insets.left
+            val fullscreenMidpointFromBottom =
+                ((deviceProfile.widthPx - fullscreenInsetThickness) / 2)
+            val midpointFromEndPct = fullscreenMidpointFromBottom.toFloat() / deviceProfile.widthPx
+            val insetPct = fullscreenInsetThickness.toFloat() / deviceProfile.widthPx
+            val spaceAboveSnapshots = 0
+            val overviewThumbnailAreaThickness = groupedTaskViewWidth - spaceAboveSnapshots
+            val bottomToMidpointOffset =
+                (overviewThumbnailAreaThickness * midpointFromEndPct).toInt()
+            val insetOffset = (overviewThumbnailAreaThickness * insetPct).toInt()
+
+            if (deviceProfile.isSeascape) {
+                primaryIconParams.gravity =
+                    Gravity.TOP or (if (isRtl) Gravity.END else Gravity.START)
+                secondaryIconParams.gravity =
+                    Gravity.TOP or (if (isRtl) Gravity.END else Gravity.START)
+                if (!inSplitSelection) {
+                    if (splitConfig.initiatedFromSeascape) {
+                        if (oneIconHiddenDueToSmallWidth) {
+                            // Center both icons
+                            val centerX = bottomToMidpointOffset - (taskIconHeight / 2f)
+                            primaryIconView.translationX = centerX
+                            secondaryIconView.translationX = centerX
+                        } else {
+                            // the task on the right (secondary) is slightly larger
+                            primaryIconView.translationX =
+                                (bottomToMidpointOffset - taskIconHeight).toFloat()
+                            secondaryIconView.translationX = bottomToMidpointOffset.toFloat()
+                        }
+                    } else {
+                        if (oneIconHiddenDueToSmallWidth) {
+                            // Center both icons
+                            val centerX =
+                                bottomToMidpointOffset + insetOffset - (taskIconHeight / 2f)
+                            primaryIconView.translationX = centerX
+                            secondaryIconView.translationX = centerX
+                        } else {
+                            // the task on the left (primary) is slightly larger
+                            primaryIconView.translationX =
+                                (bottomToMidpointOffset + insetOffset - taskIconHeight).toFloat()
+                            secondaryIconView.translationX =
+                                (bottomToMidpointOffset + insetOffset).toFloat()
+                        }
+                    }
+                }
+            } else {
+                primaryIconParams.gravity =
+                    Gravity.TOP or (if (isRtl) Gravity.START else Gravity.END)
+                secondaryIconParams.gravity =
+                    Gravity.TOP or (if (isRtl) Gravity.START else Gravity.END)
+                if (!inSplitSelection) {
+                    if (!splitConfig.initiatedFromSeascape) {
+                        if (oneIconHiddenDueToSmallWidth) {
+                            // Center both icons
+                            val centerX = -bottomToMidpointOffset + (taskIconHeight / 2f)
+                            primaryIconView.translationX = centerX
+                            secondaryIconView.translationX = centerX
+                        } else {
+                            // the task on the left (primary) is slightly larger
+                            primaryIconView.translationX = -bottomToMidpointOffset.toFloat()
+                            secondaryIconView.translationX =
+                                (-bottomToMidpointOffset + taskIconHeight).toFloat()
+                        }
+                    } else {
+                        if (oneIconHiddenDueToSmallWidth) {
+                            // Center both icons
+                            val centerX =
+                                -bottomToMidpointOffset - insetOffset + (taskIconHeight / 2f)
+                            primaryIconView.translationX = centerX
+                            secondaryIconView.translationX = centerX
+                        } else {
+                            // the task on the right (secondary) is slightly larger
+                            primaryIconView.translationX =
+                                (-bottomToMidpointOffset - insetOffset).toFloat()
+                            secondaryIconView.translationX =
+                                (-bottomToMidpointOffset - insetOffset + taskIconHeight).toFloat()
+                        }
+                    }
+                }
+            }
+        } else {
+            primaryIconParams.gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
+            secondaryIconParams.gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
+            if (!inSplitSelection) {
+                if (oneIconHiddenDueToSmallWidth) {
+                    // Center both icons
+                    primaryIconView.translationX = 0f
+                    secondaryIconView.translationX = 0f
+                } else {
+                    // shifts icon half a width left (height is used here since icons are square)
+                    primaryIconView.translationX = -(taskIconHeight / 2f)
+                    secondaryIconView.translationX = taskIconHeight / 2f
+                }
+            }
+        }
+        if (!enableOverviewIconMenu() && !inSplitSelection) {
+            primaryIconView.translationY = 0f
+            secondaryIconView.translationY = 0f
+        }
+
+        primaryIconView.layoutParams = primaryIconParams
+        secondaryIconView.layoutParams = secondaryIconParams
+    }
+
+    override fun getDefaultSplitPosition(deviceProfile: DeviceProfile): Int {
+        check(deviceProfile.isTablet) { "Default position available only for large screens" }
+        return if (deviceProfile.isLeftRightSplit) {
+            SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
+        } else {
+            SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT
+        }
+    }
+
+    override fun <T> getSplitSelectTaskOffset(
+        primary: FloatProperty<T>,
+        secondary: FloatProperty<T>,
+        deviceProfile: DeviceProfile,
+    ): Pair<FloatProperty<T>, FloatProperty<T>> =
+        if (deviceProfile.isLeftRightSplit) { // or seascape
+            Pair(primary, secondary)
+        } else {
+            Pair(secondary, primary)
+        }
+
+    override fun getFloatingTaskOffscreenTranslationTarget(
+        floatingTask: View,
+        onScreenRect: RectF,
+        @StagePosition stagePosition: Int,
+        dp: DeviceProfile,
+    ): Float {
+        if (dp.isLeftRightSplit) {
+            val currentTranslationX = floatingTask.translationX
+            return if (stagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT)
+                currentTranslationX - onScreenRect.width()
+            else currentTranslationX + onScreenRect.width()
+        } else {
+            val currentTranslationY = floatingTask.translationY
+            return currentTranslationY - onScreenRect.height()
+        }
+    }
+
+    override fun setFloatingTaskPrimaryTranslation(
+        floatingTask: View,
+        translation: Float,
+        dp: DeviceProfile,
+    ) {
+        if (dp.isLeftRightSplit) {
+            floatingTask.translationX = translation
+        } else {
+            floatingTask.translationY = translation
+        }
+    }
+
+    override fun getFloatingTaskPrimaryTranslation(floatingTask: View, dp: DeviceProfile): Float =
+        if (dp.isLeftRightSplit) floatingTask.translationX else floatingTask.translationY
+
+    override fun getHandlerTypeForLogging(): LauncherAtom.TaskSwitcherContainer.OrientationHandler =
+        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 9b3c467..a7bc93b 100644
--- a/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
@@ -82,13 +82,13 @@
 
     fun getSplitTranslationDirectionFactor(
         @StagePosition stagePosition: Int,
-        deviceProfile: DeviceProfile
+        deviceProfile: DeviceProfile,
     ): Int
 
     fun <T> getSplitSelectTaskOffset(
         primary: FloatProperty<T>,
         secondary: FloatProperty<T>,
-        deviceProfile: DeviceProfile
+        deviceProfile: DeviceProfile,
     ): Pair<FloatProperty<T>, FloatProperty<T>>
 
     fun getDistanceToBottomOfRect(dp: DeviceProfile, rect: Rect): Int
@@ -101,7 +101,7 @@
         placeholderInset: Int,
         dp: DeviceProfile,
         @StagePosition stagePosition: Int,
-        out: Rect
+        out: Rect,
     )
 
     /**
@@ -128,7 +128,7 @@
         drawableWidth: Int,
         drawableHeight: Int,
         dp: DeviceProfile,
-        @StagePosition stagePosition: Int
+        @StagePosition stagePosition: Int,
     )
 
     /**
@@ -143,7 +143,7 @@
         out: View,
         dp: DeviceProfile,
         splitInstructionsHeight: Int,
-        splitInstructionsWidth: Int
+        splitInstructionsWidth: Int,
     )
 
     /**
@@ -159,7 +159,7 @@
         dp: DeviceProfile,
         @StagePosition stagePosition: Int,
         out1: Rect,
-        out2: Rect
+        out2: Rect,
     )
 
     fun getDefaultSplitPosition(deviceProfile: DeviceProfile): Int
@@ -174,7 +174,7 @@
         dp: DeviceProfile,
         outRect: Rect,
         splitInfo: SplitConfigurationOptions.SplitBounds,
-        @StagePosition desiredStagePosition: Int
+        @StagePosition desiredStagePosition: Int,
     )
 
     fun measureGroupedTaskViewThumbnailBounds(
@@ -185,7 +185,7 @@
         splitBoundsConfig: SplitConfigurationOptions.SplitBounds,
         dp: DeviceProfile,
         isRtl: Boolean,
-        inSplitSelection: Boolean
+        inSplitSelection: Boolean,
     )
 
     /**
@@ -198,7 +198,7 @@
         dp: DeviceProfile,
         splitBoundsConfig: SplitConfigurationOptions.SplitBounds,
         parentWidth: Int,
-        parentHeight: Int
+        parentHeight: Int,
     ): Pair<Point, Point>
 
     // Overview TaskMenuView methods
@@ -208,7 +208,7 @@
         taskIconMargin: Int,
         taskIconHeight: Int,
         thumbnailTopMargin: Int,
-        isRtl: Boolean
+        isRtl: Boolean,
     )
 
     /**
@@ -216,14 +216,14 @@
      */
     fun setIconAppChipChildrenParams(
         iconParams: FrameLayout.LayoutParams,
-        chipChildMarginStart: Int
+        chipChildMarginStart: Int,
     )
 
     fun setIconAppChipMenuParams(
         iconAppChipView: IconAppChipView,
         iconMenuParams: FrameLayout.LayoutParams,
         iconMenuMargin: Int,
-        thumbnailTopMargin: Int
+        thumbnailTopMargin: Int,
     )
 
     fun setSplitIconParams(
@@ -252,7 +252,7 @@
         thumbnailView: View,
         deviceProfile: DeviceProfile,
         taskInsetMargin: Float,
-        taskViewIcon: View
+        taskViewIcon: View,
     ): Float
 
     fun getTaskMenuY(
@@ -261,20 +261,20 @@
         stagePosition: Int,
         taskMenuView: View,
         taskInsetMargin: Float,
-        taskViewIcon: View
+        taskViewIcon: View,
     ): Float
 
     fun getTaskMenuWidth(
         thumbnailView: View,
         deviceProfile: DeviceProfile,
-        @StagePosition stagePosition: Int
+        @StagePosition stagePosition: Int,
     ): Int
 
     fun getTaskMenuHeight(
         taskInsetMargin: Float,
         deviceProfile: DeviceProfile,
         taskMenuX: Float,
-        taskMenuY: Float
+        taskMenuY: Float,
     ): Int
 
     /**
@@ -285,7 +285,7 @@
         deviceProfile: DeviceProfile,
         taskMenuLayout: LinearLayout,
         dividerSpacing: Int,
-        dividerDrawable: ShapeDrawable
+        dividerDrawable: ShapeDrawable,
     )
 
     /**
@@ -295,7 +295,7 @@
     fun setLayoutParamsForTaskMenuOptionItem(
         lp: LinearLayout.LayoutParams,
         viewGroup: LinearLayout,
-        deviceProfile: DeviceProfile
+        deviceProfile: DeviceProfile,
     )
 
     /** Layout a Digital Wellbeing Banner on its parent. TaskView. */
@@ -306,7 +306,7 @@
         deviceProfile: DeviceProfile,
         snapshotViewWidth: Int,
         snapshotViewHeight: Int,
-        banner: View
+        banner: View,
     )
 
     /**
@@ -322,7 +322,7 @@
         deviceProfile: DeviceProfile,
         thumbnailViews: Array<View>,
         desiredTaskId: Int,
-        banner: View
+        banner: View,
     ): Pair<Float, Float>
 
     // The following are only used by TaskViewTouchHandler.
@@ -342,6 +342,15 @@
     /** @return Either 1 or -1, a factor to multiply by so the animation goes the correct way. */
     fun getTaskDragDisplacementFactor(isRtl: Boolean): Int
 
+    /** @return Either 1 or -1, the direction sign towards task dismiss. */
+    fun getTaskDismissVerticalDirection(): Int
+
+    /** @return the length to drag a task off screen for dismiss. */
+    fun getTaskDismissLength(secondaryDimension: Int, taskThumbnailBounds: Rect): Int
+
+    /** @return the length to drag a task to full screen for launch. */
+    fun getTaskLaunchLength(secondaryDimension: Int, taskThumbnailBounds: Rect): Int
+
     /**
      * Maps the velocity from the coordinate plane of the foreground app to that of Launcher's
      * (which now will always be portrait)
@@ -371,7 +380,7 @@
         floatingTask: View,
         onScreenRect: RectF,
         @StagePosition stagePosition: Int,
-        dp: DeviceProfile
+        dp: DeviceProfile,
     ): Float
 
     /**
diff --git a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
index 0cb983d..67358bbb 100644
--- a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
@@ -26,6 +26,7 @@
 import android.view.View
 import android.view.View.MeasureSpec
 import android.widget.FrameLayout
+import android.widget.LinearLayout
 import androidx.core.util.component1
 import androidx.core.util.component2
 import androidx.core.view.updateLayoutParams
@@ -53,7 +54,7 @@
 
     override fun getSplitTranslationDirectionFactor(
         stagePosition: Int,
-        deviceProfile: DeviceProfile
+        deviceProfile: DeviceProfile,
     ): Int = if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) -1 else 1
 
     override fun getRecentsRtlSetting(resources: Resources): Boolean = Utilities.isRtl(resources)
@@ -70,7 +71,7 @@
         thumbnailView: View,
         deviceProfile: DeviceProfile,
         taskInsetMargin: Float,
-        taskViewIcon: View
+        taskViewIcon: View,
     ): Float = x + taskInsetMargin
 
     override fun getTaskMenuY(
@@ -79,7 +80,7 @@
         stagePosition: Int,
         taskMenuView: View,
         taskInsetMargin: Float,
-        taskViewIcon: View
+        taskViewIcon: View,
     ): Float {
         if (Flags.enableOverviewIconMenu()) {
             return y
@@ -97,14 +98,14 @@
         taskInsetMargin: Float,
         deviceProfile: DeviceProfile,
         taskMenuX: Float,
-        taskMenuY: Float
+        taskMenuY: Float,
     ): Int = (deviceProfile.availableWidthPx - taskInsetMargin - taskMenuX).toInt()
 
     override fun setSplitTaskSwipeRect(
         dp: DeviceProfile,
         outRect: Rect,
         splitInfo: SplitBounds,
-        desiredStagePosition: Int
+        desiredStagePosition: Int,
     ) {
         val topLeftTaskPercent = splitInfo.leftTopTaskPercent
         val dividerBarPercent = splitInfo.dividerPercent
@@ -126,7 +127,7 @@
         deviceProfile: DeviceProfile,
         snapshotViewWidth: Int,
         snapshotViewHeight: Int,
-        banner: View
+        banner: View,
     ) {
         banner.pivotX = 0f
         banner.pivotY = 0f
@@ -149,9 +150,9 @@
         deviceProfile: DeviceProfile,
         thumbnailViews: Array<View>,
         desiredTaskId: Int,
-        banner: View
+        banner: View,
     ): Pair<Float, Float> {
-        val snapshotParams = thumbnailViews[0].layoutParams as FrameLayout.LayoutParams
+        val snapshotParams = thumbnailViews[0].layoutParams as LinearLayout.LayoutParams
         val translationX: Float = (taskViewWidth - banner.height).toFloat()
         val translationY: Float
         if (splitBounds == null) {
@@ -181,7 +182,7 @@
                 R.drawable.ic_split_horizontal,
                 R.string.recent_task_option_split_screen,
                 STAGE_POSITION_BOTTOM_OR_RIGHT,
-                STAGE_TYPE_MAIN
+                STAGE_TYPE_MAIN,
             )
         )
 
@@ -189,7 +190,7 @@
         out: View,
         dp: DeviceProfile,
         splitInstructionsHeight: Int,
-        splitInstructionsWidth: Int
+        splitInstructionsWidth: Int,
     ) {
         out.pivotX = 0f
         out.pivotY = splitInstructionsHeight.toFloat()
@@ -217,7 +218,7 @@
         taskIconMargin: Int,
         taskIconHeight: Int,
         thumbnailTopMargin: Int,
-        isRtl: Boolean
+        isRtl: Boolean,
     ) {
         iconParams.gravity =
             if (isRtl) {
@@ -230,7 +231,7 @@
 
     override fun setIconAppChipChildrenParams(
         iconParams: FrameLayout.LayoutParams,
-        chipChildMarginStart: Int
+        chipChildMarginStart: Int,
     ) {
         iconParams.setMargins(0, 0, 0, 0)
         iconParams.marginStart = chipChildMarginStart
@@ -241,7 +242,7 @@
         iconAppChipView: IconAppChipView,
         iconMenuParams: FrameLayout.LayoutParams,
         iconMenuMargin: Int,
-        thumbnailTopMargin: Int
+        thumbnailTopMargin: Int,
     ) {
         val isRtl = iconAppChipView.layoutDirection == View.LAYOUT_DIRECTION_RTL
         val iconCenter = iconAppChipView.getHeight() / 2f
@@ -268,7 +269,7 @@
 
     /**
      * @param inSplitSelection Whether user currently has a task from this task group staged for
-     * split screen. Currently this state is not reachable in fake seascape.
+     *   split screen. Currently this state is not reachable in fake seascape.
      */
     override fun measureGroupedTaskViewThumbnailBounds(
         primarySnapshot: View,
@@ -278,7 +279,7 @@
         splitBoundsConfig: SplitBounds,
         dp: DeviceProfile,
         isRtl: Boolean,
-        inSplitSelection: Boolean
+        inSplitSelection: Boolean,
     ) {
         val primaryParams = primarySnapshot.layoutParams as FrameLayout.LayoutParams
         val secondaryParams = secondarySnapshot.layoutParams as FrameLayout.LayoutParams
@@ -300,11 +301,11 @@
             (taskViewSecond.y + spaceAboveSnapshot + dividerBar).toFloat()
         primarySnapshot.measure(
             MeasureSpec.makeMeasureSpec(taskViewFirst.x, MeasureSpec.EXACTLY),
-            MeasureSpec.makeMeasureSpec(taskViewFirst.y, MeasureSpec.EXACTLY)
+            MeasureSpec.makeMeasureSpec(taskViewFirst.y, MeasureSpec.EXACTLY),
         )
         secondarySnapshot.measure(
             MeasureSpec.makeMeasureSpec(taskViewSecond.x, MeasureSpec.EXACTLY),
-            MeasureSpec.makeMeasureSpec(taskViewSecond.y, MeasureSpec.EXACTLY)
+            MeasureSpec.makeMeasureSpec(taskViewSecond.y, MeasureSpec.EXACTLY),
         )
     }
 
@@ -312,7 +313,7 @@
         dp: DeviceProfile,
         splitBoundsConfig: SplitBounds,
         parentWidth: Int,
-        parentHeight: Int
+        parentHeight: Int,
     ): Pair<Point, Point> {
         // Measure and layout the thumbnails bottom up, since the primary is on the visual left
         // (portrait bottom) and secondary is on the right (portrait top)
@@ -344,6 +345,14 @@
 
     override fun getTaskDragDisplacementFactor(isRtl: Boolean): Int = if (isRtl) -1 else 1
 
+    override fun getTaskDismissVerticalDirection(): Int = -1
+
+    override fun getTaskDismissLength(secondaryDimension: Int, taskThumbnailBounds: Rect): Int =
+        taskThumbnailBounds.right
+
+    override fun getTaskLaunchLength(secondaryDimension: Int, taskThumbnailBounds: Rect): Int =
+        secondaryDimension - taskThumbnailBounds.right
+
     /* -------------------- */
 
     override fun getSplitIconsPosition(
@@ -376,10 +385,7 @@
             if (oneIconHiddenDueToSmallWidth) {
                 // Center both icons
                 val centerY = primarySnapshotHeight + ((dividerSize - taskIconHeight) / 2)
-                SplitIconPositions(
-                    topLeftY = centerY,
-                    bottomRightY = centerY,
-                )
+                SplitIconPositions(topLeftY = centerY, bottomRightY = centerY)
             } else {
                 SplitIconPositions(
                     topLeftY = primarySnapshotHeight - taskIconHeight,
diff --git a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
index c6b3d6a..553a620 100644
--- a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
+++ b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
@@ -18,7 +18,6 @@
 
 import android.content.Context
 import android.util.Log
-import android.view.View
 import com.android.launcher3.util.coroutines.DispatcherProvider
 import com.android.launcher3.util.coroutines.ProductionDispatchers
 import com.android.quickstep.RecentsModel
@@ -32,8 +31,6 @@
 import com.android.quickstep.recents.domain.usecase.IsThumbnailValidUseCase
 import com.android.quickstep.recents.domain.usecase.OrganizeDesktopTasksUseCase
 import com.android.quickstep.recents.viewmodel.RecentsViewData
-import com.android.quickstep.task.viewmodel.TaskOverlayViewModel
-import com.android.systemui.shared.recents.model.Task
 import com.android.systemui.shared.recents.utilities.PreviewPositionHelper
 import kotlinx.coroutines.CoroutineName
 import kotlinx.coroutines.CoroutineScope
@@ -49,10 +46,12 @@
     }
 
     /**
-     * This function initialised the default scope with RecentsView dependencies. These dependencies
-     * are used multiple times and should be a singleton to share across Recents classes.
+     * This function initialises the default scope with RecentsView dependencies. Some dependencies
+     * are global while others are per-RecentsView. The scope is used to differentiate between
+     * RecentsViews.
      */
     private fun startDefaultScope(appContext: Context) {
+        Log.d(TAG, "startDefaultScope")
         createScope(DEFAULT_SCOPE_ID).apply {
             set(RecentsViewData::class.java.simpleName, RecentsViewData())
             val dispatcherProvider: DispatcherProvider = ProductionDispatchers
@@ -86,6 +85,32 @@
         }
     }
 
+    /**
+     * This function initialises a scope associated with the dependencies of a single RecentsView.
+     *
+     * @param viewContext the Context associated with a RecentsView.
+     * @return the scope id associated with the new RecentsDependenciesScope.
+     */
+    fun createRecentsViewScope(viewContext: Context): String {
+        val scopeId = viewContext.hashCode().toString()
+        Log.d(TAG, "createRecentsViewScope $scopeId")
+        val scope =
+            createScope(scopeId).apply {
+                set(RecentsViewData::class.java.simpleName, RecentsViewData())
+                val dispatcherProvider: DispatcherProvider =
+                    get<DispatcherProvider>(DEFAULT_SCOPE_ID)
+                val recentsCoroutineScope =
+                    CoroutineScope(
+                        SupervisorJob() +
+                            dispatcherProvider.unconfined +
+                            CoroutineName("RecentsView$scopeId")
+                    )
+                set(CoroutineScope::class.java.simpleName, recentsCoroutineScope)
+            }
+        scope.linkTo(getScope(DEFAULT_SCOPE_ID))
+        return scopeId
+    }
+
     inline fun <reified T> inject(
         scopeId: RecentsScopeId = "",
         extras: RecentsDependenciesExtras = RecentsDependenciesExtras(),
@@ -170,25 +195,14 @@
         log("linked scopes: ${getScope(scopeId).scopeIdsLinked}")
         val instance: Any =
             when (modelClass) {
-                RecentsViewData::class.java -> RecentsViewData()
-                TaskOverlayViewModel::class.java -> {
-                    val task = extras["Task"] as Task
-                    TaskOverlayViewModel(
-                        task = task,
-                        recentsViewData = inject(),
-                        recentTasksRepository = inject(),
-                        getThumbnailPositionUseCase = inject(),
-                        dispatcherProvider = inject(),
-                    )
-                }
                 IsThumbnailValidUseCase::class.java ->
-                    IsThumbnailValidUseCase(rotationStateRepository = inject())
-                GetTaskUseCase::class.java -> GetTaskUseCase(repository = inject())
+                    IsThumbnailValidUseCase(rotationStateRepository = inject(scopeId))
+                GetTaskUseCase::class.java -> GetTaskUseCase(repository = inject(scopeId))
                 GetSysUiStatusNavFlagsUseCase::class.java -> GetSysUiStatusNavFlagsUseCase()
                 GetThumbnailPositionUseCase::class.java ->
                     GetThumbnailPositionUseCase(
-                        deviceProfileRepository = inject(),
-                        rotationStateRepository = inject(),
+                        deviceProfileRepository = inject(scopeId),
+                        rotationStateRepository = inject(scopeId),
                         previewPositionHelper = PreviewPositionHelper(),
                     )
                 OrganizeDesktopTasksUseCase::class.java -> OrganizeDesktopTasksUseCase()
@@ -222,58 +236,58 @@
     }
 
     companion object {
-        private const val DEFAULT_SCOPE_ID = "RecentsDependencies::GlobalScope"
+        const val DEFAULT_SCOPE_ID = "RecentsDependencies::GlobalScope"
         private const val TAG = "RecentsDependencies"
         private const val DEBUG = false
-        private var activeRecentsCount = 0
 
-        @Volatile private lateinit var instance: RecentsDependencies
+        @Volatile private var instance: RecentsDependencies? = null
 
-        fun initialize(view: View): RecentsDependencies = initialize(view.context)
-
-        fun initialize(context: Context): RecentsDependencies {
+        private fun initialize(context: Context): RecentsDependencies {
             Log.d(TAG, "initializing")
             synchronized(this) {
-                activeRecentsCount++
-                instance = RecentsDependencies(context.applicationContext)
+                val newInstance = RecentsDependencies(context.applicationContext)
+                instance = newInstance
+                return newInstance
             }
-            return instance
+        }
+
+        fun maybeInitialize(context: Context): RecentsDependencies {
+            return instance ?: initialize(context)
         }
 
         fun getInstance(): RecentsDependencies {
-            if (!Companion::instance.isInitialized) {
-                throw UninitializedPropertyAccessException(
-                    "Recents dependencies are not initialized. " +
-                        "Call `RecentsDependencies.initialize` before using this container."
-                )
-            }
             return instance
+                ?: throw UninitializedPropertyAccessException(
+                    "Recents dependencies are not initialized. " +
+                        "Call `RecentsDependencies.maybeInitialize` before using this container."
+                )
         }
 
         @JvmStatic
-        fun destroy() {
-            // When Launcher Activity restarts, the old view's RecentsView.onDetachedFromWindow
-            // happens after the new view's creation. This means that destroy can be called after a
-            // new initialisation. This check prevents a newly initialised tree from being
-            // destroyed. Ideally we would have 1 instance of the dependency tree for each
-            // RecentsView.
-            //
-            // This check is sufficient to avoid a leak of the dependency tree after the Activity is
-            // destroyed while also allowing Launcher auto-restarts (production behaviour) to easily
-            // reinitialise the dependency tree.
-            //
-            // TODO(b/353917593): Better lifecycle decisions will be implemented in this bug or when
-            //  replacing with Dagger (b/371370483).
-            activeRecentsCount--
-            if (activeRecentsCount == 0) {
-                instance.scopes.clear()
-                Log.d(TAG, "destroyed", Exception("Printing stack trace"))
-            } else {
-                Log.d(
-                    TAG,
-                    "RecentsDependencies was not destroyed. " +
-                        "There is still an active RecentsView instance.",
-                )
+        fun destroy(viewContext: Context) {
+            synchronized(this) {
+                val localInstance = instance ?: return
+                val scopeId = viewContext.hashCode().toString()
+                val scope = localInstance.scopes[scopeId]
+                if (scope == null) {
+                    Log.e(
+                        TAG,
+                        "Trying to destroy an unknown scope. Scopes: ${localInstance.scopes.size}",
+                    )
+                    return
+                }
+                scope.close()
+                localInstance.scopes.remove(scopeId)
+                if (DEBUG) {
+                    Log.d(TAG, "destroyed $scopeId", Exception("Printing stack trace"))
+                } else {
+                    Log.d(TAG, "destroyed $scopeId")
+                }
+                if (localInstance.scopes.size == 1) {
+                    // Only the default scope left - destroy this too.
+                    instance = null
+                    Log.d(TAG, "also destroyed default scope")
+                }
             }
         }
     }
diff --git a/quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt b/quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt
index 619075f..aa1c236 100644
--- a/quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt
+++ b/quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt
@@ -18,17 +18,49 @@
 
 import android.view.View.OnClickListener
 import com.android.quickstep.recents.ui.viewmodel.TaskData
+import com.android.quickstep.task.thumbnail.TaskHeaderUiState
 import com.android.quickstep.task.thumbnail.TaskThumbnailUiState
 import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
 import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
 import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
 import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.SnapshotSplash
-import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.ThumbnailHeader
 import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
 
 object TaskUiStateMapper {
 
     /**
+     * Converts a [TaskData] object into a [TaskHeaderUiState] for display in the UI.
+     *
+     * This function handles different types of [TaskData] and determines the appropriate UI state
+     * based on the data and provided flags.
+     *
+     * @param taskData The [TaskData] to convert. Can be null or a specific subclass.
+     * @param hasHeader A flag indicating whether the UI should display a header.
+     * @param clickCloseListener A callback when the close button in the UI is clicked.
+     * @return A [TaskHeaderUiState] representing the UI state for the given task data.
+     */
+    fun toTaskHeaderState(
+        taskData: TaskData?,
+        hasHeader: Boolean,
+        clickCloseListener: OnClickListener?,
+    ): TaskHeaderUiState =
+        when {
+            taskData !is TaskData.Data -> TaskHeaderUiState.HideHeader
+            canHeaderBeCreated(taskData, hasHeader, clickCloseListener) -> {
+                TaskHeaderUiState.ShowHeader(
+                    TaskHeaderUiState.ThumbnailHeader(
+                        // TODO(http://b/353965691): figure out what to do when `icon` or
+                        // `titleDescription` is null.
+                        taskData.icon!!,
+                        taskData.titleDescription!!,
+                        clickCloseListener!!,
+                    )
+                )
+            }
+            else -> TaskHeaderUiState.HideHeader
+        }
+
+    /**
      * Converts a [TaskData] object into a [TaskThumbnailUiState] for display in the UI.
      *
      * This function handles different types of [TaskData] and determines the appropriate UI state
@@ -36,46 +68,24 @@
      *
      * @param taskData The [TaskData] to convert. Can be null or a specific subclass.
      * @param isLiveTile A flag indicating whether the task data represents live tile.
-     * @param hasHeader A flag indicating whether the UI should display a header.
-     * @param clickCloseListener A callback when the close button in the UI is clicked.
      * @return A [TaskThumbnailUiState] representing the UI state for the given task data.
      */
-    fun toTaskThumbnailUiState(
-        taskData: TaskData?,
-        isLiveTile: Boolean,
-        hasHeader: Boolean,
-        clickCloseListener: OnClickListener?,
-    ): TaskThumbnailUiState =
+    fun toTaskThumbnailUiState(taskData: TaskData?, isLiveTile: Boolean): TaskThumbnailUiState =
         when {
             taskData !is TaskData.Data -> Uninitialized
-            isLiveTile -> createLiveTileState(taskData, hasHeader, clickCloseListener)
+            isLiveTile -> LiveTile
             isBackgroundOnly(taskData) -> BackgroundOnly(taskData.backgroundColor)
             isSnapshotSplash(taskData) ->
                 SnapshotSplash(
-                    createSnapshotState(taskData, hasHeader, clickCloseListener),
+                    Snapshot(
+                        taskData.thumbnailData?.thumbnail!!,
+                        taskData.thumbnailData.rotation,
+                        taskData.backgroundColor,
+                    ),
                     taskData.icon,
                 )
-            else -> Uninitialized
-        }
 
-    private fun createSnapshotState(
-        taskData: TaskData.Data,
-        hasHeader: Boolean,
-        clickCloseListener: OnClickListener?,
-    ): Snapshot =
-        if (canHeaderBeCreated(taskData, hasHeader, clickCloseListener)) {
-            Snapshot.WithHeader(
-                taskData.thumbnailData?.thumbnail!!,
-                taskData.thumbnailData.rotation,
-                taskData.backgroundColor,
-                ThumbnailHeader(taskData.icon!!, taskData.titleDescription!!, clickCloseListener!!),
-            )
-        } else {
-            Snapshot.WithoutHeader(
-                taskData.thumbnailData?.thumbnail!!,
-                taskData.thumbnailData.rotation,
-                taskData.backgroundColor,
-            )
+            else -> Uninitialized
         }
 
     private fun isBackgroundOnly(taskData: TaskData.Data) =
@@ -93,17 +103,4 @@
             taskData.icon != null &&
             taskData.titleDescription != null &&
             clickCloseListener != null
-
-    private fun createLiveTileState(
-        taskData: TaskData.Data,
-        hasHeader: Boolean,
-        clickCloseListener: OnClickListener?,
-    ) =
-        if (canHeaderBeCreated(taskData, hasHeader, clickCloseListener)) {
-            // TODO(http://b/353965691): figure out what to do when `icon` or `titleDescription` is
-            //  null.
-            LiveTile.WithHeader(
-                ThumbnailHeader(taskData.icon!!, taskData.titleDescription!!, clickCloseListener!!)
-            )
-        } else LiveTile.WithoutHeader
 }
diff --git a/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskTileUiState.kt b/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskTileUiState.kt
index 118a931..8a6a805 100644
--- a/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskTileUiState.kt
+++ b/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskTileUiState.kt
@@ -36,6 +36,7 @@
     val isLiveTile: Boolean,
     val hasHeader: Boolean,
     val sysUiStatusNavFlags: Int,
+    val taskOverlayEnabled: Boolean,
 )
 
 sealed class TaskData {
diff --git a/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModel.kt b/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModel.kt
index 3c4a384..09e2071 100644
--- a/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModel.kt
+++ b/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModel.kt
@@ -29,6 +29,7 @@
 import com.android.quickstep.recents.domain.usecase.ThumbnailPosition
 import com.android.quickstep.recents.viewmodel.RecentsViewData
 import com.android.quickstep.views.TaskViewType
+import com.android.quickstep.views.TaskViewType.SINGLE
 import com.android.systemui.shared.recents.model.ThumbnailData
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
@@ -74,8 +75,18 @@
             )
         }
 
+    private val overlayEnabled =
+        combine(recentsViewData.overlayEnabled, recentsViewData.settledFullyVisibleTaskIds) {
+                isOverlayEnabled,
+                settledFullyVisibleTaskIds ->
+                taskViewType == SINGLE &&
+                    isOverlayEnabled &&
+                    settledFullyVisibleTaskIds.any { it in taskIds.value }
+            }
+            .distinctUntilChanged()
+
     val state: Flow<TaskTileUiState> =
-        combine(taskData, isLiveTile) { tasks, isLiveTile -> mapToTaskTile(tasks, isLiveTile) }
+        combine(taskData, isLiveTile, overlayEnabled, ::mapToTaskTile)
             .distinctUntilChanged()
             .flowOn(dispatcherProvider.background)
 
@@ -99,13 +110,18 @@
             isRtl = isRtl,
         )
 
-    private fun mapToTaskTile(tasks: List<TaskData>, isLiveTile: Boolean): TaskTileUiState {
+    private fun mapToTaskTile(
+        tasks: List<TaskData>,
+        isLiveTile: Boolean,
+        overlayEnabled: Boolean,
+    ): TaskTileUiState {
         val firstThumbnailData = (tasks.firstOrNull() as? TaskData.Data)?.thumbnailData
         return TaskTileUiState(
             tasks = tasks,
             isLiveTile = isLiveTile,
             hasHeader = taskViewType == TaskViewType.DESKTOP,
             sysUiStatusNavFlags = getSysUiStatusNavFlagsUseCase(firstThumbnailData),
+            taskOverlayEnabled = overlayEnabled,
         )
     }
 
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskContentView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskContentView.kt
new file mode 100644
index 0000000..2dbd811
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskContentView.kt
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2025 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.thumbnail
+
+import android.content.Context
+import android.graphics.Outline
+import android.graphics.Path
+import android.graphics.Rect
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewOutlineProvider
+import android.widget.LinearLayout
+import androidx.core.view.isInvisible
+import com.android.launcher3.Flags.enableDesktopExplodedView
+import com.android.launcher3.Flags.enableRefactorTaskThumbnail
+import com.android.launcher3.R
+import com.android.launcher3.util.ViewPool
+import com.android.quickstep.views.TaskHeaderView
+import com.android.quickstep.views.TaskThumbnailViewDeprecated
+
+/**
+ * TaskContentView is a wrapper around the TaskHeaderView and TaskThumbnailView. It is a sibling to
+ * DWB, AiAi (TaskOverlay).
+ */
+class TaskContentView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
+    LinearLayout(context, attrs), ViewPool.Reusable {
+
+    private var taskHeaderView: TaskHeaderView? = null
+    private var taskThumbnailView: TaskThumbnailView? = null
+    private var taskThumbnailViewDeprecated: TaskThumbnailViewDeprecated? = null
+    private var onSizeChanged: ((width: Int, height: Int) -> Unit)? = null
+    private val outlinePath = Path()
+
+    /**
+     * Sets the outline bounds of the view. Default to use view's bound as outline when set to null.
+     */
+    var outlineBounds: Rect? = null
+        set(value) {
+            field = value
+            invalidateOutline()
+        }
+
+    private val bounds = Rect()
+
+    var cornerRadius: Float = 0f
+        set(value) {
+            field = value
+            invalidateOutline()
+        }
+
+    override fun onFinishInflate() {
+        super.onFinishInflate()
+        maybeCreateHeader()
+        createTaskThumbnailView()
+    }
+
+    override fun setScaleX(scaleX: Float) {
+        super.setScaleX(scaleX)
+        taskThumbnailView?.parentScaleXUpdated(scaleX)
+    }
+
+    override fun setScaleY(scaleY: Float) {
+        super.setScaleY(scaleY)
+        taskThumbnailView?.parentScaleYUpdated(scaleY)
+    }
+
+    override fun onAttachedToWindow() {
+        super.onAttachedToWindow()
+        clipToOutline = true
+        outlineProvider =
+            object : ViewOutlineProvider() {
+                override fun getOutline(view: View, outline: Outline) {
+                    val outlineRect = outlineBounds ?: bounds
+                    outlinePath.apply {
+                        rewind()
+                        addRoundRect(
+                            outlineRect.left.toFloat(),
+                            outlineRect.top.toFloat(),
+                            outlineRect.right.toFloat(),
+                            outlineRect.bottom.toFloat(),
+                            cornerRadius / scaleX,
+                            cornerRadius / scaleY,
+                            Path.Direction.CW,
+                        )
+                    }
+                    outline.setPath(outlinePath)
+                }
+            }
+    }
+
+    override fun onRecycle() {
+        taskHeaderView?.isInvisible = true
+        onSizeChanged = null
+        outlineBounds = null
+        taskThumbnailView?.onRecycle()
+        taskThumbnailViewDeprecated?.onRecycle()
+    }
+
+    fun doOnSizeChange(action: (width: Int, height: Int) -> Unit) {
+        onSizeChanged = action
+    }
+
+    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+        super.onSizeChanged(w, h, oldw, oldh)
+        onSizeChanged?.invoke(width, height)
+        bounds.set(0, 0, w, h)
+        invalidateOutline()
+    }
+
+    private fun maybeCreateHeader() {
+        if (enableDesktopExplodedView() && taskHeaderView == null) {
+            taskHeaderView =
+                LayoutInflater.from(context).inflate(R.layout.task_header_view, this, false)
+                    as TaskHeaderView
+            addView(taskHeaderView)
+        }
+    }
+
+    private fun createTaskThumbnailView() {
+        if (taskThumbnailView == null) {
+            if (enableRefactorTaskThumbnail()) {
+                taskThumbnailView =
+                    LayoutInflater.from(context).inflate(R.layout.task_thumbnail, this, false)
+                        as TaskThumbnailView
+                addView(taskThumbnailView)
+            } else {
+                taskThumbnailViewDeprecated =
+                    LayoutInflater.from(context)
+                        .inflate(R.layout.task_thumbnail_deprecated, this, false)
+                        as TaskThumbnailViewDeprecated
+                addView(taskThumbnailViewDeprecated)
+            }
+        }
+    }
+
+    fun setState(
+        taskHeaderState: TaskHeaderUiState,
+        taskThumbnailUiState: TaskThumbnailUiState,
+        taskId: Int?,
+    ) {
+        taskHeaderView?.setState(taskHeaderState)
+        taskThumbnailView?.setState(taskThumbnailUiState, taskId)
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskHeaderUiState.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskHeaderUiState.kt
new file mode 100644
index 0000000..09fb540
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskHeaderUiState.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2025 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.thumbnail
+
+import android.graphics.drawable.Drawable
+import android.view.View
+
+sealed class TaskHeaderUiState {
+    data class ShowHeader(val header: ThumbnailHeader) : TaskHeaderUiState()
+
+    data object HideHeader : TaskHeaderUiState()
+
+    data class ThumbnailHeader(
+        val icon: Drawable,
+        val title: String,
+        val clickCloseListener: View.OnClickListener,
+    )
+}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskOverlayUiState.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskOverlayUiState.kt
deleted file mode 100644
index 5fb5b90..0000000
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskOverlayUiState.kt
+++ /dev/null
@@ -1,26 +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.task.thumbnail
-
-import android.graphics.Bitmap
-
-/** Ui state for [com.android.quickstep.TaskOverlayFactory.TaskOverlay] */
-sealed class TaskOverlayUiState {
-    data object Disabled : TaskOverlayUiState()
-
-    data class Enabled(val isRealSnapshot: Boolean, val thumbnail: Bitmap?) : TaskOverlayUiState()
-}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
index db593d3..a5c9ac0 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
@@ -19,7 +19,6 @@
 import android.graphics.Bitmap
 import android.graphics.drawable.Drawable
 import android.view.Surface
-import android.view.View.OnClickListener
 import androidx.annotation.ColorInt
 
 sealed class TaskThumbnailUiState {
@@ -27,37 +26,14 @@
 
     data class BackgroundOnly(@ColorInt val backgroundColor: Int) : TaskThumbnailUiState()
 
+    data object LiveTile : TaskThumbnailUiState()
+
     data class SnapshotSplash(val snapshot: Snapshot, val splash: Drawable?) :
         TaskThumbnailUiState()
 
-    sealed class LiveTile : TaskThumbnailUiState() {
-        data class WithHeader(val header: ThumbnailHeader) : LiveTile()
-
-        data object WithoutHeader : LiveTile()
-    }
-
-    sealed class Snapshot {
-        abstract val bitmap: Bitmap
-        abstract val thumbnailRotation: Int
-        abstract val backgroundColor: Int
-
-        data class WithHeader(
-            override val bitmap: Bitmap,
-            @Surface.Rotation override val thumbnailRotation: Int,
-            @ColorInt override val backgroundColor: Int,
-            val header: ThumbnailHeader,
-        ) : Snapshot()
-
-        data class WithoutHeader(
-            override val bitmap: Bitmap,
-            @Surface.Rotation override val thumbnailRotation: Int,
-            @ColorInt override val backgroundColor: Int,
-        ) : Snapshot()
-    }
-
-    data class ThumbnailHeader(
-        val icon: Drawable,
-        val title: String,
-        val clickCloseListener: OnClickListener,
+    data class Snapshot(
+        val bitmap: Bitmap,
+        @Surface.Rotation val thumbnailRotation: Int,
+        @ColorInt val backgroundColor: Int,
     )
 }
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
index 0edbacc..78a16f1 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
@@ -19,32 +19,24 @@
 import android.content.Context
 import android.graphics.Color
 import android.graphics.Matrix
-import android.graphics.Outline
-import android.graphics.Path
-import android.graphics.Rect
 import android.graphics.drawable.ShapeDrawable
 import android.util.AttributeSet
 import android.util.Log
-import android.view.LayoutInflater
 import android.view.View
-import android.view.ViewOutlineProvider
 import android.widget.FrameLayout
 import androidx.annotation.ColorInt
 import androidx.core.view.isInvisible
-import com.android.launcher3.Flags.enableDesktopExplodedView
 import com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA
 import com.android.launcher3.R
 import com.android.launcher3.util.MultiPropertyFactory
-import com.android.launcher3.util.ViewPool
 import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
 import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
 import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
 import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.SnapshotSplash
 import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
 import com.android.quickstep.views.FixedSizeImageView
-import com.android.quickstep.views.TaskThumbnailViewHeader
 
-class TaskThumbnailView : FrameLayout, ViewPool.Reusable {
+class TaskThumbnailView : FrameLayout {
     private val scrimView: View by lazy { findViewById(R.id.task_thumbnail_scrim) }
     private val liveTileView: LiveTileView by lazy { findViewById(R.id.task_thumbnail_live_tile) }
     private val thumbnailView: FixedSizeImageView by lazy { findViewById(R.id.task_thumbnail) }
@@ -53,30 +45,9 @@
     private val dimAlpha: MultiPropertyFactory<View> by lazy {
         MultiPropertyFactory(scrimView, VIEW_ALPHA, ScrimViewAlpha.entries.size, ::maxOf)
     }
-    private val outlinePath = Path()
-    private var onSizeChanged: ((width: Int, height: Int) -> Unit)? = null
-
-    private var taskThumbnailViewHeader: TaskThumbnailViewHeader? = null
 
     private var uiState: TaskThumbnailUiState = Uninitialized
 
-    /**
-     * Sets the outline bounds of the view. Default to use view's bound as outline when set to null.
-     */
-    var outlineBounds: Rect? = null
-        set(value) {
-            field = value
-            invalidateOutline()
-        }
-
-    private val bounds = Rect()
-
-    var cornerRadius: Float = 0f
-        set(value) {
-            field = value
-            invalidateOutline()
-        }
-
     constructor(context: Context) : super(context)
 
     constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
@@ -87,39 +58,8 @@
         defStyleAttr: Int,
     ) : super(context, attrs, defStyleAttr)
 
-    override fun onFinishInflate() {
-        super.onFinishInflate()
-        maybeCreateHeader()
-    }
-
-    override fun onAttachedToWindow() {
-        super.onAttachedToWindow()
-        clipToOutline = true
-        outlineProvider =
-            object : ViewOutlineProvider() {
-                override fun getOutline(view: View, outline: Outline) {
-                    val outlineRect = outlineBounds ?: bounds
-                    outlinePath.apply {
-                        rewind()
-                        addRoundRect(
-                            outlineRect.left.toFloat(),
-                            outlineRect.top.toFloat(),
-                            outlineRect.right.toFloat(),
-                            outlineRect.bottom.toFloat(),
-                            cornerRadius / scaleX,
-                            cornerRadius / scaleY,
-                            Path.Direction.CW,
-                        )
-                    }
-                    outline.setPath(outlinePath)
-                }
-            }
-    }
-
-    override fun onRecycle() {
+    fun onRecycle() {
         uiState = Uninitialized
-        onSizeChanged = null
-        outlineBounds = null
         resetViews()
     }
 
@@ -130,7 +70,7 @@
         resetViews()
         when (state) {
             is Uninitialized -> {}
-            is LiveTile -> drawLiveWindow(state)
+            is LiveTile -> drawLiveWindow()
             is SnapshotSplash -> drawSnapshotSplash(state)
             is BackgroundOnly -> drawBackground(state.backgroundColor)
         }
@@ -155,25 +95,12 @@
         splashIcon.alpha = value
     }
 
-    fun doOnSizeChange(action: (width: Int, height: Int) -> Unit) {
-        onSizeChanged = action
-    }
-
-    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
-        super.onSizeChanged(w, h, oldw, oldh)
-        onSizeChanged?.invoke(width, height)
-        bounds.set(0, 0, w, h)
-        invalidateOutline()
-    }
-
-    override fun setScaleX(scaleX: Float) {
-        super.setScaleX(scaleX)
+    fun parentScaleXUpdated(scaleX: Float) {
         // Splash icon should ignore scale on TTV
         splashIcon.scaleX = 1 / scaleX
     }
 
-    override fun setScaleY(scaleY: Float) {
-        super.setScaleY(scaleY)
+    fun parentScaleYUpdated(scaleY: Float) {
         // Splash icon should ignore scale on TTV
         splashIcon.scaleY = 1 / scaleY
     }
@@ -187,20 +114,14 @@
         splashIcon.setImageDrawable(null)
         scrimView.alpha = 0f
         setBackgroundColor(Color.BLACK)
-        taskThumbnailViewHeader?.isInvisible = true
     }
 
     private fun drawBackground(@ColorInt background: Int) {
         setBackgroundColor(background)
     }
 
-    private fun drawLiveWindow(liveTile: LiveTile) {
+    private fun drawLiveWindow() {
         liveTileView.isInvisible = false
-
-        if (liveTile is LiveTile.WithHeader) {
-            taskThumbnailViewHeader?.isInvisible = false
-            taskThumbnailViewHeader?.setHeader(liveTile.header)
-        }
     }
 
     private fun drawSnapshotSplash(snapshotSplash: SnapshotSplash) {
@@ -212,11 +133,6 @@
     }
 
     private fun drawSnapshot(snapshot: Snapshot) {
-        if (snapshot is Snapshot.WithHeader) {
-            taskThumbnailViewHeader?.isInvisible = false
-            taskThumbnailViewHeader?.setHeader(snapshot.header)
-        }
-
         drawBackground(snapshot.backgroundColor)
         thumbnailView.setImageBitmap(snapshot.bitmap)
         thumbnailView.isInvisible = false
@@ -232,16 +148,6 @@
         Log.d(TAG, "[TaskThumbnailView@${Integer.toHexString(hashCode())}] $message")
     }
 
-    private fun maybeCreateHeader() {
-        if (enableDesktopExplodedView() && taskThumbnailViewHeader == null) {
-            taskThumbnailViewHeader =
-                LayoutInflater.from(context)
-                    .inflate(R.layout.task_thumbnail_view_header, this, false)
-                    as TaskThumbnailViewHeader
-            addView(taskThumbnailViewHeader)
-        }
-    }
-
     private companion object {
         const val TAG = "TaskThumbnailView"
         private const val MAX_SCRIM_ALPHA = 0.4f
diff --git a/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt b/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt
deleted file mode 100644
index f51660b..0000000
--- a/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt
+++ /dev/null
@@ -1,127 +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.task.util
-
-import android.util.Log
-import android.view.View.OnLayoutChangeListener
-import com.android.launcher3.util.coroutines.DispatcherProvider
-import com.android.quickstep.TaskOverlayFactory
-import com.android.quickstep.recents.di.RecentsDependencies
-import com.android.quickstep.recents.di.get
-import com.android.quickstep.task.thumbnail.TaskOverlayUiState
-import com.android.quickstep.task.thumbnail.TaskOverlayUiState.Disabled
-import com.android.quickstep.task.thumbnail.TaskOverlayUiState.Enabled
-import com.android.quickstep.task.viewmodel.TaskOverlayViewModel
-import com.android.systemui.shared.recents.model.Task
-import kotlinx.coroutines.CoroutineName
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.cancel
-import kotlinx.coroutines.flow.dropWhile
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.launch
-
-/**
- * Helper for [TaskOverlayFactory.TaskOverlay] to interact with [TaskOverlayViewModel], this helper
- * should merge with [TaskOverlayFactory.TaskOverlay] when it's migrated to MVVM.
- */
-class TaskOverlayHelper(val task: Task, val overlay: TaskOverlayFactory.TaskOverlay<*>) {
-    private val recentsCoroutineScope: CoroutineScope = RecentsDependencies.get()
-    private val dispatcherProvider: DispatcherProvider = RecentsDependencies.get()
-    private lateinit var overlayInitializedScope: CoroutineScope
-    private var uiState: TaskOverlayUiState = Disabled
-
-    private lateinit var viewModel: TaskOverlayViewModel
-
-    // TODO(b/331753115): TaskOverlay should listen for state changes and react.
-    val enabledState: Enabled
-        get() = uiState as Enabled
-
-    private val snapshotLayoutChangeListener = OnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
-        (uiState as? Enabled)?.let { initOverlay(it) }
-    }
-
-    fun getThumbnailMatrix() = getThumbnailPositionState().matrix
-
-    private fun getThumbnailPositionState() =
-        viewModel.getThumbnailPositionState(
-            overlay.snapshotView.width,
-            overlay.snapshotView.height,
-            overlay.snapshotView.isLayoutRtl,
-        )
-
-    fun init() {
-        overlayInitializedScope =
-            CoroutineScope(
-                SupervisorJob() + Dispatchers.Main.immediate + CoroutineName("TaskOverlayHelper")
-            )
-        viewModel =
-            TaskOverlayViewModel(
-                task = task,
-                recentsViewData = RecentsDependencies.get(),
-                getThumbnailPositionUseCase = RecentsDependencies.get(),
-                recentTasksRepository = RecentsDependencies.get(),
-                dispatcherProvider = RecentsDependencies.get(),
-            )
-        viewModel.overlayState
-            .dropWhile { it == Disabled }
-            .flowOn(dispatcherProvider.background)
-            .onEach {
-                uiState = it
-                if (it is Enabled) {
-                    initOverlay(it)
-                } else {
-                    reset()
-                }
-            }
-            .launchIn(overlayInitializedScope)
-        overlay.snapshotView.addOnLayoutChangeListener(snapshotLayoutChangeListener)
-    }
-
-    private fun initOverlay(enabledState: Enabled) {
-        if (DEBUG) {
-            Log.d(TAG, "initOverlay - taskId: ${task.key.id}, thumbnail: ${enabledState.thumbnail}")
-        }
-        with(getThumbnailPositionState()) {
-            overlay.initOverlay(task, enabledState.thumbnail, matrix, isRotated)
-        }
-    }
-
-    private fun reset() {
-        if (DEBUG) {
-            Log.d(TAG, "reset - taskId: ${task.key.id}")
-        }
-        overlay.reset()
-    }
-
-    fun destroy() {
-        recentsCoroutineScope.launch(dispatcherProvider.background) {
-            overlayInitializedScope.cancel("TaskOverlay being destroyed")
-        }
-        uiState = Disabled
-        overlay.snapshotView.removeOnLayoutChangeListener(snapshotLayoutChangeListener)
-        reset()
-    }
-
-    companion object {
-        private const val TAG = "TaskOverlayHelper"
-        private const val DEBUG = false
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/task/viewmodel/TaskOverlayViewModel.kt b/quickstep/src/com/android/quickstep/task/viewmodel/TaskOverlayViewModel.kt
deleted file mode 100644
index 9bff3ac..0000000
--- a/quickstep/src/com/android/quickstep/task/viewmodel/TaskOverlayViewModel.kt
+++ /dev/null
@@ -1,75 +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.task.viewmodel
-
-import android.graphics.Matrix
-import com.android.launcher3.util.coroutines.DispatcherProvider
-import com.android.quickstep.recents.data.RecentTasksRepository
-import com.android.quickstep.recents.domain.usecase.GetThumbnailPositionUseCase
-import com.android.quickstep.recents.viewmodel.RecentsViewData
-import com.android.quickstep.task.thumbnail.TaskOverlayUiState.Disabled
-import com.android.quickstep.task.thumbnail.TaskOverlayUiState.Enabled
-import com.android.systemui.shared.recents.model.Task
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
-
-/** View model for TaskOverlay */
-class TaskOverlayViewModel(
-    private val task: Task,
-    recentsViewData: RecentsViewData,
-    private val getThumbnailPositionUseCase: GetThumbnailPositionUseCase,
-    private val recentTasksRepository: RecentTasksRepository,
-    dispatcherProvider: DispatcherProvider,
-) {
-    val overlayState =
-        combine(
-                recentsViewData.overlayEnabled,
-                recentsViewData.settledFullyVisibleTaskIds
-                    .map { it.contains(task.key.id) }
-                    .distinctUntilChanged(),
-                recentTasksRepository.getThumbnailById(task.key.id),
-            ) { isOverlayEnabled, isFullyVisible, thumbnailData ->
-                if (isOverlayEnabled && isFullyVisible) {
-                    Enabled(
-                        isRealSnapshot = (thumbnailData?.isRealSnapshot ?: false) && !task.isLocked,
-                        thumbnailData?.thumbnail,
-                    )
-                } else {
-                    Disabled
-                }
-            }
-            .distinctUntilChanged()
-            .flowOn(dispatcherProvider.background)
-
-    fun getThumbnailPositionState(width: Int, height: Int, isRtl: Boolean): ThumbnailPositionState {
-        val thumbnailPositionState =
-            getThumbnailPositionUseCase(
-                thumbnailData = recentTasksRepository.getCurrentThumbnailById(task.key.id),
-                width = width,
-                height = height,
-                isRtl = isRtl,
-            )
-        return ThumbnailPositionState(
-            thumbnailPositionState.matrix,
-            thumbnailPositionState.isRotated,
-        )
-    }
-
-    data class ThumbnailPositionState(val matrix: Matrix, val isRotated: Boolean)
-}
diff --git a/quickstep/src/com/android/quickstep/util/ActivityPreloadUtil.kt b/quickstep/src/com/android/quickstep/util/ActivityPreloadUtil.kt
index 47b39db..df26b6b 100644
--- a/quickstep/src/com/android/quickstep/util/ActivityPreloadUtil.kt
+++ b/quickstep/src/com/android/quickstep/util/ActivityPreloadUtil.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.content.Intent
 import android.os.Trace
+import android.view.Display.DEFAULT_DISPLAY
 import com.android.launcher3.provider.RestoreDbTask
 import com.android.launcher3.util.Executors
 import com.android.launcher3.util.LockedUserState
@@ -59,7 +60,12 @@
             // The activity has been created before the initialization of overview service. It is
             // usually happens when booting or launcher is the top activity, so we should already
             // have the latest state.
-            if (fromInit && overviewCompObserver.containerInterface.createdContainer != null) return
+            if (
+                fromInit &&
+                    overviewCompObserver.getContainerInterface(DEFAULT_DISPLAY).createdContainer !=
+                        null
+            )
+                return
 
             ActiveGestureProtoLogProxy.logPreloadRecentsAnimation()
             val overviewIntent = Intent(overviewCompObserver.overviewIntentIgnoreSysUiState)
diff --git a/quickstep/src/com/android/quickstep/util/AnimUtils.java b/quickstep/src/com/android/quickstep/util/AnimUtils.java
index fda0c29..3492788 100644
--- a/quickstep/src/com/android/quickstep/util/AnimUtils.java
+++ b/quickstep/src/com/android/quickstep/util/AnimUtils.java
@@ -21,16 +21,22 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
 import android.animation.AnimatorSet;
-import android.annotation.NonNull;
+import android.os.BinderUtils;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.IRemoteCallback;
 import android.view.animation.Interpolator;
 
+import androidx.annotation.NonNull;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.views.ActivityContext;
 import com.android.quickstep.views.RecentsViewContainer;
 
 /**
@@ -95,14 +101,30 @@
     }
 
     /**
-     * Returns a IRemoteCallback which completes the provided list as a result
+     * Returns a IRemoteCallback which completes the provided list as a result or when the owner
+     * is destroyed
      */
-    public static IRemoteCallback completeRunnableListCallback(RunnableList list) {
+    public static IRemoteCallback completeRunnableListCallback(
+            RunnableList list, ActivityContext owner) {
+        DefaultLifecycleObserver destroyObserver = new DefaultLifecycleObserver() {
+            @Override
+            public void onDestroy(@NonNull LifecycleOwner owner) {
+                list.executeAllAndClear();
+            }
+        };
+        MAIN_EXECUTOR.execute(() -> owner.getLifecycle().addObserver(destroyObserver));
+        list.add(() -> owner.getLifecycle().removeObserver(destroyObserver));
+
         return new IRemoteCallback.Stub() {
             @Override
             public void sendResult(Bundle bundle) {
                 MAIN_EXECUTOR.execute(list::executeAllAndDestroy);
             }
+
+            @Override
+            public IBinder asBinder() {
+                return BinderUtils.wrapLifecycle(this, owner.getOwnerCleanupSet());
+            }
         };
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt b/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
index d00a39c..3bc9adc 100644
--- a/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
+++ b/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
@@ -21,6 +21,7 @@
 import android.app.contextualsearch.ContextualSearchManager.FEATURE_CONTEXTUAL_SEARCH
 import android.content.Context
 import android.util.Log
+import android.view.Display.DEFAULT_DISPLAY
 import androidx.annotation.VisibleForTesting
 import com.android.internal.app.AssistUtils
 import com.android.launcher3.logging.StatsLogManager
@@ -222,7 +223,8 @@
 
     @VisibleForTesting
     fun getRecentsContainerInterface(): BaseContainerInterface<*, *>? {
-        return OverviewComponentObserver.INSTANCE.get(context).containerInterface
+        return OverviewComponentObserver.INSTANCE.get(context)
+            .getContainerInterface(DEFAULT_DISPLAY)
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/util/DesksUtils.kt b/quickstep/src/com/android/quickstep/util/DesksUtils.kt
new file mode 100644
index 0000000..ccfdbb9
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/DesksUtils.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2025 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.util
+
+import android.content.Context
+import android.window.DesktopExperienceFlags
+import com.android.systemui.shared.recents.model.Task
+
+class DesksUtils {
+    companion object {
+        @JvmStatic
+        fun areMultiDesksFlagsEnabled() =
+            DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue() &&
+                DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_FRONTEND.isTrue()
+
+        /** Returns true if this [task] contains the [DesktopWallpaperActivity]. */
+        @JvmStatic
+        fun isDesktopWallpaperTask(context: Context, task: Task): Boolean {
+            val sysUiPackage =
+                context.getResources().getString(com.android.internal.R.string.config_systemUi)
+            val component = task.key.component
+            if (component != null) {
+                return component.className.contains("DesktopWallpaperActivity") &&
+                    component.packageName.contains(sysUiPackage)
+            }
+            return false
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/GroupTask.kt b/quickstep/src/com/android/quickstep/util/GroupTask.kt
index 49c37dc..add8821 100644
--- a/quickstep/src/com/android/quickstep/util/GroupTask.kt
+++ b/quickstep/src/com/android/quickstep/util/GroupTask.kt
@@ -15,6 +15,9 @@
  */
 package com.android.quickstep.util
 
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK
+import com.android.launcher3.model.data.TaskItemInfo
+import com.android.launcher3.model.data.WorkspaceItemInfo
 import com.android.launcher3.util.SplitConfigurationOptions
 import com.android.quickstep.views.TaskViewType
 import com.android.systemui.shared.recents.model.Task
@@ -68,6 +71,21 @@
         if (o !is SingleTask) return false
         return super.equals(o)
     }
+
+    companion object {
+        /** Creates a [TaskItemInfo] using the information of the SingleTask */
+        fun createTaskItemInfo(task: SingleTask): TaskItemInfo {
+            // TODO: b/344657629 - Support GroupTask in addition to SingleTask.
+            val wii =
+                WorkspaceItemInfo().apply {
+                    title = task.task.title
+                    intent = task.task.key.baseIntent
+                    itemType = ITEM_TYPE_TASK
+                    contentDescription = task.task.titleDescription
+                }
+            return TaskItemInfo(task.task.key.id, wii)
+        }
+    }
 }
 
 /**
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index a5be89a..9cdde01 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -22,7 +22,9 @@
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 
+import static com.android.launcher3.Flags.enableOverviewOnConnectedDisplays;
 import static com.android.launcher3.LauncherPrefs.ALLOW_ROTATION;
+import static com.android.launcher3.LauncherPrefs.FIXED_LANDSCAPE_MODE;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.SettingsCache.ROTATION_SETTING_URI;
 import static com.android.quickstep.BaseActivityInterface.getTaskDimension;
@@ -43,6 +45,7 @@
 import androidx.annotation.NonNull;
 
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherPrefChangeListener;
 import com.android.launcher3.LauncherPrefs;
@@ -53,6 +56,7 @@
 import com.android.quickstep.BaseContainerInterface;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.fallback.window.RecentsDisplayModel;
 import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
 
 import java.lang.annotation.Retention;
@@ -105,9 +109,12 @@
     // Ignore shared prefs for home rotation rotation, allowing it in if the activity supports it
     private static final int FLAG_IGNORE_ALLOW_HOME_ROTATION_PREF = 1 << 9;
 
+    // Shared prefs for fixed 90 degree rotation, activities should rotate if they support it
+    private static final int FLAG_HOME_FIXED_LANDSCAPE_PREFS = 1 << 10;
+
     private static final int MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE =
             FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY
-            | FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY;
+                    | FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY;
 
     // State for which rotation watcher will be enabled. We skip it when home rotation or
     // multi-window is enabled as in that case, activity itself rotates.
@@ -225,7 +232,7 @@
 
     private boolean updateHandler() {
         mRecentsActivityRotation = inferRecentsActivityRotation(mDisplayRotation);
-        if (mRecentsActivityRotation == mTouchRotation || isRecentsActivityRotationAllowed()) {
+        if (mRecentsActivityRotation == mTouchRotation || shouldUseRealOrientation()) {
             mOrientationHandler = RecentsPagedOrientationHandler.PORTRAIT;
         } else if (mTouchRotation == ROTATION_90) {
             mOrientationHandler = RecentsPagedOrientationHandler.LANDSCAPE;
@@ -247,9 +254,13 @@
         return mStateId != oldStateId;
     }
 
+    private boolean shouldUseRealOrientation() {
+        return isRecentsActivityRotationAllowed() || isLauncherFixedLandscape();
+    }
+
     @SurfaceRotation
     private int inferRecentsActivityRotation(@SurfaceRotation int displayRotation) {
-        if (isRecentsActivityRotationAllowed()) {
+        if (shouldUseRealOrientation()) {
             return mRecentsRotation < 0 ? displayRotation : mRecentsRotation;
         } else {
             return ROTATION_0;
@@ -286,6 +297,9 @@
         if (LauncherPrefs.ALLOW_ROTATION.getSharedPrefKey().equals(s)) {
             updateHomeRotationSetting();
         }
+        if (LauncherPrefs.FIXED_LANDSCAPE_MODE.getSharedPrefKey().equals(s)) {
+            updateFixedLandscapeSetting();
+        }
     }
 
     private void updateAutoRotateSetting() {
@@ -293,6 +307,15 @@
                 mSettingsCache.getValue(ROTATION_SETTING_URI, 1));
     }
 
+    private void updateFixedLandscapeSetting() {
+        if (Flags.oneGridSpecs()) {
+            setFlag(
+                    FLAG_HOME_FIXED_LANDSCAPE_PREFS,
+                    LauncherPrefs.get(mContext).get(FIXED_LANDSCAPE_MODE)
+            );
+        }
+    }
+
     private void updateHomeRotationSetting() {
         boolean homeRotationEnabled = LauncherPrefs.get(mContext).get(ALLOW_ROTATION);
         setFlag(FLAG_HOME_ROTATION_ALLOWED_IN_PREFS, homeRotationEnabled);
@@ -305,6 +328,7 @@
         // initialize external flags
         updateAutoRotateSetting();
         updateHomeRotationSetting();
+        updateFixedLandscapeSetting();
     }
 
     private void initMultipleOrientationListeners() {
@@ -381,15 +405,19 @@
         setFlag(FLAG_IGNORE_ALLOW_HOME_ROTATION_PREF, true);
     }
 
+    public boolean isLauncherFixedLandscape() {
+        return (mFlags & FLAG_HOME_FIXED_LANDSCAPE_PREFS) == FLAG_HOME_FIXED_LANDSCAPE_PREFS;
+    }
+
     public boolean isRecentsActivityRotationAllowed() {
         // Activity rotation is allowed if the multi-simulated-rotation is not supported
         // (fallback recents or tablets) or activity rotation is enabled by various settings.
         return ((mFlags & MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE)
                 != MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE)
                 || (mFlags & (FLAG_IGNORE_ALLOW_HOME_ROTATION_PREF
-                        | FLAG_HOME_ROTATION_ALLOWED_IN_PREFS
-                        | FLAG_MULTIWINDOW_ROTATION_ALLOWED
-                        | FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING)) != 0;
+                | FLAG_HOME_ROTATION_ALLOWED_IN_PREFS
+                | FLAG_MULTIWINDOW_ROTATION_ALLOWED
+                | FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING)) != 0;
     }
 
     /**
@@ -571,19 +599,25 @@
     /**
      * Returns the device profile based on expected launcher rotation
      */
-    public DeviceProfile getLauncherDeviceProfile() {
-        InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext);
-        Point currentSize = DisplayController.INSTANCE.get(mContext).getInfo().currentSize;
-
-        int width, height;
-        if ((mRecentsActivityRotation == ROTATION_90 || mRecentsActivityRotation == ROTATION_270)) {
-            width = Math.max(currentSize.x, currentSize.y);
-            height = Math.min(currentSize.x, currentSize.y);
+    public DeviceProfile getLauncherDeviceProfile(int displayId) {
+        if (enableOverviewOnConnectedDisplays()) {
+            return RecentsDisplayModel.getINSTANCE().get(mContext).getRecentsWindowManager(
+                    displayId).getDeviceProfile();
         } else {
-            width = Math.min(currentSize.x, currentSize.y);
-            height = Math.max(currentSize.x, currentSize.y);
+            InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext);
+            Point currentSize = DisplayController.INSTANCE.get(mContext).getInfo().currentSize;
+
+            int width, height;
+            if ((mRecentsActivityRotation == ROTATION_90
+                    || mRecentsActivityRotation == ROTATION_270)) {
+                width = Math.max(currentSize.x, currentSize.y);
+                height = Math.min(currentSize.x, currentSize.y);
+            } else {
+                width = Math.min(currentSize.x, currentSize.y);
+                height = Math.max(currentSize.x, currentSize.y);
+            }
+            return idp.getBestMatch(width, height, mRecentsActivityRotation);
         }
-        return idp.getBestMatch(width, height, mRecentsActivityRotation);
     }
 
     private static String nameAndAddress(Object obj) {
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index a9dbbf2..d6e553d 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -143,7 +143,7 @@
         } else {
             // Initiating split from overview on fullscreen task TaskView
             val taskView = taskViewSupplier.get()
-            taskView.taskContainers.first().let {
+            taskView.firstTaskContainer!!.let {
                 val drawable = getDrawable(it.iconView, splitSelectSource)
                 return SplitAnimInitProps(
                     it.snapshotView,
@@ -192,7 +192,7 @@
         taskViewHeight: Int,
         isPrimaryTaskSplitting: Boolean,
     ) {
-        val snapshot = taskContainer.snapshotView
+        val taskContentView = taskContainer.taskContentView
         val iconView: View = taskContainer.iconView.asView()
         if (enableRefactorTaskThumbnail()) {
             builder.add(
@@ -241,7 +241,11 @@
             val centerThumbnailTranslationX: Float = (taskViewWidth - snapshotViewSize.x) / 2f
             val finalScaleX: Float = taskViewWidth.toFloat() / snapshotViewSize.x
             builder.add(
-                ObjectAnimator.ofFloat(snapshot, View.TRANSLATION_X, centerThumbnailTranslationX)
+                ObjectAnimator.ofFloat(
+                    taskContentView,
+                    View.TRANSLATION_X,
+                    centerThumbnailTranslationX,
+                )
             )
             if (!enableOverviewIconMenu()) {
                 // icons are anchored from Gravity.END, so need to use negative translation
@@ -250,15 +254,17 @@
                     ObjectAnimator.ofFloat(iconView, View.TRANSLATION_X, -centerIconTranslationX)
                 )
             }
-            builder.add(ObjectAnimator.ofFloat(snapshot, View.SCALE_X, finalScaleX))
+            builder.add(ObjectAnimator.ofFloat(taskContentView, View.SCALE_X, finalScaleX))
 
             // Reset other dimensions
             // TODO(b/271468547), can't set Y translate to 0, need to account for top space
-            snapshot.scaleY = 1f
+            taskContentView.scaleY = 1f
             val translateYResetVal: Float =
                 if (!isPrimaryTaskSplitting) 0f
                 else deviceProfile.overviewTaskThumbnailTopMarginPx.toFloat()
-            builder.add(ObjectAnimator.ofFloat(snapshot, View.TRANSLATION_Y, translateYResetVal))
+            builder.add(
+                ObjectAnimator.ofFloat(taskContentView, View.TRANSLATION_Y, translateYResetVal)
+            )
         } else {
             val thumbnailSize = taskViewHeight - deviceProfile.overviewTaskThumbnailTopMarginPx
             // Center view first so scaling happens uniformly, alternatively we can move pivotY to 0
@@ -281,18 +287,22 @@
             }
             val finalScaleY: Float = thumbnailSize.toFloat() / snapshotViewSize.y
             builder.add(
-                ObjectAnimator.ofFloat(snapshot, View.TRANSLATION_Y, centerThumbnailTranslationY)
+                ObjectAnimator.ofFloat(
+                    taskContentView,
+                    View.TRANSLATION_Y,
+                    centerThumbnailTranslationY,
+                )
             )
 
             if (!enableOverviewIconMenu()) {
                 // icons are anchored from Gravity.END, so need to use negative translation
                 builder.add(ObjectAnimator.ofFloat(iconView, View.TRANSLATION_X, 0f))
             }
-            builder.add(ObjectAnimator.ofFloat(snapshot, View.SCALE_Y, finalScaleY))
+            builder.add(ObjectAnimator.ofFloat(taskContentView, View.SCALE_Y, finalScaleY))
 
             // Reset other dimensions
-            snapshot.scaleX = 1f
-            builder.add(ObjectAnimator.ofFloat(snapshot, View.TRANSLATION_X, 0f))
+            taskContentView.scaleX = 1f
+            builder.add(ObjectAnimator.ofFloat(taskContentView, View.TRANSLATION_X, 0f))
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index b844079..661fe89 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -378,6 +378,29 @@
     }
 
     /**
+     * Calculates the crop rect for desktop tasks given the current matrix.
+     */
+    private void calculateDesktopTaskCropRect() {
+        // The approach here is to map a rect that represents the untransformed thumbnail position
+        // using the current matrix. This will give us a rect that can be intersected with
+        // [mFullTaskSize]. Using the intersection, we then compute how much of the task window that
+        // needs to be cropped (which will be nothing if the window is entirely within the desktop).
+        mTempRectF.set(0, 0, mThumbnailPosition.width(), mThumbnailPosition.height());
+        mMatrix.mapRect(mTempRectF);
+
+        float offsetX = mTempRectF.left;
+        float offsetY = mTempRectF.top;
+        float scale = mThumbnailPosition.width() / mTempRectF.width();
+
+        if (mTempRectF.intersect(mFullTaskSize.left, mFullTaskSize.top, mFullTaskSize.right,
+                mFullTaskSize.bottom)) {
+            mTempRectF.offset(-offsetX, -offsetY);
+            mTempRectF.scale(scale);
+            mTempRectF.round(mTmpCropRect);
+        }
+    }
+
+    /**
      * Applies the rotation on the matrix to so that it maps from launcher coordinate space to
      * window coordinate space.
      */
@@ -442,7 +465,16 @@
         mMatrix.postTranslate(mTaskRect.left, mTaskRect.top);
         if (mTaskRectTransform != null) {
             mMatrix.postConcat(mTaskRectTransform);
+
+            // Calculate cropping for desktop tasks. The order is important since it uses the
+            // current matrix. Therefore we calculate it here, after applying the task rect
+            // transform, but before applying scaling/translation that affects the whole
+            // recentsview.
+            if (mIsDesktopTask) {
+                calculateDesktopTaskCropRect();
+            }
         }
+
         mOrientationState.getOrientationHandler().setPrimary(mMatrix, MATRIX_POST_TRANSLATE,
                 taskPrimaryTranslation.value);
         mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
diff --git a/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt b/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt
index 9943770..37359a1 100644
--- a/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt
+++ b/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt
@@ -20,10 +20,14 @@
 import android.graphics.Canvas
 import android.graphics.Rect
 import android.util.AttributeSet
+import android.util.FloatProperty
 import android.widget.ImageButton
 import com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X
 import com.android.launcher3.R
+import com.android.launcher3.util.KFloatProperty
+import com.android.launcher3.util.MultiPropertyDelegate
 import com.android.launcher3.util.MultiPropertyFactory
+import com.android.launcher3.util.MultiValueAlpha
 import com.android.quickstep.util.BorderAnimator
 import com.android.quickstep.util.BorderAnimator.Companion.createSimpleBorderAnimator
 
@@ -34,28 +38,17 @@
 class AddDesktopButton @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
     ImageButton(context, attrs) {
 
-    private enum class TranslationX {
-        GRID,
-        OFFSET,
-    }
+    private val addDeskButtonAlpha = MultiValueAlpha(this, Alpha.entries.size)
+    var contentAlpha by MultiPropertyDelegate(addDeskButtonAlpha, Alpha.CONTENT)
+    var visibilityAlpha by MultiPropertyDelegate(addDeskButtonAlpha, Alpha.VISIBILITY)
 
     private val multiTranslationX =
         MultiPropertyFactory(this, VIEW_TRANSLATE_X, TranslationX.entries.size) { a: Float, b: Float
             ->
             a + b
         }
-
-    var gridTranslationX
-        get() = multiTranslationX[TranslationX.GRID.ordinal].value
-        set(value) {
-            multiTranslationX[TranslationX.GRID.ordinal].value = value
-        }
-
-    var offsetTranslationX
-        get() = multiTranslationX[TranslationX.OFFSET.ordinal].value
-        set(value) {
-            multiTranslationX[TranslationX.OFFSET.ordinal].value = value
-        }
+    var gridTranslationX by MultiPropertyDelegate(multiTranslationX, TranslationX.GRID)
+    var offsetTranslationX by MultiPropertyDelegate(multiTranslationX, TranslationX.OFFSET)
 
     private val focusBorderAnimator: BorderAnimator =
         createSimpleBorderAnimator(
@@ -91,9 +84,6 @@
         }
     }
 
-    protected fun getScrollAdjustment(showAsGrid: Boolean): Int =
-        if (showAsGrid) gridTranslationX.toInt() else 0
-
     private fun getBorderBounds(bounds: Rect) {
         bounds.set(0, 0, width, height)
         val outlinePadding =
@@ -105,4 +95,20 @@
         focusBorderAnimator.drawBorder(canvas)
         super.draw(canvas)
     }
+
+    companion object {
+        private enum class Alpha {
+            CONTENT,
+            VISIBILITY,
+        }
+
+        private enum class TranslationX {
+            GRID,
+            OFFSET,
+        }
+
+        @JvmField
+        val VISIBILITY_ALPHA: FloatProperty<AddDesktopButton> =
+            KFloatProperty(AddDesktopButton::visibilityAlpha)
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
deleted file mode 100644
index 2426697..0000000
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * Copyright (C) 2018 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.quickstep.util.BorderAnimator.DEFAULT_BORDER_COLOR;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.util.FloatProperty;
-import android.widget.Button;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.Flags;
-import com.android.launcher3.R;
-import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
-import com.android.quickstep.util.BorderAnimator;
-
-import kotlin.Unit;
-
-public class ClearAllButton extends Button {
-
-    public static final FloatProperty<ClearAllButton> VISIBILITY_ALPHA =
-            new FloatProperty<ClearAllButton>("visibilityAlpha") {
-                @Override
-                public Float get(ClearAllButton view) {
-                    return view.mVisibilityAlpha;
-                }
-
-                @Override
-                public void setValue(ClearAllButton view, float v) {
-                    view.setVisibilityAlpha(v);
-                }
-            };
-
-    public static final FloatProperty<ClearAllButton> DISMISS_ALPHA =
-            new FloatProperty<ClearAllButton>("dismissAlpha") {
-                @Override
-                public Float get(ClearAllButton view) {
-                    return view.mDismissAlpha;
-                }
-
-                @Override
-                public void setValue(ClearAllButton view, float v) {
-                    view.setDismissAlpha(v);
-                }
-            };
-
-    private float mScrollAlpha = 1;
-    private float mContentAlpha = 1;
-    private float mVisibilityAlpha = 1;
-    private float mDismissAlpha = 1;
-    private float mFullscreenProgress = 1;
-    private float mGridProgress = 1;
-
-    private boolean mIsRtl;
-    private float mNormalTranslationPrimary;
-    private float mFullscreenTranslationPrimary;
-    private float mGridTranslationPrimary;
-    private float mTaskAlignmentTranslationY;
-    private float mGridScrollOffset;
-    private float mScrollOffsetPrimary;
-
-    private int mSidePadding;
-    private int mOutlinePadding;
-    private boolean mBorderEnabled;
-    @Nullable
-    private final BorderAnimator mFocusBorderAnimator;
-
-    public ClearAllButton(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mIsRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
-
-        if (Flags.enableFocusOutline()) {
-            TypedArray styledAttrs = context.obtainStyledAttributes(attrs,
-                    R.styleable.ClearAllButton);
-            Resources resources = getResources();
-            mOutlinePadding = resources.getDimensionPixelSize(
-                    R.dimen.recents_clear_all_outline_padding);
-            mFocusBorderAnimator =
-                    BorderAnimator.createSimpleBorderAnimator(
-                            /* borderRadiusPx= */ resources.getDimensionPixelSize(
-                                    R.dimen.recents_clear_all_outline_radius),
-                            /* borderWidthPx= */ context.getResources().getDimensionPixelSize(
-                                    R.dimen.keyboard_quick_switch_border_width),
-                            /* boundsBuilder= */ this::updateBorderBounds,
-                            /* targetView= */ this,
-                            /* borderColor= */ styledAttrs.getColor(
-                                    R.styleable.ClearAllButton_focusBorderColor,
-                                    DEFAULT_BORDER_COLOR));
-            styledAttrs.recycle();
-        } else {
-            mFocusBorderAnimator = null;
-        }
-    }
-
-    private Unit updateBorderBounds(@NonNull Rect bounds) {
-        bounds.set(0, 0, getWidth(), getHeight());
-        // Make the value negative to form a padding between button and outline
-        bounds.inset(-mOutlinePadding, -mOutlinePadding);
-        return Unit.INSTANCE;
-    }
-
-    @Override
-    public void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
-        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
-        if (mFocusBorderAnimator != null && mBorderEnabled) {
-            mFocusBorderAnimator.setBorderVisibility(gainFocus, /* animated= */ true);
-        }
-    }
-
-    /**
-     * Enable or disable showing border on focus change
-     */
-    public void setBorderEnabled(boolean enabled) {
-        if (mBorderEnabled == enabled) {
-            return;
-        }
-
-        mBorderEnabled = enabled;
-        if (mFocusBorderAnimator != null) {
-            mFocusBorderAnimator.setBorderVisibility(/* visible= */
-                    enabled && isFocused(), /* animated= */true);
-        }
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        if (mFocusBorderAnimator != null) {
-            mFocusBorderAnimator.drawBorder(canvas);
-        }
-        super.draw(canvas);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        RecentsPagedOrientationHandler orientationHandler =
-                getRecentsView().getPagedOrientationHandler();
-        mSidePadding = orientationHandler.getClearAllSidePadding(getRecentsView(), mIsRtl);
-    }
-
-    private RecentsView getRecentsView() {
-        return (RecentsView) getParent();
-    }
-
-    @Override
-    public void onRtlPropertiesChanged(int layoutDirection) {
-        super.onRtlPropertiesChanged(layoutDirection);
-        mIsRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-
-    public float getScrollAlpha() {
-        return mScrollAlpha;
-    }
-
-    public void setContentAlpha(float alpha) {
-        if (mContentAlpha != alpha) {
-            mContentAlpha = alpha;
-            updateAlpha();
-        }
-    }
-
-    public void setVisibilityAlpha(float alpha) {
-        if (mVisibilityAlpha != alpha) {
-            mVisibilityAlpha = alpha;
-            updateAlpha();
-        }
-    }
-
-    public void setDismissAlpha(float alpha) {
-        if (mDismissAlpha != alpha) {
-            mDismissAlpha = alpha;
-            updateAlpha();
-        }
-    }
-
-    public void onRecentsViewScroll(int scroll, boolean gridEnabled) {
-        RecentsView recentsView = getRecentsView();
-        if (recentsView == null) {
-            return;
-        }
-
-        RecentsPagedOrientationHandler orientationHandler =
-                recentsView.getPagedOrientationHandler();
-        float orientationSize = orientationHandler.getPrimaryValue(getWidth(), getHeight());
-        if (orientationSize == 0) {
-            return;
-        }
-
-        int clearAllScroll = recentsView.getClearAllScroll();
-        int adjustedScrollFromEdge = Math.abs(scroll - clearAllScroll);
-        float shift = Math.min(adjustedScrollFromEdge, orientationSize);
-        mNormalTranslationPrimary = mIsRtl ? -shift : shift;
-        if (!gridEnabled) {
-            mNormalTranslationPrimary += mSidePadding;
-        }
-        applyPrimaryTranslation();
-        applySecondaryTranslation();
-        float clearAllSpacing =
-                recentsView.getPageSpacing() + recentsView.getClearAllExtraPageSpacing();
-        clearAllSpacing = mIsRtl ? -clearAllSpacing : clearAllSpacing;
-        mScrollAlpha = Math.max((clearAllScroll + clearAllSpacing - scroll) / clearAllSpacing, 0);
-        updateAlpha();
-    }
-
-    private void updateAlpha() {
-        final float alpha = mScrollAlpha * mContentAlpha * mVisibilityAlpha * mDismissAlpha;
-        setAlpha(alpha);
-        setClickable(Math.min(alpha, 1) == 1);
-    }
-
-    public void setFullscreenTranslationPrimary(float fullscreenTranslationPrimary) {
-        mFullscreenTranslationPrimary = fullscreenTranslationPrimary;
-        applyPrimaryTranslation();
-    }
-
-    /**
-     * Sets `mTaskAlignmentTranslationY` to the given `value`. In order to put the button at the
-     * middle in the secondary coordinate.
-     */
-    public void setTaskAlignmentTranslationY(float value) {
-        mTaskAlignmentTranslationY = value;
-        applySecondaryTranslation();
-    }
-
-    public void setGridTranslationPrimary(float gridTranslationPrimary) {
-        mGridTranslationPrimary = gridTranslationPrimary;
-        applyPrimaryTranslation();
-    }
-
-    public void setGridScrollOffset(float gridScrollOffset) {
-        mGridScrollOffset = gridScrollOffset;
-    }
-
-    public void setScrollOffsetPrimary(float scrollOffsetPrimary) {
-        mScrollOffsetPrimary = scrollOffsetPrimary;
-    }
-
-    public float getScrollAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
-        float scrollAdjustment = 0;
-        if (fullscreenEnabled) {
-            scrollAdjustment += mFullscreenTranslationPrimary;
-        }
-        if (gridEnabled) {
-            scrollAdjustment += mGridTranslationPrimary + mGridScrollOffset;
-        }
-        scrollAdjustment += mScrollOffsetPrimary;
-        return scrollAdjustment;
-    }
-
-    public float getOffsetAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
-        return getScrollAdjustment(fullscreenEnabled, gridEnabled);
-    }
-
-    /**
-     * Adjust translation when this TaskView is about to be shown fullscreen.
-     *
-     * @param progress: 0 = no translation; 1 = translate according to TaskVIew translations.
-     */
-    public void setFullscreenProgress(float progress) {
-        mFullscreenProgress = progress;
-        applyPrimaryTranslation();
-    }
-
-    /**
-     * Moves ClearAllButton between carousel and 2 row grid.
-     *
-     * @param gridProgress 0 = carousel; 1 = 2 row grid.
-     */
-    public void setGridProgress(float gridProgress) {
-        mGridProgress = gridProgress;
-        applyPrimaryTranslation();
-    }
-
-    private void applyPrimaryTranslation() {
-        RecentsView recentsView = getRecentsView();
-        if (recentsView == null) {
-            return;
-        }
-
-        RecentsPagedOrientationHandler orientationHandler =
-                recentsView.getPagedOrientationHandler();
-        orientationHandler.getPrimaryViewTranslate().set(this,
-                orientationHandler.getPrimaryValue(0f, mTaskAlignmentTranslationY)
-                        + mNormalTranslationPrimary + getFullscreenTrans(
-                        mFullscreenTranslationPrimary) + getGridTrans(mGridTranslationPrimary));
-    }
-
-    private void applySecondaryTranslation() {
-        RecentsView recentsView = getRecentsView();
-        if (recentsView == null) {
-            return;
-        }
-
-        RecentsPagedOrientationHandler orientationHandler =
-                recentsView.getPagedOrientationHandler();
-        orientationHandler.getSecondaryViewTranslate().set(this,
-                orientationHandler.getSecondaryValue(0f, mTaskAlignmentTranslationY));
-    }
-
-    private float getFullscreenTrans(float endTranslation) {
-        return mFullscreenProgress > 0 ? endTranslation : 0;
-    }
-
-    private float getGridTrans(float endTranslation) {
-        return mGridProgress > 0 ? endTranslation : 0;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.kt b/quickstep/src/com/android/quickstep/views/ClearAllButton.kt
new file mode 100644
index 0000000..69c85ee
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.kt
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2025 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.Canvas
+import android.graphics.Rect
+import android.util.AttributeSet
+import android.util.FloatProperty
+import android.widget.Button
+import com.android.launcher3.Flags.enableFocusOutline
+import com.android.launcher3.R
+import com.android.launcher3.util.KFloatProperty
+import com.android.launcher3.util.MultiPropertyDelegate
+import com.android.launcher3.util.MultiValueAlpha
+import com.android.quickstep.util.BorderAnimator
+import com.android.quickstep.util.BorderAnimator.Companion.createSimpleBorderAnimator
+import kotlin.math.abs
+import kotlin.math.min
+
+class ClearAllButton @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
+    Button(context, attrs) {
+
+    private val clearAllButtonAlpha =
+        object : MultiValueAlpha(this, Alpha.entries.size) {
+            override fun apply(value: Float) {
+                super.apply(value)
+                isClickable = value >= 1f
+            }
+        }
+    var scrollAlpha by MultiPropertyDelegate(clearAllButtonAlpha, Alpha.SCROLL)
+    var contentAlpha by MultiPropertyDelegate(clearAllButtonAlpha, Alpha.CONTENT)
+    var visibilityAlpha by MultiPropertyDelegate(clearAllButtonAlpha, Alpha.VISIBILITY)
+    var dismissAlpha by MultiPropertyDelegate(clearAllButtonAlpha, Alpha.DISMISS)
+
+    var fullscreenProgress = 1f
+        set(value) {
+            if (field == value) {
+                return
+            }
+            field = value
+            applyPrimaryTranslation()
+        }
+
+    /**
+     * Moves ClearAllButton between carousel and 2 row grid.
+     *
+     * 0 = carousel; 1 = 2 row grid.
+     */
+    var gridProgress = 1f
+        set(value) {
+            if (field == value) {
+                return
+            }
+            field = value
+            applyPrimaryTranslation()
+        }
+
+    private var normalTranslationPrimary = 0f
+    var fullscreenTranslationPrimary = 0f
+        set(value) {
+            if (field == value) {
+                return
+            }
+            field = value
+            applyPrimaryTranslation()
+        }
+
+    var gridTranslationPrimary = 0f
+        set(value) {
+            if (field == value) {
+                return
+            }
+            field = value
+            applyPrimaryTranslation()
+        }
+
+    /** Used to put the button at the middle in the secondary coordinate. */
+    var taskAlignmentTranslationY = 0f
+        set(value) {
+            if (field == value) {
+                return
+            }
+            field = value
+            applySecondaryTranslation()
+        }
+
+    var gridScrollOffset = 0f
+    var scrollOffsetPrimary = 0f
+
+    private var sidePadding = 0
+    var borderEnabled = false
+        set(value) {
+            if (field == value) {
+                return
+            }
+            field = value
+            focusBorderAnimator?.setBorderVisibility(visible = field && isFocused, animated = true)
+        }
+
+    private val focusBorderAnimator: BorderAnimator? =
+        if (enableFocusOutline())
+            createSimpleBorderAnimator(
+                context.resources.getDimensionPixelSize(R.dimen.recents_clear_all_outline_radius),
+                context.resources.getDimensionPixelSize(R.dimen.keyboard_quick_switch_border_width),
+                this::getBorderBounds,
+                this,
+                context
+                    .obtainStyledAttributes(attrs, R.styleable.ClearAllButton)
+                    .getColor(
+                        R.styleable.ClearAllButton_focusBorderColor,
+                        BorderAnimator.DEFAULT_BORDER_COLOR,
+                    ),
+            )
+        else null
+
+    private fun getBorderBounds(bounds: Rect) {
+        bounds.set(0, 0, width, height)
+        val outlinePadding =
+            context.resources.getDimensionPixelSize(R.dimen.recents_clear_all_outline_padding)
+        // Make the value negative to form a padding between button and outline
+        bounds.inset(-outlinePadding, -outlinePadding)
+    }
+
+    public override fun onFocusChanged(
+        gainFocus: Boolean,
+        direction: Int,
+        previouslyFocusedRect: Rect?,
+    ) {
+        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect)
+        if (borderEnabled) {
+            focusBorderAnimator?.setBorderVisibility(gainFocus, /* animated= */ true)
+        }
+    }
+
+    override fun draw(canvas: Canvas) {
+        focusBorderAnimator?.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)
+        sidePadding =
+            recentsView?.let { it.pagedOrientationHandler?.getClearAllSidePadding(it, isLayoutRtl) }
+                ?: 0
+    }
+
+    private val recentsView: RecentsView<*, *>?
+        get() = parent as? RecentsView<*, *>?
+
+    override fun hasOverlappingRendering() = false
+
+    fun onRecentsViewScroll(scroll: Int, gridEnabled: Boolean) {
+        val recentsView = recentsView ?: return
+
+        val orientationSize =
+            recentsView.pagedOrientationHandler.getPrimaryValue(width, height).toFloat()
+        if (orientationSize == 0f) {
+            return
+        }
+
+        val clearAllScroll = recentsView.clearAllScroll
+        val adjustedScrollFromEdge = abs((scroll - clearAllScroll)).toFloat()
+        val shift = min(adjustedScrollFromEdge, orientationSize)
+        normalTranslationPrimary = if (isLayoutRtl) -shift else shift
+        if (!gridEnabled) {
+            normalTranslationPrimary += sidePadding.toFloat()
+        }
+        applyPrimaryTranslation()
+        applySecondaryTranslation()
+        var clearAllSpacing = recentsView.pageSpacing + recentsView.clearAllExtraPageSpacing
+        clearAllSpacing = if (isLayoutRtl) -clearAllSpacing else clearAllSpacing
+        scrollAlpha =
+            ((clearAllScroll + clearAllSpacing - scroll) / clearAllSpacing.toFloat()).coerceAtLeast(
+                0f
+            )
+    }
+
+    fun getScrollAdjustment(fullscreenEnabled: Boolean, gridEnabled: Boolean): Float {
+        var scrollAdjustment = 0f
+        if (fullscreenEnabled) {
+            scrollAdjustment += fullscreenTranslationPrimary
+        }
+        if (gridEnabled) {
+            scrollAdjustment += gridTranslationPrimary + gridScrollOffset
+        }
+        scrollAdjustment += scrollOffsetPrimary
+        return scrollAdjustment
+    }
+
+    fun getOffsetAdjustment(fullscreenEnabled: Boolean, gridEnabled: Boolean) =
+        getScrollAdjustment(fullscreenEnabled, gridEnabled)
+
+    private fun applyPrimaryTranslation() {
+        val recentsView = recentsView ?: return
+        val orientationHandler = recentsView.pagedOrientationHandler
+        orientationHandler.primaryViewTranslate.set(
+            this,
+            (orientationHandler.getPrimaryValue(0f, taskAlignmentTranslationY) +
+                normalTranslationPrimary +
+                getFullscreenTrans(fullscreenTranslationPrimary) +
+                getGridTrans(gridTranslationPrimary)),
+        )
+    }
+
+    private fun applySecondaryTranslation() {
+        val recentsView = recentsView ?: return
+        val orientationHandler = recentsView.pagedOrientationHandler
+        orientationHandler.secondaryViewTranslate.set(
+            this,
+            orientationHandler.getSecondaryValue(0f, taskAlignmentTranslationY),
+        )
+    }
+
+    private fun getFullscreenTrans(endTranslation: Float) =
+        if (fullscreenProgress > 0) endTranslation else 0f
+
+    private fun getGridTrans(endTranslation: Float) = if (gridProgress > 0) endTranslation else 0f
+
+    companion object {
+        private enum class Alpha {
+            SCROLL,
+            CONTENT,
+            VISIBILITY,
+            DISMISS,
+        }
+
+        @JvmField
+        val VISIBILITY_ALPHA: FloatProperty<ClearAllButton> =
+            KFloatProperty(ClearAllButton::visibilityAlpha)
+
+        @JvmField
+        val DISMISS_ALPHA: FloatProperty<ClearAllButton> =
+            KFloatProperty(ClearAllButton::dismissAlpha)
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
index 75f3b69..27657b4 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
@@ -35,6 +35,7 @@
 import com.android.launcher3.Flags.enableOverviewIconMenu
 import com.android.launcher3.Flags.enableRefactorTaskThumbnail
 import com.android.launcher3.R
+import com.android.launcher3.statehandlers.DesktopVisibilityController
 import com.android.launcher3.testing.TestLogging
 import com.android.launcher3.testing.shared.TestProtocol
 import com.android.launcher3.util.RunnableList
@@ -54,6 +55,7 @@
 import com.android.quickstep.recents.domain.model.DesktopTaskBoundsData
 import com.android.quickstep.recents.ui.viewmodel.DesktopTaskViewModel
 import com.android.quickstep.recents.ui.viewmodel.TaskData
+import com.android.quickstep.task.thumbnail.TaskContentView
 import com.android.quickstep.task.thumbnail.TaskThumbnailView
 import com.android.quickstep.util.DesktopTask
 import com.android.quickstep.util.RecentsOrientedState
@@ -67,29 +69,18 @@
         type = TaskViewType.DESKTOP,
         thumbnailFullscreenParams = DesktopFullscreenDrawParams(context),
     ) {
+    var deskId = DesktopVisibilityController.INACTIVE_DESK_ID
+
     private val contentViewFullscreenParams = FullscreenDrawParams(context)
 
-    private val taskThumbnailViewDeprecatedPool =
-        if (!enableRefactorTaskThumbnail()) {
-            ViewPool<TaskThumbnailViewDeprecated>(
-                context,
-                this,
-                R.layout.task_thumbnail_deprecated,
-                VIEW_POOL_MAX_SIZE,
-                VIEW_POOL_INITIAL_SIZE,
-            )
-        } else null
-
-    private val taskThumbnailViewPool =
-        if (enableRefactorTaskThumbnail()) {
-            ViewPool<TaskThumbnailView>(
-                context,
-                this,
-                R.layout.task_thumbnail,
-                VIEW_POOL_MAX_SIZE,
-                VIEW_POOL_INITIAL_SIZE,
-            )
-        } else null
+    private val taskContentViewPool =
+        ViewPool<TaskContentView>(
+            context,
+            this,
+            R.layout.task_content_view,
+            VIEW_POOL_MAX_SIZE,
+            VIEW_POOL_INITIAL_SIZE,
+        )
 
     private val tempPointF = PointF()
     private val lastComputedTaskSize = Rect()
@@ -239,7 +230,7 @@
             // for all cases where the progress is non-zero.
             if (explodeProgress == 0.0f || explodeProgress == 1.0f) {
                 // Reset scaling and translation that may have been applied during animation.
-                it.snapshotView.apply {
+                it.taskContentView.apply {
                     scaleX = 1.0f
                     scaleY = 1.0f
                     translationX = 0.0f
@@ -247,7 +238,7 @@
                 }
 
                 // Position the task to the same position as it would be on the desktop
-                it.snapshotView.updateLayoutParams<LayoutParams> {
+                it.taskContentView?.updateLayoutParams<LayoutParams> {
                     gravity = Gravity.LEFT or Gravity.TOP
                     width = taskWidth.toInt()
                     height = taskHeight.toInt()
@@ -258,7 +249,7 @@
                 if (
                     enableDesktopRecentsTransitionsCornersBugfix() && enableRefactorTaskThumbnail()
                 ) {
-                    it.thumbnailView.outlineBounds =
+                    it.taskContentView?.outlineBounds =
                         if (intersects(overviewTaskPosition, screenRect))
                             Rect(overviewTaskPosition).apply {
                                 intersectUnchecked(screenRect)
@@ -275,7 +266,7 @@
             } else {
                 // During the animation, apply translation and scale such that the view is
                 // transformed to where we want, without triggering layout.
-                it.snapshotView.apply {
+                it.taskContentView.apply {
                     pivotX = 0.0f
                     pivotY = 0.0f
                     translationX = taskLeft - left
@@ -293,6 +284,7 @@
         orientedState: RecentsOrientedState,
         taskOverlayFactory: TaskOverlayFactory,
     ) {
+        deskId = desktopTask.deskId
         // TODO(b/370495260): Minimized tasks should not be filtered with desktop exploded view
         // support.
         // Minimized tasks should not be shown in Overview.
@@ -308,17 +300,19 @@
         val backgroundViewIndex = contentView.indexOfChild(backgroundView)
         taskContainers =
             tasks.map { task ->
+                val taskContentView = taskContentViewPool.view
+                contentView.addView(taskContentView, backgroundViewIndex + 1)
                 val snapshotView =
                     if (enableRefactorTaskThumbnail()) {
-                        taskThumbnailViewPool!!.view
+                        taskContentView.findViewById<TaskThumbnailView>(R.id.snapshot)
                     } else {
-                        taskThumbnailViewDeprecatedPool!!.view
+                        taskContentView.findViewById<TaskThumbnailViewDeprecated>(R.id.snapshot)
                     }
-                contentView.addView(snapshotView, backgroundViewIndex + 1)
 
                 TaskContainer(
                     this,
                     task,
+                    taskContentView,
                     snapshotView,
                     iconView,
                     TransformingTouchDelegate(iconView.asView()),
@@ -336,18 +330,24 @@
 
         if (enableRefactorTaskThumbnail()) {
             viewModel =
-                DesktopTaskViewModel(organizeDesktopTasksUseCase = RecentsDependencies.get())
+                DesktopTaskViewModel(organizeDesktopTasksUseCase = RecentsDependencies.get(context))
         }
     }
 
     override fun onRecycle() {
         super.onRecycle()
+        deskId = DesktopVisibilityController.INACTIVE_DESK_ID
         explodeProgress = 0.0f
         viewModel = null
         visibility = VISIBLE
         taskContainers.forEach { removeAndRecycleThumbnailView(it) }
     }
 
+    override fun setOrientationState(orientationState: RecentsOrientedState) {
+        super.setOrientationState(orientationState)
+        iconView.setIconOrientation(orientationState, isGridTask)
+    }
+
     @SuppressLint("RtlHardcoded")
     override fun updateTaskSize(lastComputedTaskSize: Rect, lastComputedGridTaskSize: Rect) {
         super.updateTaskSize(lastComputedTaskSize, lastComputedGridTaskSize)
@@ -465,12 +465,8 @@
     }
 
     private fun removeAndRecycleThumbnailView(taskContainer: TaskContainer) {
-        contentView.removeView(taskContainer.snapshotView)
-        if (enableRefactorTaskThumbnail()) {
-            taskThumbnailViewPool!!.recycle(taskContainer.thumbnailView)
-        } else {
-            taskThumbnailViewDeprecatedPool!!.recycle(taskContainer.thumbnailViewDeprecated)
-        }
+        contentView.removeView(taskContainer.taskContentView)
+        taskContentViewPool.recycle(taskContainer.taskContentView)
     }
 
     private fun updateTaskPositions() {
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
index a8eee0a..71a4dde 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
@@ -24,7 +24,6 @@
 import android.view.ViewStub
 import com.android.internal.jank.Cuj
 import com.android.launcher3.Flags.enableOverviewIconMenu
-import com.android.launcher3.Flags.enableRefactorTaskThumbnail
 import com.android.launcher3.R
 import com.android.launcher3.Utilities
 import com.android.launcher3.util.RunnableList
@@ -78,8 +77,8 @@
         val splitBoundsConfig = splitBoundsConfig ?: return
         val inSplitSelection = getThisTaskCurrentlyInSplitSelection() != INVALID_TASK_ID
         pagedOrientationHandler.measureGroupedTaskViewThumbnailBounds(
-            leftTopTaskContainer.snapshotView,
-            rightBottomTaskContainer.snapshotView,
+            leftTopTaskContainer.taskContentView,
+            rightBottomTaskContainer.taskContentView,
             widthSize,
             heightSize,
             splitBoundsConfig,
@@ -95,12 +94,8 @@
 
     override fun inflateViewStubs() {
         super.inflateViewStubs()
-        findViewById<ViewStub>(R.id.bottomright_snapshot)
-            ?.apply {
-                layoutResource =
-                    if (enableRefactorTaskThumbnail()) R.layout.task_thumbnail
-                    else R.layout.task_thumbnail_deprecated
-            }
+        findViewById<ViewStub>(R.id.bottomright_task_content_view)
+            ?.apply { layoutResource = R.layout.task_content_view }
             ?.inflate()
         findViewById<ViewStub>(R.id.bottomRight_icon)
             ?.apply {
@@ -128,6 +123,7 @@
             listOf(
                 createTaskContainer(
                     primaryTask,
+                    R.id.task_content_view,
                     R.id.snapshot,
                     R.id.icon,
                     R.id.show_windows,
@@ -137,7 +133,8 @@
                 ),
                 createTaskContainer(
                     secondaryTask,
-                    R.id.bottomright_snapshot,
+                    R.id.bottomright_task_content_view,
+                    R.id.snapshot,
                     R.id.bottomRight_icon,
                     R.id.show_windows_right,
                     R.id.bottomRight_digital_wellbeing_toast,
@@ -240,8 +237,8 @@
                 leftTopTaskContainer.iconView.asView(),
                 rightBottomTaskContainer.iconView.asView(),
                 taskIconHeight,
-                leftTopTaskContainer.snapshotView.measuredWidth,
-                leftTopTaskContainer.snapshotView.measuredHeight,
+                leftTopTaskContainer.taskContentView.measuredWidth,
+                leftTopTaskContainer.taskContentView.measuredHeight,
                 measuredHeight,
                 measuredWidth,
                 isRtl,
diff --git a/quickstep/src/com/android/quickstep/views/IconAppChipView.kt b/quickstep/src/com/android/quickstep/views/IconAppChipView.kt
index 85d14cc..c20aa11 100644
--- a/quickstep/src/com/android/quickstep/views/IconAppChipView.kt
+++ b/quickstep/src/com/android/quickstep/views/IconAppChipView.kt
@@ -296,7 +296,7 @@
     fun getMenuTranslationY(): MultiPropertyFactory<View>.MultiProperty =
         viewTranslationY[INDEX_MENU_TRANSLATION]
 
-    internal fun revealAnim(isRevealing: Boolean) {
+    internal fun revealAnim(isRevealing: Boolean, animated: Boolean = true) {
         cancelInProgressAnimations()
         val collapsedBackgroundBounds = getCollapsedBackgroundLtrBounds()
         val expandedBackgroundBounds = getExpandedBackgroundLtrBounds()
@@ -392,6 +392,7 @@
             animator!!.setDuration(MENU_BACKGROUND_HIDE_DURATION.toLong())
         }
 
+        if (!animated) animator!!.duration = 0
         animator!!.interpolator = Interpolators.EMPHASIZED
         animator!!.start()
     }
@@ -427,7 +428,6 @@
         private const val INDEX_CONTENT_ALPHA = 0
         private const val INDEX_COLOR_FILTER_ALPHA = 1
         private const val INDEX_MODAL_ALPHA = 2
-
         /** Used to hide the app chip for 90:10 flex split. */
         private const val INDEX_MINIMUM_RATIO_ALPHA = 3
 
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 4a2be2a..5f08209 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -40,6 +40,8 @@
 import com.android.launcher3.util.NavigationMode;
 import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks;
 import com.android.quickstep.util.LayoutUtils;
+import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -157,6 +159,7 @@
         // Currently, the only grouped task action is "save app pairs".
         mActionButtons = findViewById(R.id.action_buttons);
         mSaveAppPairButton = findViewById(R.id.action_save_app_pair);
+        TypefaceUtils.setTypeface(mSaveAppPairButton, FontFamily.GSF_LABEL_LARGE);
         // Initialize a list to hold alphas for mActionButtons and any group action buttons.
         mMultiValueAlphas[ACTIONS_ALPHAS] = new MultiValueAlpha(mActionButtons, NUM_ALPHAS);
         mMultiValueAlphas[GROUP_ACTIONS_ALPHAS] =
diff --git a/quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt b/quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt
index 3430b39..4ce18f5 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt
@@ -21,6 +21,7 @@
 import androidx.dynamicanimation.animation.FloatValueHolder
 import androidx.dynamicanimation.animation.SpringAnimation
 import androidx.dynamicanimation.animation.SpringForce
+import com.android.launcher3.Flags.enableGridOnlyOverview
 import com.android.launcher3.R
 import com.android.launcher3.Utilities.boundToRange
 import com.android.launcher3.touch.SingleAxisSwipeDetector
@@ -31,6 +32,7 @@
 import com.google.android.msdl.data.model.MSDLToken
 import com.google.android.msdl.domain.InteractionProperties
 import kotlin.math.abs
+import kotlin.math.roundToInt
 
 /**
  * Helper class for [RecentsView]. This util class contains refactored and extracted functions from
@@ -76,11 +78,18 @@
                 }
                 .addEndListener { _, _, _, _ ->
                     if (isDismissing) {
-                        recentsView.dismissTaskView(
-                            draggedTaskView,
-                            /* animateTaskView = */ false,
-                            /* removeTask = */ true,
-                        )
+                        if (!recentsView.showAsGrid() || enableGridOnlyOverview()) {
+                            runTaskGridReflowSpringAnimation(
+                                draggedTaskView,
+                                getDismissedTaskGapForReflow(draggedTaskView),
+                            )
+                        } else {
+                            recentsView.dismissTaskView(
+                                draggedTaskView,
+                                /* animateTaskView = */ false,
+                                /* removeTask = */ true,
+                            )
+                        }
                     } else {
                         recentsView.onDismissAnimationEnds()
                     }
@@ -160,8 +169,8 @@
         if (recentsView.showAsGrid()) {
             val taskGridNavHelper =
                 TaskGridNavHelper(
-                    recentsView.topRowIdArray,
-                    recentsView.bottomRowIdArray,
+                    recentsView.mUtils.getTopRowIdArray(),
+                    recentsView.mUtils.getBottomRowIdArray(),
                     recentsView.mUtils.getLargeTaskViewIds(),
                     hasAddDesktopButton = false,
                 )
@@ -237,6 +246,19 @@
             )
     }
 
+    private fun createExpressiveGridReflowSpringForce(
+        finalPosition: Float = Float.MAX_VALUE
+    ): SpringForce {
+        val resourceProvider = DynamicResource.provider(recentsView.mContainer)
+        return SpringForce(finalPosition)
+            .setDampingRatio(
+                resourceProvider.getFloat(R.dimen.expressive_dismiss_task_trans_x_damping_ratio)
+            )
+            .setStiffness(
+                resourceProvider.getFloat(R.dimen.expressive_dismiss_task_trans_x_stiffness)
+            )
+    }
+
     /**
      * Plays a haptic as the dragged task view settles back into its rest state.
      *
@@ -286,6 +308,197 @@
             .apply { animateToFinalPosition(RECENTS_SCALE_SPRING_MULTIPLIER * scale) }
     }
 
+    /** Animates with springs the TaskViews beyond the dismissed task to fill the gap it left. */
+    private fun runTaskGridReflowSpringAnimation(
+        dismissedTaskView: TaskView,
+        dismissedTaskGap: Float,
+    ) {
+        // Empty spring animation exists for conditional start, and to drive neighboring springs.
+        val springAnimationDriver =
+            SpringAnimation(FloatValueHolder())
+                .setSpring(createExpressiveGridReflowSpringForce(finalPosition = dismissedTaskGap))
+        val towardsStart = if (recentsView.isRtl) dismissedTaskGap < 0 else dismissedTaskGap > 0
+
+        // Build the chains of Spring Animations
+        when {
+            !recentsView.showAsGrid() -> {
+                buildDismissReflowSpringAnimationChain(
+                    getTasksToReflow(
+                        recentsView.mUtils.taskViews.toList(),
+                        dismissedTaskView,
+                        towardsStart,
+                    ),
+                    dismissedTaskGap,
+                    previousSpring = springAnimationDriver,
+                )
+            }
+            dismissedTaskView.isLargeTile -> {
+                val lastSpringAnimation =
+                    buildDismissReflowSpringAnimationChain(
+                        getTasksToReflow(
+                            recentsView.mUtils.getLargeTaskViews(),
+                            dismissedTaskView,
+                            towardsStart,
+                        ),
+                        dismissedTaskGap,
+                        previousSpring = springAnimationDriver,
+                    )
+                // Add all top and bottom grid tasks when animating towards the end of the grid.
+                if (!towardsStart) {
+                    buildDismissReflowSpringAnimationChain(
+                        recentsView.mUtils.getTopRowTaskViews(),
+                        dismissedTaskGap,
+                        previousSpring = lastSpringAnimation,
+                    )
+                    buildDismissReflowSpringAnimationChain(
+                        recentsView.mUtils.getBottomRowTaskViews(),
+                        dismissedTaskGap,
+                        previousSpring = lastSpringAnimation,
+                    )
+                }
+            }
+            recentsView.isOnGridBottomRow(dismissedTaskView) -> {
+                buildDismissReflowSpringAnimationChain(
+                    getTasksToReflow(
+                        recentsView.mUtils.getBottomRowTaskViews(),
+                        dismissedTaskView,
+                        towardsStart,
+                    ),
+                    dismissedTaskGap,
+                    previousSpring = springAnimationDriver,
+                )
+            }
+            else -> {
+                buildDismissReflowSpringAnimationChain(
+                    getTasksToReflow(
+                        recentsView.mUtils.getTopRowTaskViews(),
+                        dismissedTaskView,
+                        towardsStart,
+                    ),
+                    dismissedTaskGap,
+                    previousSpring = springAnimationDriver,
+                )
+            }
+        }
+
+        // Start animations and remove the dismissed task at the end, dismiss immediately if no
+        // neighboring tasks exist.
+        val runGridEndAnimationAndRelayout = {
+            recentsView.expressiveDismissTaskView(dismissedTaskView)
+        }
+        springAnimationDriver?.apply {
+            addEndListener { _, _, _, _ -> runGridEndAnimationAndRelayout() }
+            animateToFinalPosition(dismissedTaskGap)
+        } ?: runGridEndAnimationAndRelayout()
+    }
+
+    private fun getDismissedTaskGapForReflow(dismissedTaskView: TaskView): Float {
+        val screenStart = recentsView.pagedOrientationHandler.getPrimaryScroll(recentsView)
+        val screenEnd =
+            screenStart + recentsView.pagedOrientationHandler.getMeasuredSize(recentsView)
+        val taskStart =
+            recentsView.pagedOrientationHandler.getChildStart(dismissedTaskView) +
+                dismissedTaskView.getOffsetAdjustment(recentsView.showAsGrid())
+        val taskSize =
+            recentsView.pagedOrientationHandler.getMeasuredSize(dismissedTaskView) *
+                dismissedTaskView.getSizeAdjustment(recentsView.showAsFullscreen())
+        val taskEnd = taskStart + taskSize
+
+        val isDismissedTaskBeyondEndOfScreen =
+            if (recentsView.isRtl) taskEnd > screenEnd else taskStart < screenStart
+        if (
+            dismissedTaskView.isLargeTile &&
+                isDismissedTaskBeyondEndOfScreen &&
+                recentsView.mUtils.getLargeTileCount() == 1
+        ) {
+            return with(recentsView) {
+                    pagedOrientationHandler.getPrimaryScroll(this) -
+                        getScrollForPage(indexOfChild(mUtils.getFirstNonDesktopTaskView()))
+                }
+                .toFloat()
+        }
+
+        // If current page is beyond last TaskView's index, use last TaskView to calculate offset.
+        val lastTaskViewIndex = recentsView.indexOfChild(recentsView.mUtils.getLastTaskView())
+        val currentPage = recentsView.currentPage.coerceAtMost(lastTaskViewIndex)
+        val dismissHorizontalFactor =
+            when {
+                dismissedTaskView.isGridTask -> 1f
+                currentPage == lastTaskViewIndex -> -1f
+                recentsView.indexOfChild(dismissedTaskView) < currentPage -> -1f
+                else -> 1f
+            } * (if (recentsView.isRtl) 1f else -1f)
+
+        return (dismissedTaskView.layoutParams.width + recentsView.pageSpacing) *
+            dismissHorizontalFactor
+    }
+
+    private fun getTasksToReflow(
+        taskViews: List<TaskView>,
+        dismissedTaskView: TaskView,
+        towardsStart: Boolean,
+    ): List<TaskView> {
+        val dismissedTaskViewIndex = taskViews.indexOf(dismissedTaskView)
+        if (dismissedTaskViewIndex == -1) {
+            return emptyList()
+        }
+        return if (towardsStart) {
+            taskViews.take(dismissedTaskViewIndex).reversed()
+        } else {
+            taskViews.takeLast(taskViews.size - dismissedTaskViewIndex - 1)
+        }
+    }
+
+    private fun willTaskBeVisibleAfterDismiss(taskView: TaskView, taskTranslation: Int): Boolean {
+        val screenStart = recentsView.pagedOrientationHandler.getPrimaryScroll(recentsView)
+        val screenEnd =
+            screenStart + recentsView.pagedOrientationHandler.getMeasuredSize(recentsView)
+        return recentsView.isTaskViewWithinBounds(
+            taskView,
+            screenStart,
+            screenEnd,
+            /* taskViewTranslation = */ taskTranslation,
+        )
+    }
+
+    /** Builds a chain of spring animations for task reflow after dismissal */
+    private fun buildDismissReflowSpringAnimationChain(
+        taskViews: Iterable<TaskView>,
+        dismissedTaskGap: Float,
+        previousSpring: SpringAnimation,
+    ): SpringAnimation {
+        var lastTaskViewSpring = previousSpring
+        taskViews
+            .filter { taskView ->
+                willTaskBeVisibleAfterDismiss(taskView, dismissedTaskGap.roundToInt())
+            }
+            .forEach { taskView ->
+                val taskViewSpringAnimation =
+                    SpringAnimation(
+                            taskView,
+                            FloatPropertyCompat.createFloatPropertyCompat(
+                                taskView.primaryDismissTranslationProperty
+                            ),
+                        )
+                        .setSpring(createExpressiveGridReflowSpringForce(dismissedTaskGap))
+                // Update live tile on spring animation.
+                if (taskView.isRunningTask && recentsView.enableDrawingLiveTile) {
+                    taskViewSpringAnimation.addUpdateListener { _, _, _ ->
+                        recentsView.runActionOnRemoteHandles { remoteTargetHandle ->
+                            remoteTargetHandle.taskViewSimulator.taskPrimaryTranslation.value =
+                                taskView.primaryDismissTranslationProperty.get(taskView)
+                        }
+                        recentsView.redrawLiveTile()
+                    }
+                }
+                lastTaskViewSpring.addUpdateListener { _, value, _ ->
+                    taskViewSpringAnimation.animateToFinalPosition(value)
+                }
+                lastTaskViewSpring = taskViewSpringAnimation
+            }
+        return lastTaskViewSpring
+    }
+
     private companion object {
         // The additional damping to apply to tasks further from the dismissed task.
         private const val ADDITIONAL_DISMISS_DAMPING_RATIO = 0.15f
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index d17dfb8..1f3fbf9 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -38,7 +38,6 @@
 import static com.android.launcher3.Flags.enableAdditionalHomeAnimations;
 import static com.android.launcher3.Flags.enableDesktopExplodedView;
 import static com.android.launcher3.Flags.enableDesktopTaskAlphaAnimation;
-import static com.android.launcher3.Flags.enableExpressiveDismissTaskMotion;
 import static com.android.launcher3.Flags.enableGridOnlyOverview;
 import static com.android.launcher3.Flags.enableLargeDesktopWindowingTile;
 import static com.android.launcher3.Flags.enableRefactorTaskThumbnail;
@@ -57,6 +56,7 @@
 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;
+import static com.android.launcher3.statehandlers.DesktopVisibilityController.INACTIVE_DESK_ID;
 import static com.android.launcher3.testing.shared.TestProtocol.DISMISS_ANIMATION_ENDS_MESSAGE;
 import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -65,6 +65,7 @@
 import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
 import static com.android.quickstep.BaseContainerInterface.getTaskDimension;
 import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
+import static com.android.quickstep.util.DesksUtils.areMultiDesksFlagsEnabled;
 import static com.android.quickstep.util.LogUtils.splitFailureMessage;
 import static com.android.quickstep.views.ClearAllButton.DISMISS_ALPHA;
 import static com.android.quickstep.views.OverviewActionsView.HIDDEN_ACTIONS_IN_MENU;
@@ -74,6 +75,7 @@
 import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_TASKS;
 import static com.android.quickstep.views.OverviewActionsView.HIDDEN_SPLIT_SELECT_ACTIVE;
 import static com.android.quickstep.views.RecentsViewUtils.DESK_EXPLODE_PROGRESS;
+import static com.android.quickstep.views.TaskView.SPLIT_ALPHA;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -143,7 +145,6 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Flags;
 import com.android.launcher3.Insettable;
-import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -201,6 +202,7 @@
 import com.android.quickstep.TaskViewUtils;
 import com.android.quickstep.TopTaskTracker;
 import com.android.quickstep.ViewUtils;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
 import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
 import com.android.quickstep.recents.data.RecentTasksRepository;
 import com.android.quickstep.recents.data.RecentsDeviceProfileRepository;
@@ -254,11 +256,11 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
-
 /**
  * A list of recent tasks.
  *
@@ -573,8 +575,6 @@
     // Keeps track of the previously known visible tasks for purposes of loading/unloading task data
     private final SparseBooleanArray mHasVisibleTaskData = new SparseBooleanArray();
 
-    private final InvariantDeviceProfile mIdp;
-
     /**
      * Getting views should be done via {@link #getTaskViewFromPool(int)}
      */
@@ -604,7 +604,7 @@
     private float mTaskThumbnailSplashAlpha = 0;
     private boolean mBorderEnabled = false;
     private boolean mShowAsGridLastOnLayout = false;
-    private final IntSet mTopRowIdSet = new IntSet();
+    protected final IntSet mTopRowIdSet = new IntSet();
     private int mClearAllShortTotalWidthTranslation = 0;
 
     // The GestureEndTarget that is still in progress.
@@ -864,7 +864,6 @@
     private final Matrix mTmpMatrix = new Matrix();
 
     private int mTaskViewCount = 0;
-
     @Nullable
     public TaskView getFirstTaskView() {
         return mUtils.getFirstTaskView();
@@ -884,22 +883,23 @@
 
         // Start Recents Dependency graph
         if (enableRefactorTaskThumbnail()) {
-            RecentsDependencies recentsDependencies = RecentsDependencies.Companion.initialize(
-                    this);
+            RecentsDependencies recentsDependencies = RecentsDependencies.Companion.maybeInitialize(
+                    context);
+            String scopeId = recentsDependencies.createRecentsViewScope(context);
             mRecentsViewModel = new RecentsViewModel(
-                    recentsDependencies.inject(RecentTasksRepository.class),
-                    recentsDependencies.inject(RecentsViewData.class)
+                    recentsDependencies.inject(RecentTasksRepository.class, scopeId),
+                    recentsDependencies.inject(RecentsViewData.class, scopeId)
             );
             mHelper = new RecentsViewModelHelper(
                     mRecentsViewModel,
-                    recentsDependencies.inject(CoroutineScope.class),
-                    recentsDependencies.inject(DispatcherProvider.class)
+                    recentsDependencies.inject(CoroutineScope.class, scopeId),
+                    recentsDependencies.inject(DispatcherProvider.class, scopeId)
             );
 
-            recentsDependencies.provide(RecentsRotationStateRepository.class,
+            recentsDependencies.provide(RecentsRotationStateRepository.class, scopeId,
                     () -> new RecentsRotationStateRepositoryImpl(mOrientationState));
 
-            recentsDependencies.provide(RecentsDeviceProfileRepository.class,
+            recentsDependencies.provide(RecentsDeviceProfileRepository.class, scopeId,
                     () -> new RecentsDeviceProfileRepositoryImpl(mContainer));
         } else {
             mRecentsViewModel = null;
@@ -911,7 +911,6 @@
         mFastFlingVelocity = getResources()
                 .getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
         mModel = RecentsModel.INSTANCE.get(context);
-        mIdp = InvariantDeviceProfile.INSTANCE.get(context);
 
         mClearAllButton = (ClearAllButton) LayoutInflater.from(context)
                 .inflate(R.layout.overview_clear_all_button, this, false);
@@ -1287,7 +1286,7 @@
                 Log.e(TAG, "Ongoing initializations could not be killed", e);
             }
             mHelper.onDestroy();
-            RecentsDependencies.destroy();
+            RecentsDependencies.destroy(getContext());
         }
     }
 
@@ -1462,7 +1461,8 @@
             anim.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
-                    finishRecentsAnimation(false /* toRecents */, null);
+                    finishRecentsAnimation(false /* toRecents */, true /*shouldPip*/,
+                            allAppsAreTranslucent(apps), null);
                 }
             });
         } else {
@@ -1473,6 +1473,18 @@
         anim.start();
     }
 
+    private boolean allAppsAreTranslucent(RemoteAnimationTarget[] apps) {
+        if (apps == null) {
+            return false;
+        }
+        for (int i = apps.length - 1; i >= 0; --i) {
+            if (!apps[i].isTranslucent) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     public boolean isTaskViewVisible(TaskView tv) {
         if (showAsGrid()) {
             int screenStart = getPagedOrientationHandler().getPrimaryScroll(this);
@@ -1497,7 +1509,7 @@
 
     @Nullable
     private TaskView getLastGridTaskView() {
-        return getLastGridTaskView(getTopRowIdArray(), getBottomRowIdArray());
+        return getLastGridTaskView(mUtils.getTopRowIdArray(), mUtils.getBottomRowIdArray());
     }
 
     @Nullable
@@ -1543,7 +1555,7 @@
      * @param taskViewTranslation taskView is considered within bounds if either translated or
      * original position of taskView is within screen bounds.
      */
-    private boolean isTaskViewWithinBounds(TaskView taskView, int screenStart, int screenEnd,
+    protected boolean isTaskViewWithinBounds(TaskView taskView, int screenStart, int screenEnd,
             int taskViewTranslation) {
         int taskStart = getPagedOrientationHandler().getChildStart(taskView)
                 + (int) taskView.getOffsetAdjustment(showAsGrid());
@@ -1578,7 +1590,10 @@
                 == getPagedOrientationHandler().getPrimaryScroll(this);
     }
 
-    private boolean isFocusedTaskInExpectedScrollPosition() {
+    /**
+     * Returns true if the focused TaskView is in expected scroll position.
+     */
+    public boolean isFocusedTaskInExpectedScrollPosition() {
         TaskView focusedTask = getFocusedTaskView();
         return focusedTask != null && isTaskInExpectedScrollPosition(focusedTask);
     }
@@ -1912,12 +1927,16 @@
             return;
         }
 
-        int[] currentTaskIds;
         TaskView currentTaskView = getTaskViewAt(mCurrentPage);
-        if (currentTaskView != null) {
+        int[] currentTaskIds = null;
+        // Track the current DesktopTaskView through [deskId] as a desk can be empty without any
+        // tasks.
+        int currentTaskViewDeskId = INACTIVE_DESK_ID;
+        if (areMultiDesksFlagsEnabled()
+                && currentTaskView instanceof DesktopTaskView desktopTaskView) {
+            currentTaskViewDeskId = desktopTaskView.getDeskId();
+        } else if (currentTaskView != null) {
             currentTaskIds = currentTaskView.getTaskIds();
-        } else {
-            currentTaskIds = new int[0];
         }
 
         // Unload existing visible task data
@@ -1929,9 +1948,19 @@
 
         // Save running task ID if it exists before rebinding all taskViews, otherwise the task from
         // the runningTaskView currently bound could get assigned to another TaskView
-        int[] runningTaskIds = getTaskIdsForTaskViewId(mRunningTaskViewId);
-        int[] focusedTaskIds = getTaskIdsForTaskViewId(mFocusedTaskViewId);
+        TaskView runningTaskView = getRunningTaskView();
+        int[] runningTaskIds = null;
 
+        // Track the running TaskView through [deskId] as a desk can be empty without any tasks.
+        int runningTaskViewDeskId = INACTIVE_DESK_ID;
+        if (areMultiDesksFlagsEnabled()
+                && runningTaskView instanceof DesktopTaskView desktopTaskView) {
+            runningTaskViewDeskId = desktopTaskView.getDeskId();
+        } else if (runningTaskView != null) {
+            runningTaskIds = runningTaskView.getTaskIds();
+        }
+
+        int[] focusedTaskIds = getTaskIdsForTaskViewId(mFocusedTaskViewId);
         // Reset the focused task to avoiding initializing TaskViews layout as focused task during
         // binding. The focused task view will be updated after all the TaskViews are bound.
         setFocusedTaskViewId(INVALID_TASK_ID);
@@ -2038,24 +2067,24 @@
         updateTaskSize();
         updateChildTaskOrientations();
 
-        TaskView newRunningTaskView = null;
-        if (hasAllValidTaskIds(runningTaskIds)) {
+        TaskView newRunningTaskView = mUtils.getDesktopTaskViewForDeskId(runningTaskViewDeskId);
+        if (newRunningTaskView == null) {
             // Update mRunningTaskViewId to be the new TaskView that was assigned by binding
             // the full list of tasks to taskViews
             newRunningTaskView = getTaskViewByTaskIds(runningTaskIds);
-            if (newRunningTaskView != null) {
-                setRunningTaskViewId(newRunningTaskView.getTaskViewId());
+        }
+        if (newRunningTaskView != null) {
+            setRunningTaskViewId(newRunningTaskView.getTaskViewId());
+        } else {
+            if (mActiveGestureRunningTasks != null) {
+                // This will update mRunningTaskViewId and create a stub view if necessary.
+                // We try to avoid this because it can cause a scroll jump, but it is needed
+                // for cases where the running task isn't included in this load plan (e.g. if
+                // the current running task is excludedFromRecents.)
+                showCurrentTask(mActiveGestureRunningTasks, "applyLoadPlan");
+                newRunningTaskView = getRunningTaskView();
             } else {
-                if (mActiveGestureRunningTasks != null) {
-                    // This will update mRunningTaskViewId and create a stub view if necessary.
-                    // We try to avoid this because it can cause a scroll jump, but it is needed
-                    // for cases where the running task isn't included in this load plan (e.g. if
-                    // the current running task is excludedFromRecents.)
-                    showCurrentTask(mActiveGestureRunningTasks, "applyLoadPlan");
-                    newRunningTaskView = getRunningTaskView();
-                } else {
-                    setRunningTaskViewId(INVALID_TASK_ID);
-                }
+                setRunningTaskViewId(INVALID_TASK_ID);
             }
         }
 
@@ -2063,11 +2092,12 @@
         if (mNextPage != INVALID_PAGE) {
             // Restore mCurrentPage but don't call setCurrentPage() as that clobbers the scroll.
             mCurrentPage = previousCurrentPage;
-            if (hasAllValidTaskIds(currentTaskIds)) {
+            currentTaskView = mUtils.getDesktopTaskViewForDeskId(currentTaskViewDeskId);
+            if (currentTaskView == null) {
                 currentTaskView = getTaskViewByTaskIds(currentTaskIds);
-                if (currentTaskView != null) {
-                    targetPage = indexOfChild(currentTaskView);
-                }
+            }
+            if (currentTaskView != null) {
+                targetPage = indexOfChild(currentTaskView);
             }
         } else if (previousFocusedPage != INVALID_PAGE) {
             targetPage = previousFocusedPage;
@@ -2493,6 +2523,11 @@
         int minDistanceFromScreenStart = Integer.MAX_VALUE;
         int minDistanceFromScreenStartIndex = INVALID_PAGE;
         for (int i = 0; i < getChildCount(); ++i) {
+            // Do not set the destination page to the AddDesktopButton, which has the same page
+            // scrolls as the first [TaskView] and shouldn't be scrolled to.
+            if (getChildAt(i) instanceof AddDesktopButton) {
+                continue;
+            }
             int distanceFromScreenStart = Math.abs(mPageScrolls[i] - scaledScroll);
             if (distanceFromScreenStart < minDistanceFromScreenStart) {
                 minDistanceFromScreenStart = distanceFromScreenStart;
@@ -2809,9 +2844,13 @@
     /**
      * Called when a gesture from an app is starting.
      */
+    // TODO: b/401582344 - Implement a way to exclude the `DesktopWallpaperActivity` from being
+    //  considered in Overview.
     public void onGestureAnimationStart(Task[] runningTasks) {
         Log.d(TAG, "onGestureAnimationStart - runningTasks: " + Arrays.toString(runningTasks));
         mActiveGestureRunningTasks = runningTasks;
+
+
         // This needs to be called before the other states are set since it can create the task view
         if (mOrientationState.setGestureActive(true)) {
             reapplyActiveRotation();
@@ -2891,10 +2930,10 @@
         if (!shouldRotateMenuForFakeRotation) {
             return;
         }
-        TaskMenuView tv = (TaskMenuView) getTopOpenViewWithType(mContainer, TYPE_TASK_MENU);
-        if (tv != null) {
+        AbstractFloatingView floatingView = getTopOpenViewWithType(mContainer, TYPE_TASK_MENU);
+        if (floatingView instanceof TaskMenuView taskMenuView) {
             // Rotation is supported on phone (details at b/254198019#comment4)
-            tv.onRotationChanged();
+            taskMenuView.onRotationChanged();
         }
     }
 
@@ -2911,12 +2950,15 @@
             updateGridProperties();
         }
 
+        BaseState<?> endState = mSizeStrategy.stateFromGestureEndTarget(endTarget);
+        // Starting the desk exploded animation when the gesture from an app is released.
         if (enableDesktopExplodedView()) {
             if (animatorSet == null) {
-                mUtils.setDeskExplodeProgress(1);
+                mUtils.setDeskExplodeProgress(endState.showExplodedDesktopView() ? 1f : 0f);
             } else {
                 animatorSet.play(
-                        ObjectAnimator.ofFloat(this, DESK_EXPLODE_PROGRESS, 1));
+                        ObjectAnimator.ofFloat(this, DESK_EXPLODE_PROGRESS,
+                                endState.showExplodedDesktopView() ? 1f : 0f));
             }
 
             for (TaskView taskView : getTaskViews()) {
@@ -2926,7 +2968,6 @@
             }
         }
 
-        BaseState<?> endState = mSizeStrategy.stateFromGestureEndTarget(endTarget);
         if (endState.displayOverviewTasksAsGrid(mContainer.getDeviceProfile())) {
             TaskView runningTaskView = getRunningTaskView();
             float runningTaskGridTranslationX = 0;
@@ -2999,9 +3040,24 @@
         startIconFadeInOnGestureComplete();
         animateActionsViewIn();
 
-        for (TaskView taskView : getTaskViews()) {
-            if (taskView instanceof DesktopTaskView desktopTaskView) {
-                desktopTaskView.setRemoteTargetHandles(mRemoteTargetHandles);
+        if (mEnableDrawingLiveTile) {
+            if (enableDesktopExplodedView()) {
+                for (TaskView taskView : getTaskViews()) {
+                    if (taskView instanceof DesktopTaskView desktopTaskView) {
+                        desktopTaskView.setRemoteTargetHandles(mRemoteTargetHandles);
+                    }
+                }
+            }
+            TaskView runningTaskView = getRunningTaskView();
+            if (showAsGrid() && enableGridOnlyOverview() && runningTaskView != null) {
+                runActionOnRemoteHandles(remoteTargetHandle -> {
+                    TaskViewSimulator taskViewSimulator = remoteTargetHandle.getTaskViewSimulator();
+                    // After settling in Overview, recentsScroll will be used to adjust horizontally
+                    // location and taskGridTranslationX doesn't needs to be applied.
+                    taskViewSimulator.taskGridTranslationX.value = 0;
+                    taskViewSimulator.taskGridTranslationY.value =
+                            runningTaskView.getGridTranslationY();
+                });
             }
         }
 
@@ -3028,6 +3084,21 @@
     }
 
     /**
+     * Creates a `DesktopTaskView` for the currently active desk on this display, which contains the
+     * gievn `runningTasks`.
+     */
+    private DesktopTaskView createDesktopTaskViewForActiveDesk(Task[] runningTasks) {
+        final int activeDeskId = mUtils.getActiveDeskIdOnThisDisplay();
+        final var desktopTaskView = (DesktopTaskView) getTaskViewFromPool(TaskViewType.DESKTOP);
+
+        // TODO: b/401582344 - Implement a way to exclude the `DesktopWallpaperActivity`.
+        desktopTaskView.bind(
+                new DesktopTask(activeDeskId, Arrays.asList(runningTasks)),
+                mOrientationState, mTaskOverlayFactory);
+        return desktopTaskView;
+    }
+
+    /**
      * Creates a task view (if necessary) to represent the task with the {@param runningTaskId}.
      *
      * All subsequent calls to reload will keep the task as the first item until {@link #reset()}
@@ -3041,20 +3112,14 @@
         }
 
         int runningTaskViewId = -1;
-        boolean needGroupTaskView = runningTasks.length > 1;
-        boolean needDesktopTask = hasDesktopTask(runningTasks);
         if (shouldAddStubTaskView(runningTasks)) {
             boolean wasEmpty = getChildCount() == 0;
             // Add an empty view for now until the task plan is loaded and applied
             final TaskView taskView;
+            final boolean needGroupTaskView = runningTasks.length > 1;
+            final boolean needDesktopTask = hasDesktopTask(runningTasks);
             if (needDesktopTask) {
-                final int activeDeskId =
-                        DesktopVisibilityController.INSTANCE.get(mContext).getActiveDeskId(
-                                mContainer.getDisplay().getDisplayId());
-                taskView = getTaskViewFromPool(TaskViewType.DESKTOP);
-                ((DesktopTaskView) taskView).bind(
-                        new DesktopTask(activeDeskId, Arrays.asList(runningTasks)),
-                        mOrientationState, mTaskOverlayFactory);
+                taskView = createDesktopTaskViewForActiveDesk(runningTasks);
             } else if (needGroupTaskView) {
                 taskView = getTaskViewFromPool(TaskViewType.GROUPED);
                 // When we create a placeholder task view mSplitBoundsConfig will be null, but with
@@ -3080,8 +3145,11 @@
             measure(makeMeasureSpec(getMeasuredWidth(), EXACTLY),
                     makeMeasureSpec(getMeasuredHeight(), EXACTLY));
             layout(getLeft(), getTop(), getRight(), getBottom());
-        } else if (getTaskViewByTaskId(runningTasks[0].key.id) != null) {
-            runningTaskViewId = getTaskViewByTaskId(runningTasks[0].key.id).getTaskViewId();
+        } else {
+            var runningTaskView = getTaskViewByTaskId(runningTasks[0].key.id);
+            if (runningTaskView != null) {
+                runningTaskViewId = runningTaskView.getTaskViewId();
+            }
         }
 
         boolean runningTaskTileHidden = mRunningTaskTileHidden;
@@ -3120,6 +3188,10 @@
                 return true;
             }
         }
+
+        // A running empty desk will have a single running app for the `DesktopWallpaperActivity`.
+        // TODO: b/401582344 - Implement a way to exclude the `DesktopWallpaperActivity`.
+
         return false;
     }
 
@@ -3516,19 +3588,6 @@
             mAddDesktopButton.setGridTranslationX(translationX);
         }
 
-        final TaskView runningTask = getRunningTaskView();
-        if (showAsGrid() && enableGridOnlyOverview() && runningTask != null) {
-            runActionOnRemoteHandles(
-                    remoteTargetHandle -> {
-                        remoteTargetHandle.getTaskViewSimulator().taskGridTranslationX.value =
-                                runningTask.getGridTranslationX()
-                                        - runningTask.getNonGridTranslationX();
-                        remoteTargetHandle.getTaskViewSimulator().taskGridTranslationY.value =
-                                runningTask.getGridTranslationY();
-                    }
-            );
-        }
-
         mClearAllButton.setGridTranslationPrimary(
                 clearAllTotalTranslationX - snappedTaskGridTranslationX);
         mClearAllButton.setGridScrollOffset(
@@ -3537,7 +3596,7 @@
         setGridProgress(mGridProgress);
     }
 
-    private boolean isSameGridRow(TaskView taskView1, TaskView taskView2) {
+    protected boolean isSameGridRow(TaskView taskView1, TaskView taskView2) {
         if (taskView1 == null || taskView2 == null) {
             return false;
         }
@@ -3741,11 +3800,13 @@
      * @param duration                    duration of the animation
      * @param dismissingForSplitSelection task dismiss animation is used for entering split
      *                                    selection state from app icon
+     * @param isExpressiveDismiss         runs expressive animations controlled via
+     *                                    {@link RecentsDismissUtils}
      */
     public void createTaskDismissAnimation(PendingAnimation anim,
             @Nullable TaskView dismissedTaskView,
             boolean animateTaskView, boolean shouldRemoveTask, long duration,
-            boolean dismissingForSplitSelection) {
+            boolean dismissingForSplitSelection, boolean isExpressiveDismiss) {
         if (mPendingAnimation != null) {
             mPendingAnimation.createPlaybackController().dispatchOnCancel().dispatchOnEnd();
         }
@@ -3863,7 +3924,7 @@
             }
         }
         if (lastGridTaskView != null && (lastGridTaskView.isVisibleToUser() || (
-                enableExpressiveDismissTaskMotion() && lastGridTaskView == dismissedTaskView))) {
+                isExpressiveDismiss && lastGridTaskView == dismissedTaskView))) {
             // After dismissal, animate translation of the remaining tasks to fill any gap left
             // between the end of the grid and the clear all button. Only animate if the clear
             // all button is visible or would become visible after dismissal.
@@ -4003,12 +4064,17 @@
                         lastTaskViewIndex);
                 int scrollDiff = newScroll[i] - oldScroll[i] + offset;
                 if (scrollDiff != 0) {
-                    translateTaskWhenDismissed(
-                            child,
-                            Math.abs(i - dismissedIndex),
-                            scrollDiff,
-                            anim,
-                            splitTimings);
+                    if (!isExpressiveDismiss) {
+                        translateTaskWhenDismissed(
+                                child,
+                                Math.abs(i - dismissedIndex),
+                                scrollDiff,
+                                anim,
+                                splitTimings);
+                    }
+                    if (child instanceof TaskView taskView) {
+                        mTaskViewsDismissPrimaryTranslations.put(taskView, scrollDiffPerPage);
+                    }
                     needsCurveUpdates = true;
                 }
             } else if (child instanceof TaskView taskView) {
@@ -4093,13 +4159,16 @@
                                 : finalTranslation + (mIsRtl ? -mLastComputedTaskSize.right
                                         : mLastComputedTaskSize.right);
                     }
-                    Animator dismissAnimator = ObjectAnimator.ofFloat(taskView,
-                            taskView.getPrimaryDismissTranslationProperty(),
-                            startTranslation, finalTranslation);
-                    dismissAnimator.setInterpolator(
-                            clampToProgress(dismissInterpolator, animationStartProgress,
-                                    animationEndProgress));
-                    anim.add(dismissAnimator);
+                    // Expressive dismiss will animate the translations of taskViews itself.
+                    if (!isExpressiveDismiss) {
+                        Animator dismissAnimator = ObjectAnimator.ofFloat(taskView,
+                                taskView.getPrimaryDismissTranslationProperty(),
+                                startTranslation, finalTranslation);
+                        dismissAnimator.setInterpolator(
+                                clampToProgress(dismissInterpolator, animationStartProgress,
+                                        animationEndProgress));
+                        anim.add(dismissAnimator);
+                    }
                     mTaskViewsDismissPrimaryTranslations.put(taskView, (int) finalTranslation);
                     distanceFromDismissedTask++;
                 }
@@ -4199,8 +4268,8 @@
                                     boolean isSnappedTaskInTopRow = mTopRowIdSet.contains(
                                             snappedTaskViewId);
                                     IntArray taskViewIdArray =
-                                            isSnappedTaskInTopRow ? getTopRowIdArray()
-                                                    : getBottomRowIdArray();
+                                            isSnappedTaskInTopRow ? mUtils.getTopRowIdArray()
+                                                    : mUtils.getBottomRowIdArray();
                                     int snappedIndex = taskViewIdArray.indexOf(snappedTaskViewId);
                                     taskViewIdArray.removeValue(dismissedTaskViewId);
                                     if (finalNextFocusedTaskView != null) {
@@ -4215,8 +4284,8 @@
                                         // dismissed row,
                                         // snap to the same column in the other grid row
                                         IntArray inverseRowTaskViewIdArray =
-                                                isSnappedTaskInTopRow ? getBottomRowIdArray()
-                                                        : getTopRowIdArray();
+                                                isSnappedTaskInTopRow ? mUtils.getBottomRowIdArray()
+                                                        : mUtils.getTopRowIdArray();
                                         if (snappedIndex < inverseRowTaskViewIdArray.size()) {
                                             taskViewIdToSnapTo = inverseRowTaskViewIdArray.get(
                                                     snappedIndex);
@@ -4298,8 +4367,8 @@
                                 }
                             }
 
-                            IntArray topRowIdArray = getTopRowIdArray();
-                            IntArray bottomRowIdArray = getBottomRowIdArray();
+                            IntArray topRowIdArray = mUtils.getTopRowIdArray();
+                            IntArray bottomRowIdArray = mUtils.getBottomRowIdArray();
                             if (finalSnapToLastTask) {
                                 // If snapping to last task, find the last task after dismissal.
                                 pageToSnapTo = indexOfChild(
@@ -4422,10 +4491,6 @@
                         animationEndProgress
                 )
         );
-
-        if (view instanceof TaskView) {
-            mTaskViewsDismissPrimaryTranslations.put((TaskView) view, scrollDiffPerPage);
-        }
         if (mEnableDrawingLiveTile && view instanceof TaskView
                 && ((TaskView) view).isRunningTask()) {
             pendingAnimation.addOnFrameCallback(() -> {
@@ -4470,41 +4535,6 @@
     }
 
     /**
-     * Returns all the tasks in the top row, without the focused task
-     */
-    IntArray getTopRowIdArray() {
-        if (mTopRowIdSet.isEmpty()) {
-            return new IntArray(0);
-        }
-        IntArray topArray = new IntArray(mTopRowIdSet.size());
-        for (TaskView taskView : getTaskViews()) {
-            int taskViewId = taskView.getTaskViewId();
-            if (mTopRowIdSet.contains(taskViewId)) {
-                topArray.add(taskViewId);
-            }
-        }
-        return topArray;
-    }
-
-    /**
-     * Returns all the tasks in the bottom row, without the focused task
-     */
-    IntArray getBottomRowIdArray() {
-        int bottomRowIdArraySize = getBottomRowTaskCountForTablet();
-        if (bottomRowIdArraySize <= 0) {
-            return new IntArray(0);
-        }
-        IntArray bottomArray = new IntArray(bottomRowIdArraySize);
-        for (TaskView taskView : getTaskViews()) {
-            int taskViewId = taskView.getTaskViewId();
-            if (!mTopRowIdSet.contains(taskViewId) && !taskView.isLargeTile()) {
-                bottomArray.add(taskViewId);
-            }
-        }
-        return bottomArray;
-    }
-
-    /**
      * Iterate the grid by columns instead of by TaskView index, starting after the focused task and
      * up to the last balanced column.
      *
@@ -4514,8 +4544,8 @@
         if (mTopRowIdSet.isEmpty()) return null; // return earlier
 
         TaskView lastVisibleTaskView = null;
-        IntArray topRowIdArray = getTopRowIdArray();
-        IntArray bottomRowIdArray = getBottomRowIdArray();
+        IntArray topRowIdArray = mUtils.getTopRowIdArray();
+        IntArray bottomRowIdArray = mUtils.getBottomRowIdArray();
         int balancedColumns = Math.min(bottomRowIdArray.size(), topRowIdArray.size());
 
         for (int i = 0; i < balancedColumns; i++) {
@@ -4533,24 +4563,33 @@
         return lastVisibleTaskView;
     }
 
-  private void removeTaskInternal(@NonNull TaskView dismissedTaskView) {
-    UI_HELPER_EXECUTOR
-        .getHandler()
-        .post(
-            () -> {
-              if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()
-                  && dismissedTaskView instanceof DesktopTaskView) {
-                // TODO: b/362720497 - Use the api with desktop id instead.
-                SystemUiProxy.INSTANCE
+    private void removeTaskInternal(@NonNull TaskView dismissedTaskView) {
+        UI_HELPER_EXECUTOR
+                .getHandler()
+                .post(
+                        () -> {
+                            if (dismissedTaskView instanceof DesktopTaskView desktopTaskView) {
+                                removeDesktopTaskView(desktopTaskView);
+                            } else {
+                                for (int taskId : dismissedTaskView.getTaskIds()) {
+                                    ActivityManagerWrapper.getInstance().removeTask(taskId);
+                                }
+                            }
+                        });
+    }
+
+    private void removeDesktopTaskView(DesktopTaskView desktopTaskView) {
+        if (areMultiDesksFlagsEnabled()) {
+            SystemUiProxy.INSTANCE
                     .get(getContext())
-                    .removeDesktop(mContainer.getDisplay().getDisplayId());
-              } else {
-                for (int taskId : dismissedTaskView.getTaskIds()) {
-                    ActivityManagerWrapper.getInstance().removeTask(taskId);
-                }
-              }
-            });
-  }
+                    .removeDesk(desktopTaskView.getDeskId());
+        } else if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
+            SystemUiProxy.INSTANCE
+                    .get(getContext())
+                    .removeDefaultDeskInDisplay(
+                            mContainer.getDisplay().getDisplayId());
+        }
+    }
 
     protected void onDismissAnimationEnds() {
         AccessibilityManagerCompat.sendTestProtocolEventToTest(getContext(),
@@ -4570,6 +4609,12 @@
         mPendingAnimation = anim;
         mPendingAnimation.addEndListener(isSuccess -> {
             if (isSuccess) {
+                // Remove desktops first, since desks can be empty (so they have no recent tasks),
+                // and closing all tasks on a desk doesn't always necessarily mean that the desk
+                // will be removed. So, there are no guarantees that the below call to
+                // `ActivityManagerWrapper::removeAllRecentTasks()` will be enough.
+                SystemUiProxy.INSTANCE.get(getContext()).removeAllDesks();
+
                 // Remove all the task views now
                 finishRecentsAnimation(true /* toRecents */, false /* shouldPip */, () -> {
                     UI_HELPER_EXECUTOR.getHandler().post(
@@ -4610,8 +4655,9 @@
         }
 
         // Init task grid nav helper with top/bottom id arrays.
-        TaskGridNavHelper taskGridNavHelper = new TaskGridNavHelper(getTopRowIdArray(),
-                getBottomRowIdArray(), mUtils.getLargeTaskViewIds(), mAddDesktopButton != null);
+        TaskGridNavHelper taskGridNavHelper = new TaskGridNavHelper(mUtils.getTopRowIdArray(),
+                mUtils.getBottomRowIdArray(), mUtils.getLargeTaskViewIds(),
+                mAddDesktopButton != null);
 
         // Get current page's task view ID.
         TaskView currentPageTaskView = getCurrentPageTaskView();
@@ -4670,7 +4716,15 @@
     public void dismissTaskView(TaskView taskView, boolean animateTaskView, boolean removeTask) {
         PendingAnimation pa = new PendingAnimation(DISMISS_TASK_DURATION);
         createTaskDismissAnimation(pa, taskView, animateTaskView, removeTask, DISMISS_TASK_DURATION,
-                false /* dismissingForSplitSelection*/);
+                false /* dismissingForSplitSelection*/, false /* isExpressiveDismiss */);
+        runDismissAnimation(pa);
+    }
+
+    protected void expressiveDismissTaskView(TaskView taskView) {
+        PendingAnimation pa = new PendingAnimation(DISMISS_TASK_DURATION);
+        createTaskDismissAnimation(pa, taskView, false /* animateTaskView */, true /* removeTask */,
+                DISMISS_TASK_DURATION, false /* dismissingForSplitSelection*/,
+                true /* isExpressiveDismiss */);
         runDismissAnimation(pa);
     }
 
@@ -4690,7 +4744,7 @@
     private void createDesk(View view) {
         SystemUiProxy.INSTANCE
                 .get(getContext())
-                .createDesktop(mContainer.getDisplay().getDisplayId());
+                .createDesk(mContainer.getDisplay().getDisplayId());
     }
 
     @Override
@@ -4762,9 +4816,8 @@
         }
         mClearAllButton.setContentAlpha(mContentAlpha);
 
-        // TODO(b/389209338): Handle the visibility of the `mAddDesktopButton`.
         if (mAddDesktopButton != null) {
-            mAddDesktopButton.setAlpha(mContentAlpha);
+            mAddDesktopButton.setContentAlpha(mContentAlpha);
         }
         int alphaInt = Math.round(alpha * 255);
         mEmptyMessagePaint.setAlpha(alphaInt);
@@ -4955,6 +5008,10 @@
         }
         setPivotX(mTempPointF.x);
         setPivotY(mTempPointF.y);
+        if (enableGridOnlyOverview()) {
+            runActionOnRemoteHandles(remoteTargetHandle ->
+                    remoteTargetHandle.getTaskViewSimulator().setPivotOverride(mTempPointF));
+        }
     }
 
     /**
@@ -5342,8 +5399,7 @@
                                 clampToProgress(timings.getDesktopTaskScaleInterpolator(), 0f,
                                         timings.getDesktopFadeSplitAnimationEndOffset()));
                     }
-                    builder.addFloat(taskView.getSplitAlphaProperty(),
-                            MULTI_PROPERTY_VALUE, 1f, 0f,
+                    builder.addFloat(taskView, SPLIT_ALPHA, 1f, 0f,
                             clampToProgress(deskTopFadeInterPolator, 0f,
                                     timings.getDesktopFadeSplitAnimationEndOffset()));
                 }
@@ -5401,7 +5457,7 @@
             }
             // Splitting from Overview for fullscreen task
             createTaskDismissAnimation(builder, mSplitHiddenTaskView, true, false, duration,
-                    true /* dismissingForSplitSelection*/);
+                    true /* dismissingForSplitSelection*/, false /* isExpressiveDismiss */);
         } else {
             // Splitting from Home
             TaskView currentPageTaskView = getTaskViewAt(mCurrentPage);
@@ -5409,7 +5465,7 @@
             // display correct animation in split mode
             if (currentPageTaskView instanceof DesktopTaskView) {
                 createTaskDismissAnimation(builder, null, true, false, duration,
-                        true /* dismissingForSplitSelection*/);
+                        true /* dismissingForSplitSelection*/, false /* isExpressiveDismiss */);
             } else {
                 createInitialSplitSelectAnimation(builder);
             }
@@ -5784,6 +5840,9 @@
         if (taskView instanceof DesktopTaskView) {
             anim.play(ObjectAnimator.ofArgb(mContainer.getScrimView(), VIEW_BACKGROUND_COLOR,
                     Color.TRANSPARENT));
+            if (enableDesktopExplodedView()) {
+                anim.play(ObjectAnimator.ofFloat(this, DESK_EXPLODE_PROGRESS, 1f, 0f));
+            }
         }
         DepthController depthController = getDepthController();
         if (depthController != null) {
@@ -6020,7 +6079,7 @@
         // mSyncTransactionApplier doesn't get transferred over
         runActionOnRemoteHandles(remoteTargetHandle -> {
             final TransformParams params = remoteTargetHandle.getTransformParams();
-            if (Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow()) {
+            if (RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
                 params.setHomeBuilderProxy((builder, app, transformParams) -> {
                     mTmpMatrix.setScale(
                             1f, 1f, app.localBounds.exactCenterX(), app.localBounds.exactCenterY());
@@ -6203,9 +6262,7 @@
 
     private int getFirstViewIndex() {
         final View firstView;
-        if (mAddDesktopButton != null) {
-            firstView = mAddDesktopButton;
-        } else if (mShowAsGridLastOnLayout) {
+        if (mShowAsGridLastOnLayout) {
             // For grid Overview, it always start if a large tile (focused task or desktop task) if
             // they exist, otherwise it start with the first task.
             TaskView firstLargeTaskView = mUtils.getFirstLargeTaskView();
@@ -6275,13 +6332,6 @@
             outPageScrolls[clearAllIndex] = clearAllScroll;
         }
 
-        int addDesktopButtonIndex = indexOfChild(mAddDesktopButton);
-        if (addDesktopButtonIndex != -1 && addDesktopButtonIndex < outPageScrolls.length) {
-            outPageScrolls[addDesktopButtonIndex] =
-                    newPageScrolls[addDesktopButtonIndex] + mAddDesktopButton.getScrollAdjustment(
-                            showAsGrid);
-        }
-
         int lastTaskScroll = getLastTaskScroll(clearAllScroll, clearAllWidth);
         getTaskViews().forEachWithIndexInParent((index, taskView) -> {
             float scrollDiff = taskView.getScrollAdjustment(showAsGrid);
@@ -6296,6 +6346,14 @@
                         "getPageScrolls - outPageScrolls[" + index + "]: " + outPageScrolls[index]);
             }
         });
+
+        int addDesktopButtonIndex = indexOfChild(mAddDesktopButton);
+        if (addDesktopButtonIndex >= 0 && addDesktopButtonIndex < outPageScrolls.length) {
+            int firstViewIndex = getFirstViewIndex();
+            if (firstViewIndex >= 0 && firstViewIndex < outPageScrolls.length) {
+                outPageScrolls[addDesktopButtonIndex] = outPageScrolls[firstViewIndex];
+            }
+        }
         if (DEBUG) {
             Log.d(TAG, "getPageScrolls - clearAllScroll: " + clearAllScroll);
         }
@@ -6329,6 +6387,11 @@
         return mClearAllButton;
     }
 
+    @Nullable
+    public AddDesktopButton getAddDeskButton() {
+        return mAddDesktopButton;
+    }
+
     /**
      * @return How many pixels the running task is offset on the currently laid out dominant axis.
      */
@@ -6399,7 +6462,8 @@
      * Returns how many pixels the page is offset from its scroll position.
      */
     private int getOffsetFromScrollPosition(int pageIndex) {
-        return getOffsetFromScrollPosition(pageIndex, getTopRowIdArray(), getBottomRowIdArray());
+        return getOffsetFromScrollPosition(pageIndex, mUtils.getTopRowIdArray(),
+                mUtils.getBottomRowIdArray());
     }
 
     private int getOffsetFromScrollPosition(
@@ -6698,7 +6762,7 @@
                 .displayOverviewTasksAsGrid(mContainer.getDeviceProfile()));
     }
 
-    private boolean showAsFullscreen() {
+    protected boolean showAsFullscreen() {
         return mOverviewFullscreenEnabled
                 && mCurrentGestureEndTarget != GestureState.GestureEndTarget.RECENTS;
     }
@@ -6913,6 +6977,58 @@
         // TODO: b/389209338 - update the AddDesktopButton's visibility on this.
     }
 
+    @Override
+    public void onDeskAdded(int displayId, int deskId) {
+        // Ignore desk changes that don't belong to this display.
+        if (displayId != mContainer.getDisplay().getDisplayId()) {
+            return;
+        }
+
+        if (mUtils.getDesktopTaskViewForDeskId(deskId) != null) {
+            Log.e(TAG, "A task view for this desk has already been added.");
+            return;
+        }
+
+        // We assume that a newly added desk is always empty and gets added to the left of the
+        // `AddNewDesktopButton`.
+        DesktopTaskView desktopTaskView =
+                (DesktopTaskView) getTaskViewFromPool(TaskViewType.DESKTOP);
+        desktopTaskView.bind(new DesktopTask(deskId, new ArrayList<>()),
+                mOrientationState, mTaskOverlayFactory);
+
+        Objects.requireNonNull(mAddDesktopButton);
+        final int insertionIndex = 1 + indexOfChild(mAddDesktopButton);
+        addView(desktopTaskView, insertionIndex);
+
+        updateTaskSize();
+        updateChildTaskOrientations();
+
+        // TODO: b/401002178 - Recalculate the new current page such that the addition of the new
+        //  desk does not result in a change in the current scroll page.
+    }
+
+    @Override
+    public void onDeskRemoved(int displayId, int deskId) {
+        // Ignore desk changes that don't belong to this display.
+        if (displayId != mContainer.getDisplay().getDisplayId()) {
+            return;
+        }
+
+        // We need to distinguish between desk removals that are triggered from outside of overview
+        // vs. the ones that were initiated from overview by dismissing the corresponding desktop
+        // task view.
+        var taskView = mUtils.getDesktopTaskViewForDeskId(deskId);
+        if (taskView != null) {
+            dismissTaskView(taskView, true, true);
+        }
+    }
+
+    @Override
+    public void onActiveDeskChanged(int displayId, int newActiveDesk, int oldActiveDesk) {
+        // TODO: b/400870600 - We may need to add code here to special case when an empty desk gets
+        // activated, since `RemoteDesktopLaunchTransitionRunner` doesn't always get triggered.
+    }
+
     /** Get the color used for foreground scrimming the RecentsView for sharing. */
     public static int getForegroundScrimDimColor(Context context) {
         return context.getColor(R.color.overview_foreground_scrim_color);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
index 67318ac..29601ef 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
@@ -22,12 +22,17 @@
 import androidx.core.view.children
 import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
 import com.android.launcher3.Flags.enableSeparateExternalDisplayTasks
+import com.android.launcher3.statehandlers.DesktopVisibilityController
+import com.android.launcher3.statehandlers.DesktopVisibilityController.Companion.INACTIVE_DESK_ID
 import com.android.launcher3.util.IntArray
+import com.android.quickstep.util.DesksUtils.Companion.areMultiDesksFlagsEnabled
+import com.android.quickstep.util.DesktopTask
 import com.android.quickstep.util.GroupTask
 import com.android.quickstep.util.isExternalDisplay
 import com.android.quickstep.views.RecentsView.RUNNING_TASK_ATTACH_ALPHA
 import com.android.systemui.shared.recents.model.ThumbnailData
 import java.util.function.BiConsumer
+import kotlin.reflect.KMutableProperty1
 
 /**
  * Helper class for [RecentsView]. This util class contains refactored and extracted functions from
@@ -51,7 +56,12 @@
      * @return Sorted list of GroupTasks to be used in the RecentsView.
      */
     fun sortDesktopTasksToFront(tasks: List<GroupTask>): List<GroupTask> {
-        val (desktopTasks, otherTasks) = tasks.partition { it.taskViewType == TaskViewType.DESKTOP }
+        var (desktopTasks, otherTasks) = tasks.partition { it.taskViewType == TaskViewType.DESKTOP }
+        if (areMultiDesksFlagsEnabled()) {
+            // Desk IDs of newer desks are larger than those of older desks, hence we can use them
+            // to sort desks from old to new.
+            desktopTasks = desktopTasks.sortedBy { (it as DesktopTask).deskId }
+        }
         return otherTasks + desktopTasks
     }
 
@@ -82,6 +92,25 @@
     /** Returns a list of all large TaskView Ids from [TaskView]s */
     fun getLargeTaskViewIds(): List<Int> = taskViews.filter { it.isLargeTile }.map { it.taskViewId }
 
+    /** Returns a list of all large TaskViews [TaskView]s */
+    fun getLargeTaskViews(): List<TaskView> = taskViews.filter { it.isLargeTile }
+
+    /** Returns all the TaskViews in the top row, without the focused task */
+    fun getTopRowTaskViews(): List<TaskView> =
+        taskViews.filter { recentsView.mTopRowIdSet.contains(it.taskViewId) }
+
+    /** Returns all the task Ids in the top row, without the focused task */
+    fun getTopRowIdArray(): IntArray = getTopRowTaskViews().map { it.taskViewId }.toIntArray()
+
+    /** Returns all the TaskViews in the bottom row, without the focused task */
+    fun getBottomRowTaskViews(): List<TaskView> =
+        taskViews.filter { !recentsView.mTopRowIdSet.contains(it.taskViewId) && !it.isLargeTile }
+
+    /** Returns all the task Ids in the bottom row, without the focused task */
+    fun getBottomRowIdArray(): IntArray = getBottomRowTaskViews().map { it.taskViewId }.toIntArray()
+
+    private fun List<Int>.toIntArray() = IntArray(size).apply { this@toIntArray.forEach(::add) }
+
     /** Counts [TaskView]s that are large tiles. */
     fun getLargeTileCount(): Int = taskViews.count { it.isLargeTile }
 
@@ -94,6 +123,22 @@
             it.isLargeTile && !(recentsView.isSplitSelectionActive && it is DesktopTaskView)
         }
 
+    /**
+     * Returns the [DesktopTaskView] that matches the given [deskId], or null if it doesn't exist.
+     */
+    fun getDesktopTaskViewForDeskId(deskId: Int): DesktopTaskView? {
+        if (deskId == INACTIVE_DESK_ID) {
+            return null
+        }
+        return taskViews.firstOrNull { it is DesktopTaskView && it.deskId == deskId }
+            as? DesktopTaskView
+    }
+
+    /** Returns the active desk ID of the display that contains the [recentsView] instance. */
+    fun getActiveDeskIdOnThisDisplay(): Int =
+        DesktopVisibilityController.INSTANCE.get(recentsView.context)
+            .getActiveDeskId(recentsView.mContainer.display.displayId)
+
     /** Returns the expected focus task. */
     fun getFirstNonDesktopTaskView(): TaskView? =
         if (enableLargeDesktopWindowingTile()) taskViews.firstOrNull { it !is DesktopTaskView }
@@ -120,6 +165,17 @@
     private fun getDeviceProfile() = (recentsView.mContainer as RecentsViewContainer).deviceProfile
 
     fun getRunningTaskExpectedIndex(runningTaskView: TaskView): Int {
+        if (areMultiDesksFlagsEnabled() && runningTaskView is DesktopTaskView) {
+            // Use the [deskId] to keep desks in the order of their creation, as a newer desk
+            // always has a larger [deskId] than the older desks.
+            val desktopTaskView =
+                taskViews.firstOrNull {
+                    it is DesktopTaskView &&
+                        it.deskId != INACTIVE_DESK_ID &&
+                        it.deskId <= runningTaskView.deskId
+                }
+            if (desktopTaskView != null) return recentsView.indexOfChild(desktopTaskView)
+        }
         val firstTaskViewIndex = recentsView.indexOfChild(getFirstTaskView())
         return if (getDeviceProfile().isTablet) {
             var index = firstTaskViewIndex
@@ -265,8 +321,8 @@
             return
         }
         getRowRect(getFirstLargeTaskView(), getLastLargeTaskView(), outTaskViewRowRect)
-        getRowRect(recentsView.getTopRowIdArray(), outTopRowRect)
-        getRowRect(recentsView.getBottomRowIdArray(), outBottomRowRect)
+        getRowRect(getTopRowIdArray(), outTopRowRect)
+        getRowRect(getBottomRowIdArray(), outBottomRowRect)
 
         // Expand large tile Rect to include space between top/bottom row.
         val nonEmptyRowRect =
@@ -305,16 +361,19 @@
         }
 
     companion object {
-        @JvmField
-        val DESK_EXPLODE_PROGRESS =
-            object : FloatProperty<RecentsView<*, *>>("deskExplodeProgress") {
-                override fun setValue(recentsView: RecentsView<*, *>, value: Float) {
-                    recentsView.mUtils.deskExplodeProgress = value
-                }
+        class RecentsViewFloatProperty(
+            private val utilsProperty: KMutableProperty1<RecentsViewUtils, Float>
+        ) : FloatProperty<RecentsView<*, *>>(utilsProperty.name) {
+            override fun get(recentsView: RecentsView<*, *>): Float =
+                utilsProperty.get(recentsView.mUtils)
 
-                override fun get(recentsView: RecentsView<*, *>) =
-                    recentsView.mUtils.deskExplodeProgress
+            override fun setValue(recentsView: RecentsView<*, *>, value: Float) {
+                utilsProperty.set(recentsView.mUtils, value)
             }
+        }
+
+        @JvmField
+        val DESK_EXPLODE_PROGRESS = RecentsViewFloatProperty(RecentsViewUtils::deskExplodeProgress)
 
         val TEMP_RECT = Rect()
     }
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
index 0d80f6a..4cf1eae 100644
--- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -39,10 +39,11 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.quickstep.util.AnimUtils;
 import com.android.quickstep.util.SplitSelectStateController;
+import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
 
 /**
  * A rounded rectangular component containing a single TextView.
@@ -129,6 +130,7 @@
         cancelTextView.setVisibility(VISIBLE);
         cancelTextView.setOnClickListener((v) -> exitSplitSelection());
         instructionTextView.setText(R.string.toast_contextual_split_select_app);
+        TypefaceUtils.setTypeface(instructionTextView, FontFamily.GSF_BODY_MEDIUM);
 
         // After layout, expand touch target of cancel button to meet minimum a11y measurements.
         post(() -> {
diff --git a/quickstep/src/com/android/quickstep/views/TaskContainer.kt b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
index 2b9d036..ec6d1c4 100644
--- a/quickstep/src/com/android/quickstep/views/TaskContainer.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
@@ -18,6 +18,7 @@
 
 import android.graphics.Bitmap
 import android.graphics.Matrix
+import android.util.Log
 import android.view.View
 import android.view.View.OnClickListener
 import com.android.launcher3.Flags.enableRefactorTaskThumbnail
@@ -26,8 +27,10 @@
 import com.android.launcher3.util.TransformingTouchDelegate
 import com.android.quickstep.TaskOverlayFactory
 import com.android.quickstep.ViewUtils.addAccessibleChildToList
+import com.android.quickstep.recents.domain.usecase.ThumbnailPosition
 import com.android.quickstep.recents.ui.mapper.TaskUiStateMapper
 import com.android.quickstep.recents.ui.viewmodel.TaskData
+import com.android.quickstep.task.thumbnail.TaskContentView
 import com.android.quickstep.task.thumbnail.TaskThumbnailView
 import com.android.systemui.shared.recents.model.Task
 import com.android.systemui.shared.recents.model.ThumbnailData
@@ -36,6 +39,7 @@
 class TaskContainer(
     val taskView: TaskView,
     val task: Task,
+    val taskContentView: TaskContentView,
     val snapshotView: View,
     val iconView: TaskViewIcon,
     /**
@@ -52,6 +56,8 @@
     taskOverlayFactory: TaskOverlayFactory,
 ) {
     val overlay: TaskOverlayFactory.TaskOverlay<*> = taskOverlayFactory.createOverlay(this)
+    var thumbnailPosition: ThumbnailPosition? = null
+    private var overlayEnabledStatus = false
 
     init {
         if (enableRefactorTaskThumbnail()) {
@@ -99,14 +105,13 @@
         if (!enableRefactorTaskThumbnail()) {
             thumbnailViewDeprecated.bind(task, overlay, taskView)
         }
-        overlay.init()
     }
 
     fun destroy() {
         digitalWellBeingToast?.destroy()
-        snapshotView.scaleX = 1f
-        snapshotView.scaleY = 1f
-        overlay.destroy()
+        taskContentView.scaleX = 1f
+        taskContentView.scaleY = 1f
+        overlay.reset()
         if (enableRefactorTaskThumbnail()) {
             isThumbnailValid = false
             thumbnailData = null
@@ -122,6 +127,34 @@
         }
     }
 
+    fun setOverlayEnabled(enabled: Boolean, thumbnailPosition: ThumbnailPosition?) {
+        if (enableRefactorTaskThumbnail()) {
+            if (overlayEnabledStatus != enabled || this.thumbnailPosition != thumbnailPosition) {
+                overlayEnabledStatus = enabled
+
+                refreshOverlay(thumbnailPosition)
+            }
+        }
+    }
+
+    fun refreshOverlay(thumbnailPosition: ThumbnailPosition?) {
+        this.thumbnailPosition = thumbnailPosition
+        when {
+            !overlayEnabledStatus -> overlay.reset()
+            thumbnailPosition == null -> {
+                Log.e(TAG, "Thumbnail position was null during overlay refresh", Exception())
+                overlay.reset()
+            }
+            else ->
+                overlay.initOverlay(
+                    task,
+                    thumbnailData?.thumbnail,
+                    thumbnailPosition.matrix,
+                    thumbnailPosition.isRotated,
+                )
+        }
+    }
+
     fun addChildForAccessibility(outChildren: ArrayList<View>) {
         addAccessibleChildToList(iconView.asView(), outChildren)
         addAccessibleChildToList(snapshotView, outChildren)
@@ -136,16 +169,13 @@
         hasHeader: Boolean,
         clickCloseListener: OnClickListener?,
     ) {
-        thumbnailView.setState(
-            TaskUiStateMapper.toTaskThumbnailUiState(
-                state,
-                liveTile,
-                hasHeader,
-                clickCloseListener,
-            ),
+        taskContentView.setState(
+            TaskUiStateMapper.toTaskHeaderState(state, hasHeader, clickCloseListener),
+            TaskUiStateMapper.toTaskThumbnailUiState(state, liveTile),
             state?.taskId,
         )
         thumbnailData = if (state is TaskData.Data) state.thumbnailData else null
+        overlay.setThumbnailState(thumbnailData)
     }
 
     fun updateTintAmount(tintAmount: Float) {
@@ -186,4 +216,8 @@
     fun updateThumbnailMatrix(matrix: Matrix) {
         thumbnailView.setImageMatrix(matrix)
     }
+
+    companion object {
+        const val TAG = "TaskContainer"
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailViewHeader.kt b/quickstep/src/com/android/quickstep/views/TaskHeaderView.kt
similarity index 66%
rename from quickstep/src/com/android/quickstep/views/TaskThumbnailViewHeader.kt
rename to quickstep/src/com/android/quickstep/views/TaskHeaderView.kt
index 9a8805b..0427402 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailViewHeader.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskHeaderView.kt
@@ -22,19 +22,29 @@
 import android.widget.ImageButton
 import android.widget.ImageView
 import android.widget.TextView
+import androidx.core.view.isGone
 import com.android.launcher3.R
-import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.ThumbnailHeader
+import com.android.quickstep.task.thumbnail.TaskHeaderUiState
 
-class TaskThumbnailViewHeader
-@JvmOverloads
-constructor(context: Context, attrs: AttributeSet? = null) : FrameLayout(context, attrs) {
+class TaskHeaderView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
+    FrameLayout(context, attrs) {
 
     private val headerTitleView: TextView by lazy { findViewById(R.id.header_app_title) }
     private val headerIconView: ImageView by lazy { findViewById(R.id.header_app_icon) }
     private val headerCloseButton: ImageButton by lazy { findViewById(R.id.header_close_button) }
 
-    fun setHeader(header: ThumbnailHeader) {
-        headerTitleView.setText(header.title)
+    fun setState(taskHeaderState: TaskHeaderUiState) {
+        when (taskHeaderState) {
+            is TaskHeaderUiState.ShowHeader -> {
+                setHeader(taskHeaderState.header)
+                isGone = false
+            }
+            TaskHeaderUiState.HideHeader -> isGone = true
+        }
+    }
+
+    private fun setHeader(header: TaskHeaderUiState.ThumbnailHeader) {
+        headerTitleView.text = header.title
         headerIconView.setImageDrawable(header.icon)
         headerCloseButton.setOnClickListener(header.clickCloseListener)
     }
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
deleted file mode 100644
index 0b3eb75..0000000
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ /dev/null
@@ -1,429 +0,0 @@
-/*
- * Copyright (C) 2018 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;
-import static com.android.launcher3.Flags.enableOverviewIconMenu;
-import static com.android.launcher3.Flags.enableRefactorTaskThumbnail;
-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;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.Outline;
-import android.graphics.Rect;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.RectShape;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.annotation.Nullable;
-
-import com.android.app.animation.Interpolators;
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
-import com.android.launcher3.popup.SystemShortcut;
-import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.TaskOverlayFactory;
-import com.android.quickstep.TaskUtils;
-import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
-import com.android.quickstep.util.TaskCornerRadius;
-
-/**
- * Contains options for a recent task when long-pressing its icon.
- */
-public class TaskMenuView extends AbstractFloatingView {
-
-    private static final Rect sTempRect = new Rect();
-
-    private static final int REVEAL_OPEN_DURATION = enableOverviewIconMenu() ? 417 : 150;
-    private static final int REVEAL_CLOSE_DURATION = enableOverviewIconMenu() ? 333 : 100;
-
-    private RecentsViewContainer mContainer;
-    private TextView mTaskName;
-    @Nullable
-    private AnimatorSet mOpenCloseAnimator;
-    @Nullable
-    private ValueAnimator mRevealAnimator;
-    @Nullable private Runnable mOnClosingStartCallback;
-    private TaskView mTaskView;
-    private TaskContainer mTaskContainer;
-    private LinearLayout mOptionLayout;
-    private float mMenuTranslationYBeforeOpen;
-    private float mMenuTranslationXBeforeOpen;
-
-    public TaskMenuView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public TaskMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        mContainer = RecentsViewContainer.containerFromContext(context);
-        setClipToOutline(true);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mTaskName = findViewById(R.id.task_name);
-        mOptionLayout = findViewById(R.id.menu_option_layout);
-    }
-
-    @Override
-    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            BaseDragLayer dl = mContainer.getDragLayer();
-            if (!dl.isEventOverView(this, ev)) {
-                // TODO: log this once we have a new container type for it?
-                close(true);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    protected void handleClose(boolean animate) {
-        animateClose();
-    }
-
-    @Override
-    protected boolean isOfType(int type) {
-        return (type & TYPE_TASK_MENU) != 0;
-    }
-
-    @Override
-    public ViewOutlineProvider getOutlineProvider() {
-        return new ViewOutlineProvider() {
-            @Override
-            public void getOutline(View view, Outline outline) {
-                outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(),
-                        TaskCornerRadius.get(view.getContext()));
-            }
-        };
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (!(enableOverviewIconMenu()
-                && ((RecentsView) mContainer.getOverviewPanel()).isOnGridBottomRow(mTaskView))) {
-            // TODO(b/326952853): Cap menu height for grid bottom row in a way that doesn't break
-            // additionalTranslationY.
-            int maxMenuHeight = calculateMaxHeight();
-            if (MeasureSpec.getSize(heightMeasureSpec) > maxMenuHeight) {
-                heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxMenuHeight, MeasureSpec.AT_MOST);
-            }
-        }
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    public void onRotationChanged() {
-        if (mOpenCloseAnimator != null && mOpenCloseAnimator.isRunning()) {
-            mOpenCloseAnimator.end();
-        }
-        if (mIsOpen) {
-            mOptionLayout.removeAllViews();
-            if (enableOverviewIconMenu() || !populateAndLayoutMenu()) {
-                close(false);
-            }
-        }
-    }
-
-    /**
-     * Show a task menu for the given taskContainer.
-     */
-    public static boolean showForTask(TaskContainer taskContainer,
-            @Nullable Runnable onClosingStartCallback) {
-        RecentsViewContainer container = RecentsViewContainer.containerFromContext(
-                taskContainer.getTaskView().getContext());
-        final TaskMenuView taskMenuView = (TaskMenuView) container.getLayoutInflater().inflate(
-                        R.layout.task_menu, container.getDragLayer(), false);
-        taskMenuView.setOnClosingStartCallback(onClosingStartCallback);
-        return taskMenuView.populateAndShowForTask(taskContainer);
-    }
-
-    /**
-     * Show a task menu for the given taskContainer.
-     */
-    public static boolean showForTask(TaskContainer taskContainer) {
-        return showForTask(taskContainer, null);
-    }
-
-    private boolean populateAndShowForTask(TaskContainer taskContainer) {
-        if (isAttachedToWindow()) {
-            return false;
-        }
-        mContainer.getDragLayer().addView(this);
-        mTaskView = taskContainer.getTaskView();
-        mTaskContainer = taskContainer;
-        if (!populateAndLayoutMenu()) {
-            return false;
-        }
-        post(this::animateOpen);
-        return true;
-    }
-
-    /** @return true if successfully able to populate task view menu, false otherwise */
-    private boolean populateAndLayoutMenu() {
-        addMenuOptions(mTaskContainer);
-        orientAroundTaskView(mTaskContainer);
-        return true;
-    }
-
-    private void addMenuOptions(TaskContainer taskContainer) {
-        if (enableOverviewIconMenu()) {
-            removeView(mTaskName);
-        } else {
-            mTaskName.setText(TaskUtils.getTitle(getContext(), taskContainer.getTask()));
-            mTaskName.setOnClickListener(v -> close(true));
-        }
-        TaskOverlayFactory.getEnabledShortcuts(mTaskView, taskContainer)
-                .forEach(this::addMenuOption);
-    }
-
-    private void addMenuOption(SystemShortcut menuOption) {
-        LinearLayout menuOptionView = (LinearLayout) mContainer.getLayoutInflater().inflate(
-                R.layout.task_view_menu_option, this, false);
-        if (enableOverviewIconMenu()) {
-            ((GradientDrawable) menuOptionView.getBackground()).setCornerRadius(0);
-        }
-        menuOption.setIconAndLabelFor(
-                menuOptionView.findViewById(R.id.icon), menuOptionView.findViewById(R.id.text));
-        LayoutParams lp = (LayoutParams) menuOptionView.getLayoutParams();
-        mTaskView.getPagedOrientationHandler().setLayoutParamsForTaskMenuOptionItem(lp,
-                menuOptionView, mContainer.getDeviceProfile());
-        // Set an onClick listener on each menu option. The onClick method is responsible for
-        // ending LiveTile mode on the thumbnail if needed.
-        menuOptionView.setOnClickListener(menuOption::onClick);
-        mOptionLayout.addView(menuOptionView);
-    }
-
-    private void orientAroundTaskView(TaskContainer taskContainer) {
-        RecentsView recentsView = mContainer.getOverviewPanel();
-        RecentsPagedOrientationHandler orientationHandler =
-                recentsView.getPagedOrientationHandler();
-        measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
-
-        // Get Position
-        DeviceProfile deviceProfile = mContainer.getDeviceProfile();
-        mContainer.getDragLayer().getDescendantRectRelativeToSelf(
-                enableOverviewIconMenu()
-                        ? getIconView().findViewById(R.id.icon_view_menu_anchor)
-                        : taskContainer.getSnapshotView(),
-                sTempRect);
-        Rect insets = mContainer.getDragLayer().getInsets();
-        BaseDragLayer.LayoutParams params = (BaseDragLayer.LayoutParams) getLayoutParams();
-        params.width = orientationHandler.getTaskMenuWidth(
-                taskContainer.getSnapshotView(), deviceProfile,
-                taskContainer.getStagePosition());
-        // Gravity set to Left instead of Start as sTempRect.left measures Left distance not Start
-        params.gravity = Gravity.LEFT;
-        setLayoutParams(params);
-        setScaleX(mTaskView.getScaleX());
-        setScaleY(mTaskView.getScaleY());
-
-        // Set divider spacing
-        ShapeDrawable divider = new ShapeDrawable(new RectShape());
-        divider.getPaint().setColor(getResources().getColor(android.R.color.transparent));
-        int dividerSpacing = (int) getResources().getDimension(R.dimen.task_menu_spacing);
-        mOptionLayout.setShowDividers(
-                enableOverviewIconMenu() ? SHOW_DIVIDER_NONE : SHOW_DIVIDER_MIDDLE);
-
-        orientationHandler.setTaskOptionsMenuLayoutOrientation(
-                deviceProfile, mOptionLayout, dividerSpacing, divider);
-        float thumbnailAlignedX = sTempRect.left - insets.left;
-        float thumbnailAlignedY = sTempRect.top - insets.top;
-
-        // Changing pivot to make computations easier
-        // NOTE: Changing the pivots means the rotated view gets rotated about the new pivots set,
-        // which would render the X and Y position set here incorrect
-        setPivotX(0);
-        setPivotY(0);
-        setRotation(orientationHandler.getDegreesRotated());
-
-        if (enableOverviewIconMenu()) {
-            setTranslationX(thumbnailAlignedX);
-            setTranslationY(thumbnailAlignedY);
-        } else {
-            // Margin that insets the menuView inside the taskView
-            float taskInsetMargin = getResources().getDimension(R.dimen.task_card_margin);
-            setTranslationX(orientationHandler.getTaskMenuX(thumbnailAlignedX,
-                    mTaskContainer.getSnapshotView(), deviceProfile, taskInsetMargin,
-                    getIconView()));
-            setTranslationY(orientationHandler.getTaskMenuY(
-                    thumbnailAlignedY, mTaskContainer.getSnapshotView(),
-                    mTaskContainer.getStagePosition(), this, taskInsetMargin,
-                    getIconView()));
-        }
-    }
-
-    private void animateOpen() {
-        mMenuTranslationYBeforeOpen = getTranslationY();
-        mMenuTranslationXBeforeOpen = getTranslationX();
-        animateOpenOrClosed(false);
-        mIsOpen = true;
-    }
-
-    private View getIconView() {
-        return mTaskContainer.getIconView().asView();
-    }
-
-    private void animateClose() {
-        animateOpenOrClosed(true);
-    }
-
-    private void animateOpenOrClosed(boolean closing) {
-        if (mOpenCloseAnimator != null && mOpenCloseAnimator.isRunning()) {
-            mOpenCloseAnimator.cancel();
-        }
-        mOpenCloseAnimator = new AnimatorSet();
-        // If we're opening, we just start from the beginning as a new `TaskMenuView` is created
-        // each time we do the open animation so there will never be a partial value here.
-        float revealAnimationStartProgress = 0f;
-        if (closing && mRevealAnimator != null) {
-            revealAnimationStartProgress = 1f - mRevealAnimator.getAnimatedFraction();
-        }
-        mRevealAnimator = createOpenCloseOutlineProvider()
-                .createRevealAnimator(this, closing, revealAnimationStartProgress);
-        mRevealAnimator.setInterpolator(enableOverviewIconMenu() ? Interpolators.EMPHASIZED
-                : Interpolators.DECELERATE);
-        AnimatorSet.Builder openCloseAnimatorBuilder = mOpenCloseAnimator.play(mRevealAnimator);
-        if (enableOverviewIconMenu()) {
-            IconAppChipView iconAppChip = (IconAppChipView) mTaskContainer.getIconView().asView();
-
-            float additionalTranslationY = 0;
-            if (((RecentsView) mContainer.getOverviewPanel()).isOnGridBottomRow(mTaskView)) {
-                // Animate menu up for enough room to display full menu when task on bottom row.
-                float menuBottom = getHeight() + mMenuTranslationYBeforeOpen;
-                float taskBottom = mTaskView.getHeight() + mTaskView.getPersistentTranslationY();
-                float taskbarTop = mContainer.getDeviceProfile().heightPx
-                        - mContainer.getDeviceProfile().getOverviewActionsClaimedSpaceBelow();
-                float midpoint = (taskBottom + taskbarTop) / 2f;
-                additionalTranslationY = -Math.max(menuBottom - midpoint, 0);
-            }
-            ObjectAnimator translationYAnim = ObjectAnimator.ofFloat(this, TRANSLATION_Y,
-                    closing ? mMenuTranslationYBeforeOpen
-                            : mMenuTranslationYBeforeOpen + additionalTranslationY);
-            translationYAnim.setInterpolator(EMPHASIZED);
-            openCloseAnimatorBuilder.with(translationYAnim);
-
-            ObjectAnimator menuTranslationYAnim = ObjectAnimator.ofFloat(
-                    iconAppChip.getMenuTranslationY(),
-                    MULTI_PROPERTY_VALUE, closing ? 0 : additionalTranslationY);
-            menuTranslationYAnim.setInterpolator(EMPHASIZED);
-            openCloseAnimatorBuilder.with(menuTranslationYAnim);
-
-            float additionalTranslationX = 0;
-            if (mContainer.getDeviceProfile().isLandscape
-                    && mTaskContainer.getStagePosition() == STAGE_POSITION_BOTTOM_OR_RIGHT) {
-                // Animate menu and icon when split task would display off the side of the screen.
-                additionalTranslationX = Math.max(
-                        getTranslationX() + getWidth() - (mContainer.getDeviceProfile().widthPx
-                                - getResources().getDimensionPixelSize(
-                                R.dimen.task_menu_edge_padding) * 2), 0);
-            }
-
-            ObjectAnimator translationXAnim = ObjectAnimator.ofFloat(this, TRANSLATION_X,
-                    closing ? mMenuTranslationXBeforeOpen
-                            : mMenuTranslationXBeforeOpen - additionalTranslationX);
-            translationXAnim.setInterpolator(EMPHASIZED);
-            openCloseAnimatorBuilder.with(translationXAnim);
-
-            ObjectAnimator menuTranslationXAnim = ObjectAnimator.ofFloat(
-                    iconAppChip.getMenuTranslationX(),
-                    MULTI_PROPERTY_VALUE, closing ? 0 : -additionalTranslationX);
-            menuTranslationXAnim.setInterpolator(EMPHASIZED);
-            openCloseAnimatorBuilder.with(menuTranslationXAnim);
-        }
-        openCloseAnimatorBuilder.with(ObjectAnimator.ofFloat(this, ALPHA, closing ? 0 : 1));
-        if (enableRefactorTaskThumbnail()) {
-            mRevealAnimator.addUpdateListener(animation -> {
-                float animatedFraction = animation.getAnimatedFraction();
-                float openProgress = closing ? (1 - animatedFraction) : animatedFraction;
-                mTaskContainer.updateMenuOpenProgress(openProgress);
-            });
-        } else {
-            openCloseAnimatorBuilder.with(ObjectAnimator.ofFloat(
-                    mTaskContainer.getThumbnailViewDeprecated(), DIM_ALPHA,
-                    closing ? 0 : TaskView.MAX_PAGE_SCRIM_ALPHA));
-        }
-        mOpenCloseAnimator.addListener(new AnimationSuccessListener() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                setVisibility(VISIBLE);
-                if (closing && mOnClosingStartCallback != null) {
-                    mOnClosingStartCallback.run();
-                }
-            }
-
-            @Override
-            public void onAnimationSuccess(Animator animator) {
-                if (closing) {
-                    closeComplete();
-                }
-            }
-        });
-        mOpenCloseAnimator.setDuration(closing ? REVEAL_CLOSE_DURATION: REVEAL_OPEN_DURATION);
-        mOpenCloseAnimator.start();
-    }
-
-    private void closeComplete() {
-        mIsOpen = false;
-        mContainer.getDragLayer().removeView(this);
-        mRevealAnimator = null;
-    }
-
-    private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
-        float radius = TaskCornerRadius.get(mContext);
-        Rect fromRect = new Rect(
-                enableOverviewIconMenu() && isLayoutRtl() ? getWidth() : 0,
-                0,
-                enableOverviewIconMenu() && !isLayoutRtl() ? 0 : getWidth(),
-                0);
-        Rect toRect = new Rect(0, 0, getWidth(), getHeight());
-        return new RoundedRectRevealOutlineProvider(radius, radius, fromRect, toRect);
-    }
-
-    /**
-     * Calculates max height based on how much space we have available.
-     * If not enough space then the view will scroll. The maximum menu size will sit inside the task
-     * with a margin on the top and bottom.
-     */
-    private int calculateMaxHeight() {
-        float taskInsetMargin = getResources().getDimension(R.dimen.task_card_margin);
-        return mTaskView.getPagedOrientationHandler().getTaskMenuHeight(taskInsetMargin,
-                mContainer.getDeviceProfile(), getTranslationX(), getTranslationY());
-    }
-
-    private void setOnClosingStartCallback(Runnable onClosingStartCallback) {
-        mOnClosingStartCallback = onClosingStartCallback;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.kt b/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
new file mode 100644
index 0000000..6bc0666
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2018 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.AnimatorSet
+import android.animation.ObjectAnimator
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.Outline
+import android.graphics.Rect
+import android.graphics.drawable.ShapeDrawable
+import android.graphics.drawable.shapes.RectShape
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewOutlineProvider
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.core.content.res.ResourcesCompat
+import com.android.app.animation.Interpolators
+import com.android.launcher3.AbstractFloatingView
+import com.android.launcher3.Flags.enableOverviewIconMenu
+import com.android.launcher3.Flags.enableRefactorTaskThumbnail
+import com.android.launcher3.R
+import com.android.launcher3.anim.AnimationSuccessListener
+import com.android.launcher3.anim.RoundedRectRevealOutlineProvider
+import com.android.launcher3.popup.SystemShortcut
+import com.android.launcher3.util.MultiPropertyFactory
+import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.launcher3.views.BaseDragLayer
+import com.android.quickstep.TaskOverlayFactory
+import com.android.quickstep.TaskUtils
+import com.android.quickstep.util.TaskCornerRadius
+import java.util.function.Consumer
+import kotlin.math.max
+
+/** Contains options for a recent task when long-pressing its icon. */
+class TaskMenuView
+@JvmOverloads
+constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int = 0) :
+    AbstractFloatingView(context, attrs, defStyleAttr) {
+    private val recentsViewContainer: RecentsViewContainer =
+        RecentsViewContainer.containerFromContext(context)
+    private val tempRect = Rect()
+    private val taskName: TextView by lazy { findViewById(R.id.task_name) }
+    private val optionLayout: LinearLayout by lazy { findViewById(R.id.menu_option_layout) }
+    private var openCloseAnimator: AnimatorSet? = null
+    private var revealAnimator: ValueAnimator? = null
+    private var onClosingStartCallback: Runnable? = null
+    private lateinit var taskView: TaskView
+    private lateinit var taskContainer: TaskContainer
+    private var menuTranslationXBeforeOpen = 0f
+    private var menuTranslationYBeforeOpen = 0f
+
+    // Spaced claimed below Overview (taskbar and insets)
+    private val taskbarTop by lazy {
+        recentsViewContainer.deviceProfile.heightPx -
+            recentsViewContainer.deviceProfile.overviewActionsClaimedSpaceBelow
+    }
+    private val minMenuTop by lazy { taskContainer.iconView.height.toFloat() }
+    // TODO(b/401476868): Replace overviewRowSpacing with correct margin to the taskbarTop.
+    private val maxMenuBottom by lazy {
+        (taskbarTop - recentsViewContainer.deviceProfile.overviewRowSpacing).toFloat()
+    }
+
+    init {
+        clipToOutline = true
+    }
+
+    override fun onControllerInterceptTouchEvent(ev: MotionEvent): Boolean {
+        if (ev.action == MotionEvent.ACTION_DOWN) {
+            if (!recentsViewContainer.dragLayer.isEventOverView(this, ev)) {
+                // TODO: log this once we have a new container type for it?
+                animateOpenOrClosed(true)
+                return true
+            }
+        }
+        return false
+    }
+
+    override fun handleClose(animate: Boolean) {
+        animateOpenOrClosed(true, animated = false)
+    }
+
+    override fun isOfType(type: Int): Boolean = (type and TYPE_TASK_MENU) != 0
+
+    override fun getOutlineProvider(): ViewOutlineProvider =
+        object : ViewOutlineProvider() {
+            override fun getOutline(view: View, outline: Outline) {
+                outline.setRoundRect(
+                    0,
+                    0,
+                    view.width,
+                    view.height,
+                    TaskCornerRadius.get(view.context),
+                )
+            }
+        }
+
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        var heightMeasure = heightMeasureSpec
+        val maxMenuHeight = calculateMaxHeight()
+        if (MeasureSpec.getSize(heightMeasure) > maxMenuHeight) {
+            heightMeasure = MeasureSpec.makeMeasureSpec(maxMenuHeight, MeasureSpec.AT_MOST)
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasure)
+    }
+
+    fun onRotationChanged() {
+        openCloseAnimator?.let { if (it.isRunning) it.end() }
+        if (mIsOpen) {
+            optionLayout.removeAllViews()
+            if (enableOverviewIconMenu() || !populateAndLayoutMenu()) {
+                close(false)
+            }
+        }
+    }
+
+    private fun populateAndShowForTask(taskContainer: TaskContainer): Boolean {
+        if (isAttachedToWindow) return false
+        recentsViewContainer.dragLayer.addView(this)
+        taskView = taskContainer.taskView
+        this.taskContainer = taskContainer
+        if (!populateAndLayoutMenu()) return false
+        post { this.animateOpen() }
+        return true
+    }
+
+    /** @return true if successfully able to populate task view menu, false otherwise */
+    private fun populateAndLayoutMenu(): Boolean {
+        addMenuOptions(taskContainer)
+        orientAroundTaskView(taskContainer)
+        return true
+    }
+
+    private fun addMenuOptions(taskContainer: TaskContainer) {
+        if (enableOverviewIconMenu()) {
+            removeView(taskName)
+        } else {
+            taskName.text = TaskUtils.getTitle(context, taskContainer.task)
+            taskName.setOnClickListener { close(true) }
+        }
+        TaskOverlayFactory.getEnabledShortcuts(taskView, taskContainer)
+            .forEach(Consumer { menuOption: SystemShortcut<*> -> this.addMenuOption(menuOption) })
+    }
+
+    private fun addMenuOption(menuOption: SystemShortcut<*>) {
+        val menuOptionView =
+            recentsViewContainer.layoutInflater.inflate(R.layout.task_view_menu_option, this, false)
+                as LinearLayout
+        if (enableOverviewIconMenu()) {
+            menuOptionView.background =
+                ResourcesCompat.getDrawable(
+                    resources,
+                    R.drawable.app_chip_menu_item_bg,
+                    context.theme,
+                )
+            menuOptionView.foreground =
+                ResourcesCompat.getDrawable(
+                    resources,
+                    R.drawable.app_chip_menu_item_fg,
+                    context.theme,
+                )
+        }
+        menuOption.setIconAndLabelFor(
+            menuOptionView.findViewById(R.id.icon),
+            menuOptionView.findViewById(R.id.text),
+        )
+        val lp = menuOptionView.layoutParams as LayoutParams
+        taskView.pagedOrientationHandler.setLayoutParamsForTaskMenuOptionItem(
+            lp,
+            menuOptionView,
+            recentsViewContainer.deviceProfile,
+        )
+        // Set an onClick listener on each menu option. The onClick method is responsible for
+        // ending LiveTile mode on the thumbnail if needed.
+        menuOptionView.setOnClickListener { v: View? -> menuOption.onClick(v) }
+        optionLayout.addView(menuOptionView)
+    }
+
+    private fun orientAroundTaskView(taskContainer: TaskContainer) {
+        val recentsView = recentsViewContainer.getOverviewPanel<RecentsView<*, *>>()
+        val orientationHandler = recentsView.pagedOrientationHandler
+        measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
+
+        // Get Position
+        val deviceProfile = recentsViewContainer.deviceProfile
+        recentsViewContainer.dragLayer.getDescendantRectRelativeToSelf(
+            if (enableOverviewIconMenu()) iconView.findViewById(R.id.icon_view_menu_anchor)
+            else taskContainer.snapshotView,
+            tempRect,
+        )
+        val insets = recentsViewContainer.dragLayer.getInsets()
+        val params = layoutParams as BaseDragLayer.LayoutParams
+        params.width =
+            orientationHandler.getTaskMenuWidth(
+                taskContainer.snapshotView,
+                deviceProfile,
+                taskContainer.stagePosition,
+            )
+        // Gravity set to Left instead of Start as sTempRect.left measures Left distance not Start
+        params.gravity = Gravity.LEFT
+        layoutParams = params
+        scaleX = taskView.scaleX
+        scaleY = taskView.scaleY
+
+        // Set divider spacing
+        val divider = ShapeDrawable(RectShape())
+        divider.paint.color = resources.getColor(android.R.color.transparent)
+        val dividerSpacing = resources.getDimension(R.dimen.task_menu_spacing).toInt()
+        optionLayout.showDividers =
+            if (enableOverviewIconMenu()) SHOW_DIVIDER_NONE else SHOW_DIVIDER_MIDDLE
+
+        optionLayout.background =
+            if (enableOverviewIconMenu()) {
+                ResourcesCompat.getDrawable(resources, R.drawable.app_chip_menu_bg, context.theme)
+            } else {
+                null
+            }
+
+        orientationHandler.setTaskOptionsMenuLayoutOrientation(
+            deviceProfile,
+            optionLayout,
+            dividerSpacing,
+            divider,
+        )
+        val thumbnailAlignedX = (tempRect.left - insets.left).toFloat()
+        val thumbnailAlignedY = (tempRect.top - insets.top).toFloat()
+
+        // Changing pivot to make computations easier
+        // NOTE: Changing the pivots means the rotated view gets rotated about the new pivots set,
+        // which would render the X and Y position set here incorrect
+        pivotX = 0f
+        pivotY = 0f
+        rotation = orientationHandler.degreesRotated
+
+        if (enableOverviewIconMenu()) {
+            translationX = thumbnailAlignedX
+            translationY = thumbnailAlignedY
+        } else {
+            // Margin that insets the menuView inside the taskView
+            val taskInsetMargin = resources.getDimension(R.dimen.task_card_margin)
+            translationX =
+                orientationHandler.getTaskMenuX(
+                    thumbnailAlignedX,
+                    this.taskContainer.snapshotView,
+                    deviceProfile,
+                    taskInsetMargin,
+                    iconView,
+                )
+            translationY =
+                orientationHandler.getTaskMenuY(
+                    thumbnailAlignedY,
+                    this.taskContainer.snapshotView,
+                    this.taskContainer.stagePosition,
+                    this,
+                    taskInsetMargin,
+                    iconView,
+                )
+        }
+    }
+
+    private fun animateOpen() {
+        menuTranslationYBeforeOpen = translationY
+        menuTranslationXBeforeOpen = translationX
+        animateOpenOrClosed(false)
+        mIsOpen = true
+    }
+
+    private val iconView: View
+        get() = taskContainer.iconView.asView()
+
+    private fun animateOpenOrClosed(closing: Boolean, animated: Boolean = true) {
+        openCloseAnimator?.let { if (it.isRunning) it.cancel() }
+        openCloseAnimator = AnimatorSet()
+        // If we're opening, we just start from the beginning as a new `TaskMenuView` is created
+        // each time we do the open animation so there will never be a partial value here.
+        var revealAnimationStartProgress = 0f
+        if (closing && revealAnimator != null) {
+            revealAnimationStartProgress = 1f - revealAnimator!!.animatedFraction
+        }
+        revealAnimator =
+            createOpenCloseOutlineProvider()
+                .createRevealAnimator(this, closing, revealAnimationStartProgress)
+        revealAnimator!!.interpolator =
+            if (enableOverviewIconMenu()) Interpolators.EMPHASIZED else Interpolators.DECELERATE
+        val openCloseAnimatorBuilder = openCloseAnimator!!.play(revealAnimator)
+        if (enableOverviewIconMenu()) {
+            animateOpenOrCloseAppChip(closing, openCloseAnimatorBuilder)
+        }
+        openCloseAnimatorBuilder.with(
+            ObjectAnimator.ofFloat(this, ALPHA, (if (closing) 0 else 1).toFloat())
+        )
+        if (enableRefactorTaskThumbnail()) {
+            revealAnimator?.addUpdateListener { animation: ValueAnimator ->
+                val animatedFraction = animation.animatedFraction
+                val openProgress = if (closing) (1 - animatedFraction) else animatedFraction
+                taskContainer.updateMenuOpenProgress(openProgress)
+            }
+        } else {
+            openCloseAnimatorBuilder.with(
+                ObjectAnimator.ofFloat(
+                    taskContainer.thumbnailViewDeprecated,
+                    TaskThumbnailViewDeprecated.DIM_ALPHA,
+                    if (closing) 0f else TaskView.MAX_PAGE_SCRIM_ALPHA,
+                )
+            )
+        }
+        openCloseAnimator!!.addListener(
+            object : AnimationSuccessListener() {
+                override fun onAnimationStart(animation: Animator) {
+                    visibility = VISIBLE
+                    if (closing) onClosingStartCallback?.run()
+                }
+
+                override fun onAnimationSuccess(animator: Animator) {
+                    if (closing) closeComplete()
+                }
+            }
+        )
+        val animationDuration =
+            when {
+                animated && closing -> REVEAL_CLOSE_DURATION
+                animated && !closing -> REVEAL_OPEN_DURATION
+                else -> 0L
+            }
+        openCloseAnimator!!.setDuration(animationDuration)
+        openCloseAnimator!!.start()
+    }
+
+    private fun TaskView.isOnGridBottomRow(): Boolean =
+        (recentsViewContainer.getOverviewPanel<View>() as RecentsView<*, *>).isOnGridBottomRow(this)
+
+    private fun closeComplete() {
+        mIsOpen = false
+        recentsViewContainer.dragLayer.removeView(this)
+        revealAnimator = null
+    }
+
+    private fun createOpenCloseOutlineProvider(): RoundedRectRevealOutlineProvider {
+        val radius = TaskCornerRadius.get(mContext)
+        val fromRect =
+            Rect(
+                if (enableOverviewIconMenu() && isLayoutRtl) width else 0,
+                0,
+                if (enableOverviewIconMenu() && !isLayoutRtl) 0 else width,
+                0,
+            )
+        val toRect = Rect(0, 0, width, height)
+        return RoundedRectRevealOutlineProvider(radius, radius, fromRect, toRect)
+    }
+
+    /**
+     * Calculates max height based on how much space we have available. If not enough space then the
+     * view will scroll. The maximum menu size will sit inside the task with a margin on the top and
+     * bottom.
+     */
+    private fun calculateMaxHeight(): Int =
+        taskView.pagedOrientationHandler.getTaskMenuHeight(
+            taskInsetMargin = resources.getDimension(R.dimen.task_card_margin), // taskInsetMargin
+            deviceProfile = recentsViewContainer.deviceProfile,
+            taskMenuX = translationX,
+            taskMenuY =
+                // Bottom menu can translate up to show more options. So we use the min
+                // translation allowed to calculate its max height.
+                if (enableOverviewIconMenu() && taskView.isOnGridBottomRow()) minMenuTop
+                else translationY,
+        )
+
+    private fun setOnClosingStartCallback(onClosingStartCallback: Runnable?) {
+        this.onClosingStartCallback = onClosingStartCallback
+    }
+
+    private fun animateOpenOrCloseAppChip(closing: Boolean, animatorBuilder: AnimatorSet.Builder) {
+        val iconAppChip = taskContainer.iconView.asView() as IconAppChipView
+
+        // Animate menu up for enough room to display full menu when task on bottom row.
+        var additionalTranslationY = 0f
+        if (taskView.isOnGridBottomRow()) {
+            val currentMenuBottom: Float = menuTranslationYBeforeOpen + height
+            additionalTranslationY =
+                if (currentMenuBottom < maxMenuBottom) 0f
+                // Translate menu up for enough room to display full menu when task on bottom row.
+                else maxMenuBottom - currentMenuBottom
+
+            val currentMenuTop = menuTranslationYBeforeOpen + additionalTranslationY
+            // If it translate above the min accepted, it translates to the top of the screen
+            if (currentMenuTop < minMenuTop) {
+                // It subtracts the menuTranslation to make it 0 (top of the screen) + chip size.
+                additionalTranslationY = -menuTranslationYBeforeOpen + minMenuTop
+            }
+        }
+
+        val translationYAnim =
+            ObjectAnimator.ofFloat(
+                this,
+                TRANSLATION_Y,
+                if (closing) menuTranslationYBeforeOpen
+                else menuTranslationYBeforeOpen + additionalTranslationY,
+            )
+        translationYAnim.interpolator = Interpolators.EMPHASIZED
+        animatorBuilder.with(translationYAnim)
+
+        val menuTranslationYAnim: ObjectAnimator =
+            ObjectAnimator.ofFloat(
+                iconAppChip.getMenuTranslationY(),
+                MultiPropertyFactory.MULTI_PROPERTY_VALUE,
+                if (closing) 0f else additionalTranslationY,
+            )
+        menuTranslationYAnim.interpolator = Interpolators.EMPHASIZED
+        animatorBuilder.with(menuTranslationYAnim)
+
+        var additionalTranslationX = 0f
+        if (
+            recentsViewContainer.deviceProfile.isLandscape &&
+                taskContainer.stagePosition ==
+                    SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
+        ) {
+            // Animate menu and icon when split task would display off the side of the screen.
+            additionalTranslationX =
+                max(
+                        (translationX + width -
+                                (recentsViewContainer.deviceProfile.widthPx -
+                                    resources.getDimensionPixelSize(
+                                        R.dimen.task_menu_edge_padding
+                                    ) * 2))
+                            .toDouble(),
+                        0.0,
+                    )
+                    .toFloat()
+        }
+
+        val translationXAnim =
+            ObjectAnimator.ofFloat(
+                this,
+                TRANSLATION_X,
+                if (closing) menuTranslationXBeforeOpen
+                else menuTranslationXBeforeOpen - additionalTranslationX,
+            )
+        translationXAnim.interpolator = Interpolators.EMPHASIZED
+        animatorBuilder.with(translationXAnim)
+
+        val menuTranslationXAnim: ObjectAnimator =
+            ObjectAnimator.ofFloat(
+                iconAppChip.getMenuTranslationX(),
+                MultiPropertyFactory.MULTI_PROPERTY_VALUE,
+                if (closing) 0f else -additionalTranslationX,
+            )
+        menuTranslationXAnim.interpolator = Interpolators.EMPHASIZED
+        animatorBuilder.with(menuTranslationXAnim)
+    }
+
+    companion object {
+        private val REVEAL_OPEN_DURATION = if (enableOverviewIconMenu()) 417L else 150L
+        private val REVEAL_CLOSE_DURATION = if (enableOverviewIconMenu()) 333L else 100L
+
+        /** Show a task menu for the given taskContainer. */
+        /** Show a task menu for the given taskContainer. */
+        @JvmOverloads
+        fun showForTask(
+            taskContainer: TaskContainer,
+            onClosingStartCallback: Runnable? = null,
+        ): Boolean {
+            val container: RecentsViewContainer =
+                RecentsViewContainer.containerFromContext(taskContainer.taskView.context)
+            val taskMenuView =
+                container.layoutInflater.inflate(R.layout.task_menu, container.dragLayer, false)
+                    as TaskMenuView
+            taskMenuView.setOnClosingStartCallback(onClosingStartCallback)
+            return taskMenuView.populateAndShowForTask(taskContainer)
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailViewDeprecated.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailViewDeprecated.java
index 5ee5e10..74d76e6 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailViewDeprecated.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailViewDeprecated.java
@@ -412,7 +412,12 @@
                 thumbnailDataAspect, MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT);
     }
 
-    private boolean isThumbnailRotationDifferentFromTask() {
+    /**
+     * Returns whether or not the current thumbnail is a different orientation to the task.
+     * <p>
+     * Used to disable modal state when screenshot doesn't match the device orientation.
+     */
+    public boolean isThumbnailRotationDifferentFromTask() {
         RecentsView recents = mTaskView.getRecentsView();
         if (recents == null || mThumbnailData == null) {
             return false;
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index 0e5382a..29793ad 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -33,7 +33,6 @@
 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
@@ -61,8 +60,9 @@
 import com.android.launcher3.testing.shared.TestProtocol
 import com.android.launcher3.util.CancellableTask
 import com.android.launcher3.util.Executors
+import com.android.launcher3.util.KFloatProperty
+import com.android.launcher3.util.MultiPropertyDelegate
 import com.android.launcher3.util.MultiPropertyFactory
-import com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE
 import com.android.launcher3.util.MultiValueAlpha
 import com.android.launcher3.util.RunnableList
 import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
@@ -81,9 +81,11 @@
 import com.android.quickstep.recents.di.RecentsDependencies
 import com.android.quickstep.recents.di.get
 import com.android.quickstep.recents.di.inject
+import com.android.quickstep.recents.domain.usecase.ThumbnailPosition
 import com.android.quickstep.recents.ui.viewmodel.TaskData
 import com.android.quickstep.recents.ui.viewmodel.TaskTileUiState
 import com.android.quickstep.recents.ui.viewmodel.TaskViewModel
+import com.android.quickstep.task.thumbnail.TaskContentView
 import com.android.quickstep.util.ActiveGestureErrorDetector
 import com.android.quickstep.util.ActiveGestureLog
 import com.android.quickstep.util.BorderAnimator
@@ -99,7 +101,6 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
-import kotlinx.coroutines.SupervisorJob
 import kotlinx.coroutines.cancel
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.launch
@@ -199,7 +200,7 @@
          */
         get() = (getNonGridTrans(nonGridTranslationX) + getGridTrans(this.gridTranslationX))
 
-    protected val persistentTranslationY: Float
+    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).
@@ -220,7 +221,7 @@
                 SPLIT_SELECT_TRANSLATION_Y,
             )
 
-    protected val primaryDismissTranslationProperty: FloatProperty<TaskView>
+    val primaryDismissTranslationProperty: FloatProperty<TaskView>
         get() =
             pagedOrientationHandler.getPrimaryValue(DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y)
 
@@ -301,13 +302,14 @@
     var sysUiStatusNavFlags: Int = 0
         get() =
             if (enableRefactorTaskThumbnail()) field
-            else taskContainers.first().thumbnailViewDeprecated.sysUiStatusNavFlags
+            else firstTaskContainer?.thumbnailViewDeprecated?.sysUiStatusNavFlags ?: 0
         private set
 
     // 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) {
+            if (value == field && enableOverviewIconMenu()) return
             field = Utilities.boundToRange(value, 0f, 1f)
             onFullscreenProgressChanged(field)
         }
@@ -440,28 +442,10 @@
             applyTranslationX()
         }
 
-    private val taskViewAlpha = MultiValueAlpha(this, NUM_ALPHA_CHANNELS)
-
-    protected var stableAlpha
-        set(value) {
-            taskViewAlpha.get(ALPHA_INDEX_STABLE).value = value
-        }
-        get() = taskViewAlpha.get(ALPHA_INDEX_STABLE).value
-
-    var attachAlpha
-        set(value) {
-            taskViewAlpha.get(ALPHA_INDEX_ATTACH).value = value
-        }
-        get() = taskViewAlpha.get(ALPHA_INDEX_ATTACH).value
-
-    var splitAlpha
-        set(value) {
-            splitAlphaProperty.value = value
-        }
-        get() = splitAlphaProperty.value
-
-    val splitAlphaProperty: MultiPropertyFactory<View>.MultiProperty
-        get() = taskViewAlpha.get(ALPHA_INDEX_SPLIT)
+    private val taskViewAlpha = MultiValueAlpha(this, Alpha.entries.size)
+    protected var stableAlpha by MultiPropertyDelegate(taskViewAlpha, Alpha.STABLE)
+    var attachAlpha by MultiPropertyDelegate(taskViewAlpha, Alpha.ATTACH)
+    var splitAlpha by MultiPropertyDelegate(taskViewAlpha, Alpha.SPLIT)
 
     protected var shouldShowScreenshot = false
         get() = !isRunningTask || field
@@ -507,6 +491,7 @@
     // 1 = The TaskView is settled and no longer transitioning
     private var settledProgress = 1f
         set(value) {
+            if (value == field && enableOverviewIconMenu()) return
             field = value
             onSettledProgressUpdated(field)
         }
@@ -515,20 +500,20 @@
         MultiPropertyFactory(
             this,
             SETTLED_PROGRESS,
-            SETTLED_PROGRESS_INDEX_COUNT,
+            SettledProgress.entries.size,
             { x: Float, y: Float -> x * y },
             1f,
         )
-    private val settledProgressFullscreen =
-        settledProgressPropertyFactory.get(SETTLED_PROGRESS_INDEX_FULLSCREEN)
-    private val settledProgressGesture =
-        settledProgressPropertyFactory.get(SETTLED_PROGRESS_INDEX_GESTURE)
-    private val settledProgressDismiss =
-        settledProgressPropertyFactory.get(SETTLED_PROGRESS_INDEX_DISMISS)
+    private var settledProgressFullscreen by
+        MultiPropertyDelegate(settledProgressPropertyFactory, SettledProgress.Fullscreen)
+    private var settledProgressGesture by
+        MultiPropertyDelegate(settledProgressPropertyFactory, SettledProgress.Gesture)
+    private var settledProgressDismiss by
+        MultiPropertyDelegate(settledProgressPropertyFactory, SettledProgress.Dismiss)
 
     private var viewModel: TaskViewModel? = null
     private val dispatcherProvider: DispatcherProvider by RecentsDependencies.inject()
-    private val coroutineScope by lazy { CoroutineScope(SupervisorJob() + dispatcherProvider.main) }
+    private val coroutineScope: CoroutineScope by RecentsDependencies.inject()
     private val coroutineJobs = mutableListOf<Job>()
 
     /**
@@ -536,7 +521,7 @@
      * interpolator.
      */
     fun getDismissIconFadeInAnimator(): ObjectAnimator =
-        ObjectAnimator.ofFloat(settledProgressDismiss, MULTI_PROPERTY_VALUE, 1f).apply {
+        ObjectAnimator.ofFloat(this, SETTLED_PROGRESS_DISMISS, 1f).apply {
             duration = FADE_IN_ICON_DURATION
             interpolator = FADE_IN_ICON_INTERPOLATOR
         }
@@ -548,8 +533,7 @@
      */
     fun getDismissIconFadeOutAnimator(): ObjectAnimator =
         AnimatedFloat { v ->
-                settledProgressDismiss.value =
-                    SETTLED_PROGRESS_FAST_OUT_INTERPOLATOR.getInterpolation(v)
+                settledProgressDismiss = SETTLED_PROGRESS_FAST_OUT_INTERPOLATOR.getInterpolation(v)
             }
             .animateToValue(1f, 0f)
 
@@ -736,13 +720,10 @@
     }
 
     protected open fun inflateViewStubs() {
-        findViewById<ViewStub>(R.id.snapshot)
-            ?.apply {
-                layoutResource =
-                    if (enableRefactorTaskThumbnail()) R.layout.task_thumbnail
-                    else R.layout.task_thumbnail_deprecated
-            }
+        findViewById<ViewStub>(R.id.task_content_view)
+            ?.apply { layoutResource = R.layout.task_content_view }
             ?.inflate()
+
         findViewById<ViewStub>(R.id.icon)
             ?.apply {
                 layoutResource =
@@ -759,7 +740,9 @@
             // onRecycle. So it should be initialized at this point. TaskView Lifecycle:
             // `bind` -> `onBind` ->  onAttachedToWindow() -> onDetachFromWindow -> onRecycle
             coroutineJobs +=
-                coroutineScope.launch { viewModel!!.state.collectLatest(::updateTaskViewState) }
+                coroutineScope.launch(dispatcherProvider.main) {
+                    viewModel!!.state.collectLatest(::updateTaskViewState)
+                }
         }
     }
 
@@ -792,11 +775,13 @@
                     },
             )
             updateThumbnailValidity(container)
-            updateThumbnailMatrix(
-                container = container,
-                width = container.thumbnailView.width,
-                height = container.thumbnailView.height,
-            )
+            val thumbnailPosition =
+                updateThumbnailMatrix(
+                    container = container,
+                    width = container.thumbnailView.width,
+                    height = container.thumbnailView.height,
+                )
+            container.setOverlayEnabled(state.taskOverlayEnabled, thumbnailPosition)
 
             if (enableOverviewIconMenu()) {
                 setIconState(container, containerState)
@@ -825,11 +810,16 @@
      * @param width The desired width of the thumbnail's container.
      * @param height The desired height of the thumbnail's container.
      */
-    private fun updateThumbnailMatrix(container: TaskContainer, width: Int, height: Int) {
+    private fun updateThumbnailMatrix(
+        container: TaskContainer,
+        width: Int,
+        height: Int,
+    ): ThumbnailPosition? {
         val thumbnailPosition =
             viewModel?.getThumbnailPosition(container.thumbnailData, width, height, isLayoutRtl)
-                ?: return
+                ?: return null
         container.updateThumbnailMatrix(thumbnailPosition.matrix)
+        return thumbnailPosition
     }
 
     override fun onDetachedFromWindow() {
@@ -853,10 +843,12 @@
         taskOverlayFactory: TaskOverlayFactory,
     ) {
         cancelPendingLoadTasks()
+        this.orientedState = orientedState // Needed for dependencies
         taskContainers =
             listOf(
                 createTaskContainer(
                     task,
+                    R.id.task_content_view,
                     R.id.snapshot,
                     R.id.icon,
                     R.id.show_windows,
@@ -870,15 +862,17 @@
 
     protected open fun onBind(orientedState: RecentsOrientedState) {
         if (enableRefactorTaskThumbnail()) {
+            val scopeId = context
+            Log.d(TAG, "onBind $scopeId ${orientedState.containerInterface}")
             viewModel =
                 TaskViewModel(
                         taskViewType = type,
-                        recentsViewData = RecentsDependencies.get(),
-                        getTaskUseCase = RecentsDependencies.get(),
-                        getSysUiStatusNavFlagsUseCase = RecentsDependencies.get(),
-                        isThumbnailValidUseCase = RecentsDependencies.get(),
-                        getThumbnailPositionUseCase = RecentsDependencies.get(),
-                        dispatcherProvider = RecentsDependencies.get(),
+                        recentsViewData = RecentsDependencies.get(scopeId),
+                        getTaskUseCase = RecentsDependencies.get(scopeId),
+                        getSysUiStatusNavFlagsUseCase = RecentsDependencies.get(scopeId),
+                        isThumbnailValidUseCase = RecentsDependencies.get(scopeId),
+                        getThumbnailPositionUseCase = RecentsDependencies.get(scopeId),
+                        dispatcherProvider = RecentsDependencies.get(scopeId),
                     )
                     .apply { bind(*taskIds) }
         }
@@ -886,10 +880,12 @@
         taskContainers.forEach { container ->
             container.bind()
             if (enableRefactorTaskThumbnail()) {
-                container.thumbnailView.cornerRadius = thumbnailFullscreenParams.currentCornerRadius
-                container.thumbnailView.doOnSizeChange { width, height ->
+                container.taskContentView.cornerRadius =
+                    thumbnailFullscreenParams.currentCornerRadius
+                container.taskContentView.doOnSizeChange { width, height ->
                     updateThumbnailValidity(container)
-                    updateThumbnailMatrix(container, width, height)
+                    val thumbnailPosition = updateThumbnailMatrix(container, width, height)
+                    container.refreshOverlay(thumbnailPosition)
                 }
             }
         }
@@ -913,6 +909,7 @@
 
     protected fun createTaskContainer(
         task: Task,
+        @IdRes taskContentViewId: Int,
         @IdRes thumbnailViewId: Int,
         @IdRes iconViewId: Int,
         @IdRes showWindowViewId: Int,
@@ -921,10 +918,12 @@
         taskOverlayFactory: TaskOverlayFactory,
     ): TaskContainer {
         val iconView = findViewById<View>(iconViewId) as TaskViewIcon
+        val taskContentView = findViewById<TaskContentView>(taskContentViewId)
         return TaskContainer(
             this,
             task,
-            findViewById(thumbnailViewId),
+            taskContentView,
+            taskContentView.findViewById(thumbnailViewId),
             iconView,
             TransformingTouchDelegate(iconView.asView()),
             stagePosition,
@@ -1012,7 +1011,7 @@
     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
-        firstTaskContainer?.snapshotView?.updateLayoutParams<LayoutParams> {
+        firstTaskContainer?.taskContentView?.updateLayoutParams<LayoutParams> {
             topMargin = container.deviceProfile.overviewTaskThumbnailTopMarginPx
         }
         taskContainers.forEach { it.digitalWellBeingToast?.setupLayout() }
@@ -1026,11 +1025,11 @@
             val thumbnailBounds = Rect()
             if (relativeToDragLayer) {
                 container.dragLayer.getDescendantRectRelativeToSelf(
-                    it.snapshotView,
+                    it.taskContentView,
                     thumbnailBounds,
                 )
             } else {
-                thumbnailBounds.set(it.snapshotView)
+                thumbnailBounds.set(it.taskContentView)
             }
             bounds.union(thumbnailBounds)
         }
@@ -1476,7 +1475,8 @@
         return if (enableOverviewIconMenu() && menuContainer.iconView is IconAppChipView) {
             menuContainer.iconView.revealAnim(/* isRevealing= */ true)
             TaskMenuView.showForTask(menuContainer) {
-                menuContainer.iconView.revealAnim(/* isRevealing= */ false)
+                val isAnimated = !recentsView.isSplitSelectionActive
+                menuContainer.iconView.revealAnim(/* isRevealing= */ false, isAnimated)
                 if (enableHoverOfChildElementsInTaskview()) {
                     recentsView.setTaskBorderEnabled(true)
                 }
@@ -1602,7 +1602,7 @@
     fun startIconFadeInOnGestureComplete() {
         iconFadeInOnGestureCompleteAnimator?.cancel()
         iconFadeInOnGestureCompleteAnimator =
-            ObjectAnimator.ofFloat(settledProgressGesture, MULTI_PROPERTY_VALUE, 1f).apply {
+            ObjectAnimator.ofFloat(this, SETTLED_PROGRESS_GESTURE, 1f).apply {
                 duration = FADE_IN_ICON_DURATION
                 interpolator = Interpolators.LINEAR
                 addListener(
@@ -1618,7 +1618,7 @@
 
     fun setIconVisibleForGesture(isVisible: Boolean) {
         iconFadeInOnGestureCompleteAnimator?.cancel()
-        settledProgressGesture.value = if (isVisible) 1f else 0f
+        settledProgressGesture = if (isVisible) 1f else 0f
     }
 
     /** Set a color tint on the snapshot and supporting views. */
@@ -1666,7 +1666,7 @@
     protected fun getScrollAdjustment(gridEnabled: Boolean) =
         if (gridEnabled) gridTranslationX else nonGridTranslationX
 
-    protected fun getOffsetAdjustment(gridEnabled: Boolean) = getScrollAdjustment(gridEnabled)
+    fun getOffsetAdjustment(gridEnabled: Boolean) = getScrollAdjustment(gridEnabled)
 
     fun getSizeAdjustment(fullscreenEnabled: Boolean) = if (fullscreenEnabled) nonGridScale else 1f
 
@@ -1704,10 +1704,12 @@
 
     protected open fun onFullscreenProgressChanged(fullscreenProgress: Float) {
         taskContainers.forEach {
-            it.iconView.setVisibility(if (fullscreenProgress < 1) VISIBLE else INVISIBLE)
+            if (!enableOverviewIconMenu()) {
+                it.iconView.setVisibility(if (fullscreenProgress < 1) VISIBLE else INVISIBLE)
+            }
             it.overlay.setFullscreenProgress(fullscreenProgress)
         }
-        settledProgressFullscreen.value =
+        settledProgressFullscreen =
             SETTLED_PROGRESS_FAST_OUT_INTERPOLATOR.getInterpolation(1 - fullscreenProgress)
         updateFullscreenParams()
     }
@@ -1716,7 +1718,7 @@
         updateFullscreenParams(thumbnailFullscreenParams)
         taskContainers.forEach {
             if (enableRefactorTaskThumbnail()) {
-                it.thumbnailView.cornerRadius = thumbnailFullscreenParams.currentCornerRadius
+                it.taskContentView.cornerRadius = thumbnailFullscreenParams.currentCornerRadius
             } else {
                 it.thumbnailViewDeprecated.setFullscreenParams(thumbnailFullscreenParams)
             }
@@ -1765,7 +1767,7 @@
         dismissScale = 1f
         translationZ = 0f
         setIconVisibleForGesture(true)
-        settledProgressDismiss.value = 1f
+        settledProgressDismiss = 1f
         setColorTint(0f, 0)
     }
 
@@ -1787,23 +1789,25 @@
 
     companion object {
         private const val TAG = "TaskView"
+
+        private enum class Alpha {
+            STABLE,
+            ATTACH,
+            SPLIT,
+        }
+
+        private enum class SettledProgress {
+            Fullscreen,
+            Gesture,
+            Dismiss,
+        }
+
         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)
 
-        const val SETTLED_PROGRESS_INDEX_FULLSCREEN = 0
-        const val SETTLED_PROGRESS_INDEX_GESTURE = 1
-        const val SETTLED_PROGRESS_INDEX_DISMISS = 2
-        const val SETTLED_PROGRESS_INDEX_COUNT = 3
-
-        private const val ALPHA_INDEX_STABLE = 0
-        private const val ALPHA_INDEX_ATTACH = 1
-        private const val ALPHA_INDEX_SPLIT = 2
-
-        private const val NUM_ALPHA_CHANNELS = 3
-
         /** The maximum amount that a task view can be scrimmed, dimmed or tinted. */
         const val MAX_PAGE_SCRIM_ALPHA = 0.4f
         const val FADE_IN_ICON_DURATION: Long = 120
@@ -1820,104 +1824,45 @@
         private val SYSTEM_GESTURE_EXCLUSION_RECT = listOf(Rect())
 
         private val SETTLED_PROGRESS: FloatProperty<TaskView> =
-            object : FloatProperty<TaskView>("settleTransition") {
-                override fun setValue(taskView: TaskView, v: Float) {
-                    taskView.settledProgress = v
-                }
+            KFloatProperty(TaskView::settledProgress)
 
-                override fun get(taskView: TaskView) = taskView.settledProgress
-            }
+        private val SETTLED_PROGRESS_GESTURE: FloatProperty<TaskView> =
+            KFloatProperty(TaskView::settledProgressGesture)
+
+        private val SETTLED_PROGRESS_DISMISS: FloatProperty<TaskView> =
+            KFloatProperty(TaskView::settledProgressDismiss)
 
         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
-            }
+            KFloatProperty(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
-            }
+            KFloatProperty(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
-            }
+            KFloatProperty(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
-            }
+            KFloatProperty(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
-            }
+            KFloatProperty(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
-            }
+            KFloatProperty(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
-            }
+            KFloatProperty(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
-            }
+            KFloatProperty(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
-            }
+            KFloatProperty(TaskView::gridEndTranslationX)
 
         @JvmField
-        val DISMISS_SCALE: FloatProperty<TaskView> =
-            object : FloatProperty<TaskView>("dismissScale") {
-                override fun setValue(taskView: TaskView, v: Float) {
-                    taskView.dismissScale = v
-                }
+        val DISMISS_SCALE: FloatProperty<TaskView> = KFloatProperty(TaskView::dismissScale)
 
-                override fun get(taskView: TaskView) = taskView.dismissScale
-            }
+        @JvmField val SPLIT_ALPHA: FloatProperty<TaskView> = KFloatProperty(TaskView::splitAlpha)
     }
 }
diff --git a/quickstep/src_protolog/com/android/launcher3/util/StateManagerProtoLogProxy.java b/quickstep/src_protolog/com/android/launcher3/util/StateManagerProtoLogProxy.java
index c319cb1..cb7254f 100644
--- a/quickstep/src_protolog/com/android/launcher3/util/StateManagerProtoLogProxy.java
+++ b/quickstep/src_protolog/com/android/launcher3/util/StateManagerProtoLogProxy.java
@@ -16,22 +16,25 @@
 
 package com.android.launcher3.util;
 
-import static com.android.launcher3.Flags.enableStateManagerProtoLog;
 import static com.android.quickstep.util.QuickstepProtoLogGroup.LAUNCHER_STATE_MANAGER;
 import static com.android.quickstep.util.QuickstepProtoLogGroup.isProtoLogInitialized;
 
+import android.window.DesktopModeFlags.DesktopModeFlag;
+
 import androidx.annotation.NonNull;
 
 import com.android.internal.protolog.ProtoLog;
+import com.android.launcher3.Flags;
 
 /**
  * Proxy class used for StateManager ProtoLog support.
  */
 public class StateManagerProtoLogProxy {
-
+    private static final DesktopModeFlag ENABLE_STATE_MANAGER_PROTO_LOG =
+            new DesktopModeFlag(Flags::enableStateManagerProtoLog, true);
     public static void logGoToState(
             @NonNull Object fromState, @NonNull Object toState, @NonNull String trace) {
-        if (!enableStateManagerProtoLog() || !isProtoLogInitialized()) return;
+        if (!ENABLE_STATE_MANAGER_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
         ProtoLog.d(LAUNCHER_STATE_MANAGER,
                 "StateManager.goToState: fromState: %s, toState: %s, partial trace:\n%s",
                 fromState,
@@ -41,7 +44,7 @@
 
     public static void logCreateAtomicAnimation(
             @NonNull Object fromState, @NonNull Object toState, @NonNull String trace) {
-        if (!enableStateManagerProtoLog() || !isProtoLogInitialized()) return;
+        if (!ENABLE_STATE_MANAGER_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
         ProtoLog.d(LAUNCHER_STATE_MANAGER, "StateManager.createAtomicAnimation: "
                         + "fromState: %s, toState: %s, partial trace:\n%s",
                 fromState,
@@ -50,17 +53,17 @@
     }
 
     public static void logOnStateTransitionStart(@NonNull Object state) {
-        if (!enableStateManagerProtoLog() || !isProtoLogInitialized()) return;
+        if (!ENABLE_STATE_MANAGER_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
         ProtoLog.d(LAUNCHER_STATE_MANAGER, "StateManager.onStateTransitionStart: state: %s", state);
     }
 
     public static void logOnStateTransitionEnd(@NonNull Object state) {
-        if (!enableStateManagerProtoLog() || !isProtoLogInitialized()) return;
+        if (!ENABLE_STATE_MANAGER_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
         ProtoLog.d(LAUNCHER_STATE_MANAGER, "StateManager.onStateTransitionEnd: state: %s", state);
     }
 
     public static void logCancelAnimation(boolean animationOngoing, @NonNull String trace) {
-        if (!enableStateManagerProtoLog() || !isProtoLogInitialized()) return;
+        if (!ENABLE_STATE_MANAGER_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
         ProtoLog.d(LAUNCHER_STATE_MANAGER,
                 "StateManager.cancelAnimation: animation ongoing: %b, partial trace:\n%s",
                 animationOngoing,
diff --git a/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureProtoLogProxy.java b/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureProtoLogProxy.java
index 37c64cf..18a5338 100644
--- a/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureProtoLogProxy.java
+++ b/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureProtoLogProxy.java
@@ -159,31 +159,37 @@
         ProtoLog.d(ACTIVE_GESTURE_LOG, "cleanUpRecentsAnimation");
     }
 
-    public static void logOnInputEventUserLocked() {
-        ActiveGestureLog.INSTANCE.addLog(
-                "TIS.onInputEvent: Cannot process input event: user is locked");
+    public static void logOnInputEventUserLocked(int displayId) {
+        ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+                "TIS.onInputEvent(displayId=%d): Cannot process input event: user is locked",
+                displayId));
         if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
         ProtoLog.d(ACTIVE_GESTURE_LOG,
-                "TIS.onInputEvent: Cannot process input event: user is locked");
+                "TIS.onInputEvent(displayId=%d): Cannot process input event: user is locked",
+                displayId);
     }
 
-    public static void logOnInputIgnoringFollowingEvents() {
-        ActiveGestureLog.INSTANCE.addLog("TIS.onMotionEvent: A new gesture has been started, "
+    public static void logOnInputIgnoringFollowingEvents(int displayId) {
+        ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+                "TIS.onMotionEvent(displayId=%d): A new gesture has been started, "
                         + "but a previously-requested recents animation hasn't started. "
-                        + "Ignoring all following motion events.",
+                        + "Ignoring all following motion events.", displayId),
                 RECENTS_ANIMATION_START_PENDING);
         if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
-        ProtoLog.d(ACTIVE_GESTURE_LOG, "TIS.onMotionEvent: A new gesture has been started, "
-                + "but a previously-requested recents animation hasn't started. "
-                + "Ignoring all following motion events.");
+        ProtoLog.d(ACTIVE_GESTURE_LOG,
+                "TIS.onMotionEvent(displayId=%d): A new gesture has been started, "
+                        + "but a previously-requested recents animation hasn't started. "
+                        + "Ignoring all following motion events.", displayId);
     }
 
-    public static void logOnInputEventThreeButtonNav() {
-        ActiveGestureLog.INSTANCE.addLog("TIS.onInputEvent: Cannot process input event: "
-                + "using 3-button nav and event is not a trackpad event");
+    public static void logOnInputEventThreeButtonNav(int displayId) {
+        ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+                "TIS.onInputEvent(displayId=%d): Cannot process input event: "
+                        + "using 3-button nav and event is not a trackpad event", displayId));
         if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
-        ProtoLog.d(ACTIVE_GESTURE_LOG, "TIS.onInputEvent: Cannot process input event: "
-                + "using 3-button nav and event is not a trackpad event");
+        ProtoLog.d(ACTIVE_GESTURE_LOG,
+                "TIS.onInputEvent(displayId=%d): Cannot process input event: "
+                        + "using 3-button nav and event is not a trackpad event", displayId);
     }
 
     public static void logPreloadRecentsAnimation() {
@@ -322,61 +328,84 @@
     }
 
     public static void logOnInputEventActionUp(
-            int x, int y, int action, @NonNull String classification) {
+            int x, int y, int action, @NonNull String classification, int displayId) {
         String actionString = MotionEvent.actionToString(action);
         ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
-                "onMotionEvent(%d, %d): %s, %s", x, y, actionString, classification),
+                "onMotionEvent(%d, %d): %s, %s, displayId=%d",
+                        x,
+                        y,
+                        actionString,
+                        classification,
+                        displayId),
                 /* gestureEvent= */ action == ACTION_DOWN
                         ? MOTION_DOWN
                         : MOTION_UP);
         if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
         ProtoLog.d(ACTIVE_GESTURE_LOG,
-                "onMotionEvent(%d, %d): %s, %s", x, y, actionString, classification);
+                "onMotionEvent(%d, %d): %s, %s, displayId=%d",
+                x,
+                y,
+                actionString,
+                classification,
+                displayId);
     }
 
     public static void logOnInputEventActionMove(
-            @NonNull String action, @NonNull String classification, int pointerCount) {
+            @NonNull String action,
+            @NonNull String classification,
+            int pointerCount,
+            int displayId) {
         ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
-                        "onMotionEvent: %s, %s, pointerCount: %d",
+                        "onMotionEvent: %s, %s, pointerCount: %d, displayId=%d",
                         action,
                         classification,
-                        pointerCount),
+                        pointerCount,
+                        displayId),
                 MOTION_MOVE);
         if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
         ProtoLog.d(ACTIVE_GESTURE_LOG,
-                "onMotionEvent: %s, %s, pointerCount: %d", action, classification, pointerCount);
+                "onMotionEvent: %s, %s, pointerCount: %d, displayId=%d",
+                action,
+                classification,
+                pointerCount,
+                displayId);
     }
 
     public static void logOnInputEventGenericAction(
-            @NonNull String action, @NonNull String classification) {
+            @NonNull String action, @NonNull String classification, int displayId) {
         ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
-                "onMotionEvent: %s, %s", action, classification));
+                "onMotionEvent: %s, %s, displayId=%d", action, classification, displayId));
         if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
-        ProtoLog.d(ACTIVE_GESTURE_LOG, "onMotionEvent: %s, %s", action, classification);
+        ProtoLog.d(ACTIVE_GESTURE_LOG,
+                "onMotionEvent: %s, %s, displayId=%d", action, classification, displayId);
     }
 
     public static void logOnInputEventNavModeSwitched(
-            @NonNull String startNavMode, @NonNull String currentNavMode) {
+            int displayId, @NonNull String startNavMode, @NonNull String currentNavMode) {
         ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
-                "TIS.onInputEvent: Navigation mode switched mid-gesture (%s -> %s); "
+                "TIS.onInputEvent(displayId=%d): Navigation mode switched mid-gesture (%s -> %s); "
                         + "cancelling gesture.",
+                        displayId,
                         startNavMode,
                         currentNavMode),
                 NAVIGATION_MODE_SWITCHED);
         if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
         ProtoLog.d(ACTIVE_GESTURE_LOG,
-                "TIS.onInputEvent: Navigation mode switched mid-gesture (%s -> %s); "
+                "TIS.onInputEvent(displayId=%d): Navigation mode switched mid-gesture (%s -> %s); "
                         + "cancelling gesture.",
+                displayId,
                 startNavMode,
                 currentNavMode);
     }
 
-    public static void logUnknownInputEvent(@NonNull String event) {
+    public static void logUnknownInputEvent(int displayId, @NonNull String event) {
         ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
-                "TIS.onInputEvent: Cannot process input event: received unknown event %s", event));
+                "TIS.onInputEvent(displayId=%d): Cannot process input event: "
+                        + "received unknown event %s", displayId, event));
         if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
         ProtoLog.d(ACTIVE_GESTURE_LOG,
-                "TIS.onInputEvent: Cannot process input event: received unknown event %s", event);
+                "TIS.onInputEvent(displayId=%d): Cannot process input event: "
+                        + "received unknown event %s", displayId, event);
     }
 
     public static void logFinishRunningRecentsAnimation(boolean toHome) {
@@ -433,11 +462,13 @@
         ProtoLog.d(ACTIVE_GESTURE_LOG, "Launching side task id=%d", taskId);
     }
 
-    public static void logOnInputEventActionDown(@NonNull ActiveGestureLog.CompoundString reason) {
+    public static void logOnInputEventActionDown(
+            int displayId, @NonNull ActiveGestureLog.CompoundString reason) {
         ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
-                "TIS.onMotionEvent: ").append(reason));
+                "TIS.onMotionEvent(displayId=%d): ", displayId).append(reason));
         if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
-        ProtoLog.d(ACTIVE_GESTURE_LOG, "TIS.onMotionEvent: %s", reason.toString());
+        ProtoLog.d(ACTIVE_GESTURE_LOG,
+                "TIS.onMotionEvent(displayId=%d): %s", displayId, reason.toString());
     }
 
     public static void logStartNewTask(@NonNull ActiveGestureLog.CompoundString tasks) {
diff --git a/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java b/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java
index 2c9ae33..99888fb 100644
--- a/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java
+++ b/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java
@@ -16,14 +16,16 @@
 
 package com.android.quickstep.util;
 
-import static com.android.launcher3.Flags.enableRecentsWindowProtoLog;
 import static com.android.quickstep.util.QuickstepProtoLogGroup.RECENTS_WINDOW;
 import static com.android.quickstep.util.QuickstepProtoLogGroup.isProtoLogInitialized;
 
+import android.window.DesktopModeFlags;
+
 import androidx.annotation.NonNull;
 
 import com.android.internal.protolog.ProtoLog;
 import com.android.internal.protolog.common.IProtoLogGroup;
+import com.android.launcher3.Flags;
 
 /**
  * Proxy class used for Recents Window ProtoLog support.
@@ -35,19 +37,20 @@
  * method. Or, if an existing entry needs to be modified, simply update it here.
  */
 public class RecentsWindowProtoLogProxy {
-
+    private static final DesktopModeFlags.DesktopModeFlag ENABLE_RECENTS_WINDOW_PROTO_LOG =
+            new DesktopModeFlags.DesktopModeFlag(Flags::enableRecentsWindowProtoLog, true);
     public static void logOnStateSetStart(@NonNull String stateName) {
-        if (!enableRecentsWindowProtoLog() || !isProtoLogInitialized()) return;
+        if (!ENABLE_RECENTS_WINDOW_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
         ProtoLog.d(RECENTS_WINDOW, "onStateSetStart: %s", stateName);
     }
 
     public static void logOnStateSetEnd(@NonNull String stateName) {
-        if (!enableRecentsWindowProtoLog() || !isProtoLogInitialized()) return;
+        if (!ENABLE_RECENTS_WINDOW_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
         ProtoLog.d(RECENTS_WINDOW, "onStateSetEnd: %s", stateName);
     }
 
     public static void logStartRecentsWindow(boolean isShown, boolean windowViewIsNull) {
-        if (!enableRecentsWindowProtoLog() || !isProtoLogInitialized()) return;
+        if (!ENABLE_RECENTS_WINDOW_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
         ProtoLog.d(RECENTS_WINDOW,
                 "Starting recents window: isShow= %b, windowViewIsNull=%b",
                 isShown,
@@ -55,7 +58,7 @@
     }
 
     public static void logCleanup(boolean isShown) {
-        if (!enableRecentsWindowProtoLog() || !isProtoLogInitialized()) return;
+        if (!ENABLE_RECENTS_WINDOW_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
         ProtoLog.d(RECENTS_WINDOW, "Cleaning up recents window: isShow= %b", isShown);
     }
 }
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/SplashHelper.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/SplashHelper.kt
new file mode 100644
index 0000000..8cc09d4
--- /dev/null
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/SplashHelper.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2025 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.thumbnail
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+
+object SplashHelper {
+    private val BITMAP_RECT_COLORS = listOf(Color.GREEN, Color.RED, Color.BLUE, Color.CYAN)
+
+    fun createSplash(): Bitmap = createBitmap(width = 20, height = 20, rectColorRotation = 1)
+
+    fun createBitmap(width: Int, height: Int, rectColorRotation: Int = 0): Bitmap =
+        Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).apply {
+            Canvas(this).apply {
+                val paint = Paint()
+                paint.color = BITMAP_RECT_COLORS[rectColorRotation % 4]
+                drawRect(0f, 0f, width / 2f, height / 2f, paint)
+                paint.color = BITMAP_RECT_COLORS[(1 + rectColorRotation) % 4]
+                drawRect(width / 2f, 0f, width.toFloat(), height / 2f, paint)
+                paint.color = BITMAP_RECT_COLORS[(2 + rectColorRotation) % 4]
+                drawRect(0f, height / 2f, width / 2f, height.toFloat(), paint)
+                paint.color = BITMAP_RECT_COLORS[(3 + rectColorRotation) % 4]
+                drawRect(width / 2f, height / 2f, width.toFloat(), height.toFloat(), paint)
+            }
+        }
+}
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskContentViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskContentViewScreenshotTest.kt
new file mode 100644
index 0000000..d36faa2
--- /dev/null
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskContentViewScreenshotTest.kt
@@ -0,0 +1,113 @@
+/*
+ * 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.thumbnail
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.drawable.BitmapDrawable
+import android.view.LayoutInflater
+import com.android.launcher3.R
+import com.android.quickstep.task.thumbnail.SplashHelper.createSplash
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
+import com.google.android.apps.nexuslauncher.imagecomparison.goldenpathmanager.ViewScreenshotGoldenPathManager
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.Displays
+import platform.test.screenshot.ViewScreenshotTestRule
+import platform.test.screenshot.getEmulatedDevicePathConfig
+
+/** Screenshot tests for [TaskContentView]. */
+@RunWith(ParameterizedAndroidJunit4::class)
+class TaskContentViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+
+    @get:Rule
+    val screenshotRule =
+        ViewScreenshotTestRule(
+            emulationSpec,
+            ViewScreenshotGoldenPathManager(getEmulatedDevicePathConfig(emulationSpec)),
+        )
+
+    @Test
+    fun taskContentView_recyclesToUninitialized() {
+        screenshotRule.screenshotTest("taskContentView_uninitialized") { activity ->
+            activity.actionBar?.hide()
+            val taskContentView = createTaskContentView(activity)
+            taskContentView.setState(
+                TaskHeaderUiState.HideHeader,
+                BackgroundOnly(Color.YELLOW),
+                null,
+            )
+            taskContentView.onRecycle()
+            taskContentView
+        }
+    }
+
+    @Test
+    fun taskContentView_shows_thumbnail_and_header() {
+        screenshotRule.screenshotTest("taskContentView_shows_thumbnail_and_header") { activity ->
+            activity.actionBar?.hide()
+            createTaskContentView(activity).apply {
+                setState(
+                    TaskHeaderUiState.ShowHeader(
+                        TaskHeaderUiState.ThumbnailHeader(
+                            BitmapDrawable(activity.resources, createSplash()),
+                            "test",
+                        ) {}
+                    ),
+                    BackgroundOnly(Color.YELLOW),
+                    null,
+                )
+            }
+        }
+    }
+
+    @Test
+    fun taskContentView_scaled_roundRoundedCorners() {
+        screenshotRule.screenshotTest("taskContentView_scaledRoundedCorners") { activity ->
+            activity.actionBar?.hide()
+            createTaskContentView(activity).apply {
+                scaleX = 0.75f
+                scaleY = 0.3f
+                setState(TaskHeaderUiState.HideHeader, BackgroundOnly(Color.YELLOW), null)
+            }
+        }
+    }
+
+    private fun createTaskContentView(context: Context): TaskContentView {
+        val taskContentView =
+            LayoutInflater.from(context).inflate(R.layout.task_content_view, null, false)
+                as TaskContentView
+        taskContentView.cornerRadius = CORNER_RADIUS
+        return taskContentView
+    }
+
+    companion object {
+        @Parameters(name = "{0}")
+        @JvmStatic
+        fun getTestSpecs() =
+            DeviceEmulationSpec.forDisplays(
+                Displays.Phone,
+                isDarkTheme = false,
+                isLandscape = false,
+            )
+
+        const val CORNER_RADIUS = 56f
+    }
+}
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskHeaderViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskHeaderViewScreenshotTest.kt
new file mode 100644
index 0000000..e30554e
--- /dev/null
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskHeaderViewScreenshotTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2025 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.thumbnail
+
+import android.content.Context
+import android.graphics.drawable.BitmapDrawable
+import android.view.LayoutInflater
+import com.android.launcher3.R
+import com.android.quickstep.task.thumbnail.SplashHelper.createSplash
+import com.android.quickstep.views.TaskHeaderView
+import com.google.android.apps.nexuslauncher.imagecomparison.goldenpathmanager.ViewScreenshotGoldenPathManager
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.Displays
+import platform.test.screenshot.ViewScreenshotTestRule
+import platform.test.screenshot.getEmulatedDevicePathConfig
+
+/** Screenshot tests for [TaskHeaderView]. */
+@RunWith(ParameterizedAndroidJunit4::class)
+class TaskHeaderViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+    @get:Rule
+    val screenshotRule =
+        ViewScreenshotTestRule(
+            emulationSpec,
+            ViewScreenshotGoldenPathManager(getEmulatedDevicePathConfig(emulationSpec)),
+        )
+
+    @Test
+    fun taskHeaderView_showHeader() {
+        screenshotRule.screenshotTest("taskHeaderView_showHeader") { activity ->
+            activity.actionBar?.hide()
+            createTaskHeaderView(activity).apply {
+                setState(
+                    TaskHeaderUiState.ShowHeader(
+                        TaskHeaderUiState.ThumbnailHeader(
+                            BitmapDrawable(activity.resources, createSplash()),
+                            "Example",
+                        ) {}
+                    )
+                )
+            }
+        }
+    }
+
+    private fun createTaskHeaderView(context: Context): TaskHeaderView {
+        val taskHeaderView =
+            LayoutInflater.from(context).inflate(R.layout.task_header_view, null, false)
+                as TaskHeaderView
+        return taskHeaderView
+    }
+
+    companion object {
+        @Parameters(name = "{0}")
+        @JvmStatic
+        fun getTestSpecs() =
+            DeviceEmulationSpec.forDisplays(
+                Displays.Tablet,
+                isDarkTheme = false,
+                isLandscape = true,
+            )
+    }
+}
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt
index 80b2c16..45df735 100644
--- a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt
@@ -16,16 +16,14 @@
 package com.android.quickstep.task.thumbnail
 
 import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.Canvas
 import android.graphics.Color
 import android.graphics.Matrix
-import android.graphics.Paint
 import android.graphics.drawable.BitmapDrawable
 import android.view.LayoutInflater
 import android.view.Surface.ROTATION_0
-import androidx.core.graphics.set
 import com.android.launcher3.R
+import com.android.quickstep.task.thumbnail.SplashHelper.createBitmap
+import com.android.quickstep.task.thumbnail.SplashHelper.createSplash
 import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
 import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
 import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.SnapshotSplash
@@ -90,23 +88,25 @@
     }
 
     @Test
-    fun taskThumbnailView_liveTile_withoutHeader() {
+    fun taskThumbnailView_liveTile() {
         screenshotRule.screenshotTest("taskThumbnailView_liveTile") { activity ->
             activity.actionBar?.hide()
-            createTaskThumbnailView(activity).apply {
-                setState(TaskThumbnailUiState.LiveTile.WithoutHeader)
-            }
+            createTaskThumbnailView(activity).apply { setState(TaskThumbnailUiState.LiveTile) }
         }
     }
 
     @Test
-    fun taskThumbnailView_image_withoutHeader() {
+    fun taskThumbnailView_image() {
         screenshotRule.screenshotTest("taskThumbnailView_image") { activity ->
             activity.actionBar?.hide()
             createTaskThumbnailView(activity).apply {
                 setState(
                     SnapshotSplash(
-                        Snapshot.WithoutHeader(createBitmap(), ROTATION_0, Color.DKGRAY),
+                        Snapshot(
+                            createBitmap(VIEW_ENV_WIDTH, VIEW_ENV_HEIGHT),
+                            ROTATION_0,
+                            Color.DKGRAY,
+                        ),
                         null,
                     )
                 )
@@ -115,14 +115,14 @@
     }
 
     @Test
-    fun taskThumbnailView_image_withoutHeader_withImageMatrix() {
+    fun taskThumbnailView_image_withImageMatrix() {
         screenshotRule.screenshotTest("taskThumbnailView_image_withMatrix") { activity ->
             activity.actionBar?.hide()
             createTaskThumbnailView(activity).apply {
                 val lessThanHeightMatchingAspectRatio = (VIEW_ENV_HEIGHT / 2) - 200
                 setState(
                     SnapshotSplash(
-                        Snapshot.WithoutHeader(
+                        Snapshot(
                             createBitmap(
                                 width = VIEW_ENV_WIDTH / 2,
                                 height = lessThanHeightMatchingAspectRatio,
@@ -139,13 +139,17 @@
     }
 
     @Test
-    fun taskThumbnailView_splash_withoutHeader() {
+    fun taskThumbnailView_splash() {
         screenshotRule.screenshotTest("taskThumbnailView_partial_splash") { activity ->
             activity.actionBar?.hide()
             createTaskThumbnailView(activity).apply {
                 setState(
                     SnapshotSplash(
-                        Snapshot.WithoutHeader(createBitmap(), ROTATION_0, Color.DKGRAY),
+                        Snapshot(
+                            createBitmap(VIEW_ENV_WIDTH, VIEW_ENV_HEIGHT),
+                            ROTATION_0,
+                            Color.DKGRAY,
+                        ),
                         BitmapDrawable(activity.resources, createSplash()),
                     )
                 )
@@ -155,14 +159,14 @@
     }
 
     @Test
-    fun taskThumbnailView_splash_withoutHeader_withImageMatrix() {
+    fun taskThumbnailView_splash_withImageMatrix() {
         screenshotRule.screenshotTest("taskThumbnailView_partial_splash_withMatrix") { activity ->
             activity.actionBar?.hide()
             createTaskThumbnailView(activity).apply {
                 val lessThanHeightMatchingAspectRatio = (VIEW_ENV_HEIGHT / 2) - 200
                 setState(
                     SnapshotSplash(
-                        Snapshot.WithoutHeader(
+                        Snapshot(
                             createBitmap(
                                 width = VIEW_ENV_WIDTH / 2,
                                 height = lessThanHeightMatchingAspectRatio,
@@ -229,31 +233,9 @@
         val taskThumbnailView =
             LayoutInflater.from(context).inflate(R.layout.task_thumbnail, null, false)
                 as TaskThumbnailView
-        taskThumbnailView.cornerRadius = CORNER_RADIUS
         return taskThumbnailView
     }
 
-    private fun createSplash() = createBitmap(width = 20, height = 20, rectColorRotation = 1)
-
-    private fun createBitmap(
-        width: Int = VIEW_ENV_WIDTH,
-        height: Int = VIEW_ENV_HEIGHT,
-        rectColorRotation: Int = 0,
-    ) =
-        Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).apply {
-            Canvas(this).apply {
-                val paint = Paint()
-                paint.color = BITMAP_RECT_COLORS[rectColorRotation % 4]
-                drawRect(0f, 0f, width / 2f, height / 2f, paint)
-                paint.color = BITMAP_RECT_COLORS[(1 + rectColorRotation) % 4]
-                drawRect(width / 2f, 0f, width.toFloat(), height / 2f, paint)
-                paint.color = BITMAP_RECT_COLORS[(2 + rectColorRotation) % 4]
-                drawRect(0f, height / 2f, width / 2f, height.toFloat(), paint)
-                paint.color = BITMAP_RECT_COLORS[(3 + rectColorRotation) % 4]
-                drawRect(width / 2f, height / 2f, width.toFloat(), height.toFloat(), paint)
-            }
-        }
-
     companion object {
         @Parameters(name = "{0}")
         @JvmStatic
@@ -264,8 +246,6 @@
                 isLandscape = false,
             )
 
-        const val CORNER_RADIUS = 56f
-        val BITMAP_RECT_COLORS = listOf(Color.GREEN, Color.RED, Color.BLUE, Color.CYAN)
         const val VIEW_ENV_WIDTH = 1440
         const val VIEW_ENV_HEIGHT = 3120
     }
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/AppEventProducerTest.java b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/AppEventProducerTest.java
index 06a939a..91f9e53 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/AppEventProducerTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/AppEventProducerTest.java
@@ -41,7 +41,7 @@
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.util.AllModulesForTest;
-import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
+import com.android.launcher3.util.SandboxContext;
 import com.android.launcher3.util.UserIconInfo;
 import com.android.systemui.shared.system.SysUiStatsLog;
 
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/WidgetsPredictionsRequesterTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/WidgetsPredictionsRequesterTest.kt
index 4ea74df..d445189 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/WidgetsPredictionsRequesterTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/WidgetsPredictionsRequesterTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.model
 
+import android.app.prediction.AppPredictionManager
+import android.app.prediction.AppPredictor
 import android.app.prediction.AppTarget
 import android.app.prediction.AppTargetEvent
 import android.app.prediction.AppTargetId
@@ -36,9 +38,15 @@
 import com.android.launcher3.model.WidgetPredictionsRequester.notOnUiSurfaceFilter
 import com.android.launcher3.util.ActivityContextWrapper
 import com.android.launcher3.util.ComponentKey
+import com.android.launcher3.util.Executors
+import com.android.launcher3.util.Executors.MODEL_EXECUTOR
+import com.android.launcher3.util.TestUtil
 import com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo
+import com.android.launcher3.widget.PendingAddWidgetInfo
 import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
 import java.util.function.Predicate
 import junit.framework.Assert.assertNotNull
 import org.junit.Before
@@ -46,6 +54,9 @@
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.whenever
 
 @RunWith(AndroidJUnit4::class)
 class WidgetsPredictionsRequesterTest {
@@ -67,11 +78,26 @@
 
     @Mock private lateinit var iconCache: IconCache
 
+    @Mock private lateinit var apmMock: AppPredictionManager
+
+    @Mock private lateinit var predictorMock: AppPredictor
+
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         mUserHandle = myUserHandle()
-        context = ActivityContextWrapper(ApplicationProvider.getApplicationContext())
+
+        whenever(apmMock.createAppPredictionSession(any())).thenReturn(predictorMock)
+
+        context =
+            object : ActivityContextWrapper(ApplicationProvider.getApplicationContext()) {
+                override fun getSystemService(name: String): Any? {
+                    if (name == "app_prediction") {
+                        return apmMock
+                    }
+                    return super.getSystemService(name)
+                }
+            }
         testInvariantProfile = LauncherAppState.getIDP(context)
         deviceProfile = testInvariantProfile.getDeviceProfile(context).copy(context)
 
@@ -114,22 +140,68 @@
                 buildExpectedAppTargetEvent(
                     /*pkg=*/ APP_1_PACKAGE_NAME,
                     /*providerClassName=*/ APP_1_PROVIDER_A_CLASS_NAME,
-                    /*user=*/ mUserHandle
+                    /*user=*/ mUserHandle,
                 ),
                 buildExpectedAppTargetEvent(
                     /*pkg=*/ APP_1_PACKAGE_NAME,
                     /*providerClassName=*/ APP_1_PROVIDER_B_CLASS_NAME,
-                    /*user=*/ mUserHandle
+                    /*user=*/ mUserHandle,
                 ),
                 buildExpectedAppTargetEvent(
                     /*pkg=*/ APP_2_PACKAGE_NAME,
                     /*providerClassName=*/ APP_2_PROVIDER_1_CLASS_NAME,
-                    /*user=*/ mUserHandle
-                )
+                    /*user=*/ mUserHandle,
+                ),
             )
     }
 
     @Test
+    fun request_invokesCallbackWithPredictedItems() {
+        TestUtil.runOnExecutorSync(MODEL_EXECUTOR) {
+            val underTest = WidgetPredictionsRequester(context, TEST_UI_SURFACE, allWidgets)
+            val existingWidgets = arrayListOf(widget1aInfo, widget1bInfo)
+            val predictions =
+                listOf(
+                    // (existing) already on surface
+                    AppTarget(
+                        AppTargetId(APP_1_PACKAGE_NAME),
+                        APP_1_PACKAGE_NAME,
+                        APP_1_PROVIDER_B_CLASS_NAME,
+                        mUserHandle,
+                    ),
+                    // eligible
+                    AppTarget(
+                        AppTargetId(APP_2_PACKAGE_NAME),
+                        APP_2_PACKAGE_NAME,
+                        APP_2_PROVIDER_1_CLASS_NAME,
+                        mUserHandle,
+                    ),
+                )
+            doAnswer {
+                    underTest.onTargetsAvailable(predictions)
+                    null
+                }
+                .whenever(predictorMock)
+                .requestPredictionUpdate()
+            val testCountDownLatch = CountDownLatch(1)
+            val listener =
+                WidgetPredictionsRequester.WidgetPredictionsListener { itemInfos ->
+                    if (itemInfos.size == 1 && itemInfos[0] is PendingAddWidgetInfo) {
+                        // only one item was eligible.
+                        testCountDownLatch.countDown()
+                    } else {
+                        println("Unexpected prediction items found: ${itemInfos.size}")
+                    }
+                }
+
+            underTest.request(existingWidgets, listener)
+            TestUtil.runOnExecutorSync(Executors.MAIN_EXECUTOR) {}
+
+            assertThat(testCountDownLatch.await(TEST_TIMEOUT, TimeUnit.SECONDS)).isTrue()
+        }
+    }
+
+    @Test
     fun filterPredictions_notOnUiSurfaceFilter_returnsOnlyEligiblePredictions() {
         val widgetsAlreadyOnSurface = arrayListOf(widget1bInfo)
         val filter: Predicate<WidgetItem> = notOnUiSurfaceFilter(widgetsAlreadyOnSurface)
@@ -141,15 +213,15 @@
                     AppTargetId(APP_1_PACKAGE_NAME),
                     APP_1_PACKAGE_NAME,
                     APP_1_PROVIDER_B_CLASS_NAME,
-                    mUserHandle
+                    mUserHandle,
                 ),
                 // eligible
                 AppTarget(
                     AppTargetId(APP_2_PACKAGE_NAME),
                     APP_2_PACKAGE_NAME,
                     APP_2_PROVIDER_1_CLASS_NAME,
-                    mUserHandle
-                )
+                    mUserHandle,
+                ),
             )
 
         // only 2 was eligible
@@ -167,27 +239,27 @@
                     AppTargetId(APP_1_PACKAGE_NAME),
                     APP_1_PACKAGE_NAME,
                     "$APP_1_PACKAGE_NAME.SomeActivity",
-                    mUserHandle
+                    mUserHandle,
                 ),
                 AppTarget(
                     AppTargetId(APP_2_PACKAGE_NAME),
                     APP_2_PACKAGE_NAME,
                     "$APP_2_PACKAGE_NAME.SomeActivity2",
-                    mUserHandle
+                    mUserHandle,
                 ),
             )
 
         assertThat(filterPredictions(predictions, allWidgets, filter)).isEmpty()
     }
 
-    private fun createWidgetItem(
-        providerInfo: AppWidgetProviderInfo,
-    ): WidgetItem {
+    private fun createWidgetItem(providerInfo: AppWidgetProviderInfo): WidgetItem {
         val widgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(context, providerInfo)
         return WidgetItem(widgetInfo, testInvariantProfile, iconCache, context)
     }
 
     companion object {
+        const val TEST_TIMEOUT = 3L
+
         const val TEST_UI_SURFACE = "widgets_test"
         const val BUNDLE_KEY_ADDED_APP_WIDGETS = "added_app_widgets"
 
@@ -203,13 +275,13 @@
         private fun buildExpectedAppTargetEvent(
             pkg: String,
             providerClassName: String,
-            userHandle: UserHandle
+            userHandle: UserHandle,
         ): AppTargetEvent {
             val appTarget =
                 AppTarget.Builder(
                         /*id=*/ AppTargetId("widget:$pkg"),
                         /*packageName=*/ pkg,
-                        /*user=*/ userHandle
+                        /*user=*/ userHandle,
                     )
                     .setClassName(providerClassName)
                     .build()
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt
index adfbca5..d2abed8 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt
@@ -28,13 +28,14 @@
 import com.android.launcher3.model.data.TaskViewItemInfo.Companion.createTaskViewAtom
 import com.android.launcher3.pm.UserCache
 import com.android.launcher3.util.AllModulesForTest
-import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext
+import com.android.launcher3.util.SandboxContext
 import com.android.launcher3.util.SplitConfigurationOptions
 import com.android.launcher3.util.TransformingTouchDelegate
 import com.android.launcher3.util.UserIconInfo
 import com.android.quickstep.TaskOverlayFactory
 import com.android.quickstep.TaskOverlayFactory.TaskOverlay
 import com.android.quickstep.recents.di.RecentsDependencies
+import com.android.quickstep.task.thumbnail.TaskContentView
 import com.android.quickstep.task.thumbnail.TaskThumbnailView
 import com.android.quickstep.views.RecentsView
 import com.android.quickstep.views.TaskContainer
@@ -76,12 +77,12 @@
         context.initDaggerComponent(
             DaggerTaskViewItemInfoTest_TestComponent.builder().bindUserCache(userCache)
         )
-        RecentsDependencies.initialize(context)
+        RecentsDependencies.maybeInitialize(context)
     }
 
     @After
     fun tearDown() {
-        RecentsDependencies.destroy()
+        RecentsDependencies.destroy(context)
     }
 
     @Test
@@ -198,6 +199,7 @@
         return TaskContainer(
             taskView,
             task,
+            mock<TaskContentView>(),
             if (enableRefactorTaskThumbnail()) mock<TaskThumbnailView>()
             else mock<TaskThumbnailViewDeprecated>(),
             mock<TaskViewIcon>(),
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
index 3a27bb1..3761044 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
@@ -19,6 +19,7 @@
 import android.animation.AnimatorTestRule
 import android.content.ComponentName
 import android.content.Intent
+import android.os.Process
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
@@ -27,6 +28,7 @@
 import com.android.launcher3.Flags.FLAG_TASKBAR_OVERFLOW
 import com.android.launcher3.R
 import com.android.launcher3.dagger.LauncherAppSingleton
+import com.android.launcher3.model.data.ItemInfo
 import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
 import com.android.launcher3.taskbar.TaskbarViewTestUtil.createHotseatItems
 import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
@@ -111,6 +113,7 @@
     @InjectController lateinit var recentAppsController: TaskbarRecentAppsController
     @InjectController lateinit var bubbleBarViewController: BubbleBarViewController
     @InjectController lateinit var bubbleStashController: BubbleStashController
+    @InjectController lateinit var keyboardQuickSwitchController: KeyboardQuickSwitchController
 
     private var desktopTaskListener: IDesktopTaskListener? = null
 
@@ -209,8 +212,10 @@
         runOnMainSync {
             val taskbarView: TaskbarView =
                 taskbarUnitTestRule.activityContext.dragLayer.findViewById(R.id.taskbar_view)
+            val hotseatItems = createHotseatItems(maxNumberOfTaskbarIcons - initialIconCount)
+
             taskbarView.updateItems(
-                createHotseatItems(maxNumberOfTaskbarIcons - initialIconCount),
+                recentAppsController.updateHotseatItemInfos(hotseatItems as Array<ItemInfo?>),
                 recentAppsController.shownTasks,
             )
         }
@@ -327,13 +332,122 @@
         assertThat(taskbarIconsCentered).isTrue()
     }
 
+    @Test
+    @TaskbarMode(PINNED)
+    fun testPressingOverflowButtonOpensKeyboardQuickSwitch() {
+        val maxNumIconViews = maxNumberOfTaskbarIcons
+        // Assume there are at least all apps and divider icon, as they would appear once running
+        // apps are added, even if not present initially.
+        val initialIconCount = currentNumberOfTaskbarIcons.coerceAtLeast(2)
+
+        val targetOverflowSize = 5
+        val createdTasks = maxNumIconViews - initialIconCount + targetOverflowSize
+        createDesktopTask(createdTasks)
+
+        assertThat(taskbarOverflowIconIndex).isEqualTo(initialIconCount)
+        tapOverflowIcon()
+        // Keyboard quick switch view is shown only after list of recent task is asynchronously
+        // retrieved from the recents model.
+        runOnMainSync { recentsModel.resolvePendingTaskRequests() }
+
+        assertThat(getOnUiThread { keyboardQuickSwitchController.isShownFromTaskbar }).isTrue()
+        assertThat(getOnUiThread { keyboardQuickSwitchController.shownTaskIds() })
+            .containsExactlyElementsIn(0..<createdTasks)
+
+        tapOverflowIcon()
+        assertThat(keyboardQuickSwitchController.isShown).isFalse()
+    }
+
+    @Test
+    @TaskbarMode(PINNED)
+    fun testHotseatItemTasksNotShownInRecents() {
+        val maxNumIconViews = maxNumberOfTaskbarIcons
+        // Assume there are at least all apps and divider icon, as they would appear once running
+        // apps are added, even if not present initially.
+        val initialIconCount = currentNumberOfTaskbarIcons.coerceAtLeast(2)
+        val hotseatItems = createHotseatItems(1)
+
+        val targetOverflowSize = 5
+        val createdTasks = maxNumIconViews - initialIconCount + targetOverflowSize
+        createDesktopTaskWithTasksFromPackages(
+            listOf("fake") +
+                listOf(hotseatItems[0]?.targetPackage ?: "") +
+                List(createdTasks - 2) { "fake" }
+        )
+
+        runOnMainSync {
+            val taskbarView: TaskbarView =
+                taskbarUnitTestRule.activityContext.dragLayer.findViewById(R.id.taskbar_view)
+            taskbarView.updateItems(
+                recentAppsController.updateHotseatItemInfos(hotseatItems as Array<ItemInfo?>),
+                recentAppsController.shownTasks,
+            )
+        }
+
+        assertThat(maxNumberOfTaskbarIcons).isEqualTo(maxNumIconViews)
+        assertThat(currentNumberOfTaskbarIcons).isEqualTo(maxNumIconViews)
+        assertThat(taskbarOverflowIconIndex).isEqualTo(initialIconCount + hotseatItems.size)
+        assertThat(overflowItems)
+            .containsExactlyElementsIn(listOf(0) + (2..targetOverflowSize + 1).toList())
+    }
+
+    @Test
+    @TaskbarMode(PINNED)
+    fun testHotseatItemTasksNotShownInKQS() {
+        val maxNumIconViews = maxNumberOfTaskbarIcons
+        // Assume there are at least all apps and divider icon, as they would appear once running
+        // apps are added, even if not present initially.
+        val initialIconCount = currentNumberOfTaskbarIcons.coerceAtLeast(2)
+        val hotseatItems = createHotseatItems(1)
+
+        val targetOverflowSize = 5
+        val createdTasks = maxNumIconViews - initialIconCount + targetOverflowSize
+        createDesktopTaskWithTasksFromPackages(
+            listOf("fake") +
+                listOf(hotseatItems[0]?.targetPackage ?: "") +
+                List(createdTasks - 2) { "fake" }
+        )
+
+        runOnMainSync {
+            val taskbarView: TaskbarView =
+                taskbarUnitTestRule.activityContext.dragLayer.findViewById(R.id.taskbar_view)
+            taskbarView.updateItems(
+                recentAppsController.updateHotseatItemInfos(hotseatItems as Array<ItemInfo?>),
+                recentAppsController.shownTasks,
+            )
+        }
+
+        tapOverflowIcon()
+        // Keyboard quick switch view is shown only after list of recent task is asynchronously
+        // retrieved from the recents model.
+        runOnMainSync { recentsModel.resolvePendingTaskRequests() }
+
+        assertThat(getOnUiThread { keyboardQuickSwitchController.isShownFromTaskbar }).isTrue()
+        assertThat(getOnUiThread { keyboardQuickSwitchController.shownTaskIds() })
+            .containsExactlyElementsIn(listOf(0) + (2..<createdTasks).toList())
+    }
+
     private fun createDesktopTask(tasksToAdd: Int) {
+        createDesktopTaskWithTasksFromPackages((0..<tasksToAdd).map { "fake" })
+    }
+
+    private fun createDesktopTaskWithTasksFromPackages(packages: List<String>) {
         val tasks =
-            (0..<tasksToAdd).map {
-                Task(Task.TaskKey(it, 0, Intent(), ComponentName("", ""), 0, 2000))
-            }
+            packages.mapIndexed({ index, p ->
+                Task(
+                    Task.TaskKey(
+                        index,
+                        0,
+                        Intent().apply { `package` = p },
+                        ComponentName(p, ""),
+                        Process.myUserHandle().identifier,
+                        2000,
+                    )
+                )
+            })
+
         recentsModel.updateRecentTasks(listOf(DesktopTask(deskId = 0, tasks)))
-        for (task in 1..tasksToAdd) {
+        for (task in 1..tasks.size) {
             desktopTaskListener?.onTasksVisibilityChanged(
                 context.virtualDisplay.display.displayId,
                 task,
@@ -394,6 +508,14 @@
             }
         }
 
+    private fun tapOverflowIcon() {
+        runOnMainSync {
+            val overflowIcon =
+                taskbarViewController.iconViews.firstOrNull { it is TaskbarOverflowView }
+            assertThat(overflowIcon?.callOnClick()).isTrue()
+        }
+    }
+
     /**
      * Adds enough running apps for taskbar to enter overflow of `targetOverflowSize`, and verifies
      * * max number of icons in the taskbar remains unchanged
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarPopupControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarPopupControllerTest.kt
new file mode 100644
index 0000000..6bb3205
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarPopupControllerTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar
+
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import com.android.launcher3.AbstractFloatingView
+import com.android.launcher3.BubbleTextView
+import com.android.launcher3.Flags.FLAG_ENABLE_MULTI_INSTANCE_MENU_TASKBAR
+import com.android.launcher3.Flags.FLAG_ENABLE_PINNING_APP_WITH_CONTEXT_MENU
+import com.android.launcher3.R
+import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
+import com.android.launcher3.taskbar.TaskbarViewTestUtil.createHotseatWorkspaceItem
+import com.android.launcher3.taskbar.TaskbarViewTestUtil.createRecents
+import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
+import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
+import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
+import com.android.launcher3.util.LauncherMultivalentJUnit
+import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
+import com.android.quickstep.util.GroupTask
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(LauncherMultivalentJUnit::class)
+@EmulatedDevices(["pixelFoldable2023", "pixelTablet2023"])
+@DisableFlags(FLAG_ENABLE_MULTI_INSTANCE_MENU_TASKBAR, FLAG_ENABLE_PINNING_APP_WITH_CONTEXT_MENU)
+class TaskbarPopupControllerTest {
+    @get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
+
+    @get:Rule(order = 1) val context = TaskbarWindowSandboxContext.create()
+
+    @get:Rule(order = 2) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
+
+    @InjectController lateinit var popupController: TaskbarPopupController
+
+    private val taskbarContext: TaskbarActivityContext
+        get() = taskbarUnitTestRule.activityContext
+
+    private lateinit var taskbarView: TaskbarView
+    private lateinit var hotseatIcon: BubbleTextView
+    private lateinit var recentTaskIcon: BubbleTextView
+
+    @Before
+    fun setup() {
+        taskbarContext.controllers.uiController.init(taskbarContext.controllers)
+        runOnMainSync { taskbarView = taskbarContext.dragLayer.findViewById(R.id.taskbar_view) }
+
+        val hotseatItems = arrayOf(createHotseatWorkspaceItem())
+        val recentItems = createRecents(2)
+        runOnMainSync {
+            taskbarView.updateItems(hotseatItems, recentItems)
+            hotseatIcon =
+                taskbarView.iconViews.filterIsInstance<BubbleTextView>().first {
+                    it.tag is WorkspaceItemInfo
+                }
+            recentTaskIcon =
+                taskbarView.iconViews.filterIsInstance<BubbleTextView>().first {
+                    it.tag is GroupTask
+                }
+        }
+    }
+
+    @Test
+    fun showForIcon_hotseatItem() {
+        assertThat(hasPopupMenu()).isFalse()
+        runOnMainSync { popupController.showForIcon(hotseatIcon) }
+        assertThat(hasPopupMenu()).isTrue()
+    }
+
+    @Test
+    fun showForIcon_recentTask() {
+        assertThat(hasPopupMenu()).isFalse()
+        runOnMainSync { popupController.showForIcon(recentTaskIcon) }
+        assertThat(hasPopupMenu()).isTrue()
+    }
+
+    private fun hasPopupMenu(): Boolean {
+        return AbstractFloatingView.hasOpenView(
+            taskbarContext,
+            AbstractFloatingView.TYPE_ACTION_POPUP,
+        )
+    }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTest.kt
index 44d31c4..24ed81f 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTest.kt
@@ -19,7 +19,6 @@
 import android.platform.test.flag.junit.FlagsParameterization
 import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
 import android.platform.test.flag.junit.SetFlagsRule
-import com.android.launcher3.Flags.FLAG_TASKBAR_RECENTS_LAYOUT_TRANSITION
 import com.android.launcher3.R
 import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
 import com.android.launcher3.taskbar.TaskbarIconType.ALL_APPS
@@ -34,6 +33,7 @@
 import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.ForceRtl
 import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
 import com.android.launcher3.util.LauncherMultivalentJUnit.Companion.isRunningInRobolectric
+import com.android.window.flags.Flags.FLAG_ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -54,7 +54,7 @@
                 } else {
                     listOf("onDevice") // Unused.
                 }
-            val flags = allCombinationsOf(FLAG_TASKBAR_RECENTS_LAYOUT_TRANSITION)
+            val flags = allCombinationsOf(FLAG_ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION)
             return devices.flatMap { d -> flags.map { f -> arrayOf(d, f) } } // Cartesian product.
         }
     }
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTestUtil.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTestUtil.kt
index df70b10..e52aacf 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTestUtil.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTestUtil.kt
@@ -46,12 +46,14 @@
 
     /** Creates an array of fake hotseat items. */
     fun createHotseatItems(size: Int): Array<ItemInfo> {
-        return Array(size) {
-            WorkspaceItemInfo(
-                    AppInfo(TEST_COMPONENT, "Test App $it", Process.myUserHandle(), Intent())
-                )
-                .apply { id = it }
-        }
+        return Array(size) { createHotseatWorkspaceItem(it) }
+    }
+
+    fun createHotseatWorkspaceItem(id: Int = 0): WorkspaceItemInfo {
+        return WorkspaceItemInfo(
+                AppInfo(TEST_COMPONENT, "Test App $id", Process.myUserHandle(), Intent())
+            )
+            .apply { this.id = id }
     }
 
     /** Creates a list of fake recent tasks. */
@@ -75,13 +77,13 @@
 }
 
 /** A `Truth` [Subject] with extensions for verifying [TaskbarView]. */
-class TaskbarViewSubject(failureMetadata: FailureMetadata, private val view: TaskbarView) :
+class TaskbarViewSubject(failureMetadata: FailureMetadata, private val view: TaskbarView?) :
     Subject(failureMetadata, view) {
 
     /** Verifies that the types of icons match [expectedTypes] in order. */
     fun hasIconTypes(vararg expectedTypes: TaskbarIconType) {
         val actualTypes =
-            view.iconViews.map {
+            view?.iconViews?.map {
                 when (it) {
                     view.allAppsButtonContainer -> ALL_APPS
                     view.taskbarDividerViewContainer -> DIVIDER
@@ -100,7 +102,7 @@
     /** Verifies that recents from [startIndex] have IDs that match [expectedIds] in order. */
     fun hasRecentsOrder(startIndex: Int, expectedIds: List<Int>) {
         val actualIds =
-            view.iconViews.slice(startIndex..<startIndex + expectedIds.size).flatMap {
+            view?.iconViews?.slice(startIndex..<startIndex + expectedIds.size)?.flatMap {
                 assertThat(it.tag).isInstanceOf(GroupTask::class.java)
                 (it.tag as GroupTask).tasks.map { task -> task.key.id }
             }
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewWithLayoutTransitionTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewWithLayoutTransitionTest.kt
index 4b6d5e5..2df4fab 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewWithLayoutTransitionTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewWithLayoutTransitionTest.kt
@@ -19,12 +19,14 @@
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
 import android.view.View
-import com.android.launcher3.Flags.FLAG_TASKBAR_RECENTS_LAYOUT_TRANSITION
+import com.android.launcher3.Flags.FLAG_TASKBAR_OVERFLOW
 import com.android.launcher3.R
+import com.android.launcher3.statehandlers.DesktopVisibilityController
 import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
 import com.android.launcher3.taskbar.TaskbarIconType.ALL_APPS
 import com.android.launcher3.taskbar.TaskbarIconType.DIVIDER
 import com.android.launcher3.taskbar.TaskbarIconType.HOTSEAT
+import com.android.launcher3.taskbar.TaskbarIconType.OVERFLOW
 import com.android.launcher3.taskbar.TaskbarIconType.RECENT
 import com.android.launcher3.taskbar.TaskbarViewTestUtil.assertThat
 import com.android.launcher3.taskbar.TaskbarViewTestUtil.createHotseatItems
@@ -34,15 +36,17 @@
 import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
 import com.android.launcher3.util.LauncherMultivalentJUnit
 import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
+import com.android.window.flags.Flags.FLAG_ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.whenever
 
 @RunWith(LauncherMultivalentJUnit::class)
 @EmulatedDevices(["pixelFoldable2023", "pixelTablet2023"])
-@EnableFlags(FLAG_TASKBAR_RECENTS_LAYOUT_TRANSITION)
+@EnableFlags(FLAG_ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION, FLAG_TASKBAR_OVERFLOW)
 class TaskbarViewWithLayoutTransitionTest {
 
     @get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
@@ -54,6 +58,12 @@
     private val iconViews: Array<View>
         get() = taskbarView.iconViews
 
+    private val desktopVisibilityController: DesktopVisibilityController
+        get() = DesktopVisibilityController.INSTANCE[context]
+
+    private val maxShownRecents: Int
+        get() = taskbarView.maxNumIconViews - 2 // Account for All Apps and Divider.
+
     @Before
     fun obtainView() {
         taskbarView = taskbarUnitTestRule.activityContext.dragLayer.findViewById(R.id.taskbar_view)
@@ -225,4 +235,118 @@
             assertThat(expectedViewToRemove in iconViews).isFalse()
         }
     }
+
+    @Test
+    fun testUpdateItems_desktopMode_hotseatItem_noDivider() {
+        whenever(desktopVisibilityController.isInDesktopMode(context.displayId)).thenReturn(true)
+        runOnMainSync { taskbarView.updateItems(createHotseatItems(1), emptyList()) }
+        assertThat(taskbarView).hasIconTypes(ALL_APPS, HOTSEAT)
+    }
+
+    @Test
+    @ForceRtl
+    fun testUpdateItems_rtlAndDesktopMode_hotseatItem_noDivider() {
+        whenever(desktopVisibilityController.isInDesktopMode(context.displayId)).thenReturn(true)
+        runOnMainSync { taskbarView.updateItems(createHotseatItems(1), emptyList()) }
+        assertThat(taskbarView).hasIconTypes(HOTSEAT, ALL_APPS)
+    }
+
+    @Test
+    fun testUpdateItems_desktopMode_recentItem_hasDivider() {
+        whenever(desktopVisibilityController.isInDesktopMode(context.displayId)).thenReturn(true)
+        runOnMainSync { taskbarView.updateItems(emptyArray(), createRecents(1)) }
+        assertThat(taskbarView).hasIconTypes(ALL_APPS, DIVIDER, RECENT)
+    }
+
+    @Test
+    @ForceRtl
+    fun testUpdateItems_rtlAndDesktopMode_recentItem_hasDivider() {
+        whenever(desktopVisibilityController.isInDesktopMode(context.displayId)).thenReturn(true)
+        runOnMainSync { taskbarView.updateItems(emptyArray(), createRecents(1)) }
+        assertThat(taskbarView).hasIconTypes(RECENT, DIVIDER, ALL_APPS)
+    }
+
+    @Test
+    fun testUpdateItems_maxRecents_noOverflow() {
+        runOnMainSync { taskbarView.updateItems(emptyArray(), createRecents(maxShownRecents)) }
+        assertThat(taskbarView).hasIconTypes(ALL_APPS, DIVIDER, *RECENT * maxShownRecents)
+    }
+
+    @Test
+    fun testUpdateItems_moreThanMaxRecents_overflowShownBeforeRecents() {
+        val recentsSize = maxShownRecents + 2
+        runOnMainSync { taskbarView.updateItems(emptyArray(), createRecents(recentsSize)) }
+
+        val expectedNumRecents = RECENT * getExpectedNumRecentsWithOverflow()
+        assertThat(taskbarView).hasIconTypes(ALL_APPS, DIVIDER, OVERFLOW, *expectedNumRecents)
+    }
+
+    @Test
+    @ForceRtl
+    fun testUpdateItems_rtl_moreThanMaxRecents_overflowShownAfterRecents() {
+        val recentsSize = maxShownRecents + 2
+        runOnMainSync { taskbarView.updateItems(emptyArray(), createRecents(recentsSize)) }
+
+        val expectedRecents = RECENT * getExpectedNumRecentsWithOverflow()
+        assertThat(taskbarView).hasIconTypes(*expectedRecents, OVERFLOW, DIVIDER, ALL_APPS)
+    }
+
+    @Test
+    fun testUpdateItems_moreThanMaxRecentsWithHotseat_fewerRecentsShown() {
+        val hotseatSize = 4
+        val recentsSize = maxShownRecents + 2
+        runOnMainSync {
+            taskbarView.updateItems(createHotseatItems(hotseatSize), createRecents(recentsSize))
+        }
+
+        val expectedRecents = RECENT * getExpectedNumRecentsWithOverflow(hotseatSize)
+        assertThat(taskbarView)
+            .hasIconTypes(ALL_APPS, *HOTSEAT * hotseatSize, DIVIDER, OVERFLOW, *expectedRecents)
+    }
+
+    @Test
+    @ForceRtl
+    fun testUpdateItems_rtl_moreThanMaxRecentsWithHotseat_fewerRecentsShown() {
+        val hotseatSize = 4
+        val recentsSize = maxShownRecents + 2
+        runOnMainSync {
+            taskbarView.updateItems(createHotseatItems(hotseatSize), createRecents(recentsSize))
+        }
+
+        val expectedRecents = RECENT * getExpectedNumRecentsWithOverflow(hotseatSize)
+        assertThat(taskbarView)
+            .hasIconTypes(*expectedRecents, OVERFLOW, DIVIDER, *HOTSEAT * hotseatSize, ALL_APPS)
+    }
+
+    @Test
+    fun testUpdateItems_moreThanMaxRecents_verifyShownRecentsOrder() {
+        val recentsSize = maxShownRecents + 2
+        runOnMainSync { taskbarView.updateItems(emptyArray(), createRecents(recentsSize)) }
+
+        val expectedNumRecents = getExpectedNumRecentsWithOverflow()
+        assertThat(taskbarView)
+            .hasRecentsOrder(
+                startIndex = iconViews.size - expectedNumRecents,
+                expectedIds = ((recentsSize - expectedNumRecents)..<recentsSize).toList(),
+            )
+    }
+
+    @Test
+    @ForceRtl
+    fun testUpdateItems_rtl_moreThanMaxRecents_verifyShownRecentsReversed() {
+        val recentsSize = maxShownRecents + 2
+        runOnMainSync { taskbarView.updateItems(emptyArray(), createRecents(recentsSize)) }
+
+        val expectedNumRecents = getExpectedNumRecentsWithOverflow()
+        assertThat(taskbarView)
+            .hasRecentsOrder(
+                startIndex = 0,
+                expectedIds = ((recentsSize - expectedNumRecents)..<recentsSize).toList().reversed(),
+            )
+    }
+
+    /** Returns the number of expected recents outside of the overflow based on [hotseatSize]. */
+    private fun getExpectedNumRecentsWithOverflow(hotseatSize: Int = 0): Int {
+        return maxShownRecents - hotseatSize - 1 // Account for overflow.
+    }
 }
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 a456fb9..61a6975 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
@@ -20,8 +20,10 @@
 import android.graphics.Color
 import android.graphics.Path
 import android.graphics.PointF
+import android.graphics.Rect
 import android.graphics.drawable.ColorDrawable
 import android.view.LayoutInflater
+import android.view.MotionEvent
 import android.view.View
 import android.view.View.INVISIBLE
 import android.view.View.VISIBLE
@@ -35,10 +37,13 @@
 import androidx.test.filters.SmallTest
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.launcher3.R
+import com.android.launcher3.taskbar.TaskbarInsetsController
 import com.android.launcher3.taskbar.bubbles.BubbleBarBubble
 import com.android.launcher3.taskbar.bubbles.BubbleBarOverflow
 import com.android.launcher3.taskbar.bubbles.BubbleBarParentViewHeightUpdateNotifier
 import com.android.launcher3.taskbar.bubbles.BubbleBarView
+import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
+import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController
 import com.android.launcher3.taskbar.bubbles.BubbleView
 import com.android.launcher3.taskbar.bubbles.flyout.BubbleBarFlyoutController
 import com.android.launcher3.taskbar.bubbles.flyout.BubbleBarFlyoutMessage
@@ -46,8 +51,10 @@
 import com.android.launcher3.taskbar.bubbles.flyout.FlyoutCallbacks
 import com.android.launcher3.taskbar.bubbles.flyout.FlyoutScheduler
 import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.BubbleLauncherState
 import com.android.wm.shell.shared.animation.PhysicsAnimator
 import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
 import com.android.wm.shell.shared.bubbles.BubbleInfo
 import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.Semaphore
@@ -56,13 +63,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.kotlin.any
-import org.mockito.kotlin.atLeastOnce
-import org.mockito.kotlin.eq
-import org.mockito.kotlin.mock
-import org.mockito.kotlin.never
-import org.mockito.kotlin.verify
-import org.mockito.kotlin.whenever
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -78,7 +78,7 @@
     private lateinit var bubble: BubbleBarBubble
     private lateinit var bubbleBarView: BubbleBarView
     private lateinit var flyoutContainer: FrameLayout
-    private lateinit var bubbleStashController: BubbleStashController
+    private lateinit var bubbleStashController: FakeBubbleStashController
     private lateinit var flyoutController: BubbleBarFlyoutController
     private val emptyRunnable = Runnable {}
 
@@ -89,6 +89,7 @@
     fun setUp() {
         animatorScheduler = TestBubbleBarViewAnimatorScheduler()
         bubbleBarParentViewController = TestBubbleBarParentViewHeightUpdateNotifier()
+        bubbleStashController = FakeBubbleStashController()
         PhysicsAnimatorTestUtils.prepareForTest()
         setupFlyoutController()
     }
@@ -96,11 +97,10 @@
     @Test
     fun animateBubbleInForStashed() {
         setUpBubbleBar()
-        setUpBubbleStashController()
 
         val handle = View(context)
         val handleAnimator = PhysicsAnimator.getInstance(handle)
-        whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+        bubbleStashController.handleAnimator = handleAnimator
 
         val animator =
             BubbleBarViewAnimator(
@@ -147,17 +147,16 @@
         assertThat(handle.translationY).isEqualTo(0)
         assertThat(bubbleBarView.alpha).isEqualTo(0)
         assertThat(animator.isAnimating).isFalse()
-        verify(bubbleStashController).stashBubbleBarImmediate()
+        assertThat(bubbleStashController.isStashed).isTrue()
     }
 
     @Test
     fun animateBubbleInForStashed_tapAnimatingBubble() {
         setUpBubbleBar()
-        setUpBubbleStashController()
 
         val handle = View(context)
         val handleAnimator = PhysicsAnimator.getInstance(handle)
-        whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+        bubbleStashController.handleAnimator = handleAnimator
 
         val animator =
             BubbleBarViewAnimator(
@@ -187,7 +186,7 @@
         assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_TASKBAR)
         assertThat(animator.isAnimating).isTrue()
 
-        verify(bubbleStashController, atLeastOnce()).updateTaskbarTouchRegion()
+        assertThat(bubbleStashController.taskbarTouchRegionUpdated).isTrue()
         assertThat(bubbleBarParentViewController.timesInvoked).isEqualTo(1)
         waitForFlyoutToShow()
 
@@ -211,11 +210,10 @@
     @Test
     fun animateBubbleInForStashed_touchTaskbarArea_whileShowing() {
         setUpBubbleBar()
-        setUpBubbleStashController()
 
         val handle = View(context)
         val handleAnimator = PhysicsAnimator.getInstance(handle)
-        whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+        bubbleStashController.handleAnimator = handleAnimator
 
         val animator =
             BubbleBarViewAnimator(
@@ -241,7 +239,6 @@
         // verify the hide bubble animation is pending
         assertThat(animatorScheduler.delayedBlock).isNotNull()
 
-        whenever(bubbleStashController.isStashed).thenReturn(true)
         InstrumentationRegistry.getInstrumentation().runOnMainSync {
             animator.onStashStateChangingWhileAnimating()
         }
@@ -255,9 +252,10 @@
         // verify that the hide animation was canceled
         assertThat(animatorScheduler.delayedBlock).isNull()
         assertThat(animator.isAnimating).isFalse()
+        assertThat(bubbleStashController.animationInterrupted).isTrue()
         assertThat(bubbleBarView.scaleX).isEqualTo(1)
         assertThat(bubbleBarView.scaleY).isEqualTo(1)
-        verify(bubbleStashController).onNewBubbleAnimationInterrupted(eq(true), any())
+        assertThat(bubbleStashController.isStashed).isTrue()
 
         // PhysicsAnimatorTestUtils posts the cancellation to the main thread so we need to wait
         // again
@@ -268,11 +266,10 @@
     @Test
     fun animateBubbleInForStashed_touchTaskbarArea_whileHiding() {
         setUpBubbleBar()
-        setUpBubbleStashController()
 
         val handle = View(context)
         val handleAnimator = PhysicsAnimator.getInstance(handle)
-        whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+        bubbleStashController.handleAnimator = handleAnimator
 
         val animator =
             BubbleBarViewAnimator(
@@ -310,7 +307,7 @@
         }
         assertThat(bubbleBarParentViewController.timesInvoked).isEqualTo(2)
         assertThat(animator.isAnimating).isFalse()
-        verify(bubbleStashController).onNewBubbleAnimationInterrupted(any(), any())
+        assertThat(bubbleStashController.animationInterrupted).isTrue()
 
         // PhysicsAnimatorTestUtils posts the cancellation to the main thread so we need to wait
         // again
@@ -321,11 +318,10 @@
     @Test
     fun animateBubbleInForStashed_showAnimationCanceled() {
         setUpBubbleBar()
-        setUpBubbleStashController()
 
         val handle = View(context)
         val handleAnimator = PhysicsAnimator.getInstance(handle)
-        whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+        bubbleStashController.handleAnimator = handleAnimator
 
         val animator =
             BubbleBarViewAnimator(
@@ -359,11 +355,10 @@
     @Test
     fun animateBubbleInForStashed_autoExpanding() {
         setUpBubbleBar()
-        setUpBubbleStashController()
 
         val handle = View(context)
         val handleAnimator = PhysicsAnimator.getInstance(handle)
-        whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+        bubbleStashController.handleAnimator = handleAnimator
 
         var notifiedExpanded = false
         val onExpanded = Runnable { notifiedExpanded = true }
@@ -399,18 +394,17 @@
         // verify there is no hide animation
         assertThat(animatorScheduler.delayedBlock).isNull()
 
-        verify(bubbleStashController).showBubbleBarImmediate()
+        assertThat(bubbleStashController.isStashed).isFalse()
         assertThat(notifiedExpanded).isTrue()
     }
 
     @Test
     fun animateBubbleInForStashed_expandedWhileAnimatingIn() {
         setUpBubbleBar()
-        setUpBubbleStashController()
 
         val handle = View(context)
         val handleAnimator = PhysicsAnimator.getInstance(handle)
-        whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+        bubbleStashController.handleAnimator = handleAnimator
 
         var notifiedExpanded = false
         val onExpanded = Runnable { notifiedExpanded = true }
@@ -459,11 +453,10 @@
     @Test
     fun animateBubbleInForStashed_expandedWhileFullyIn() {
         setUpBubbleBar()
-        setUpBubbleStashController()
 
         val handle = View(context)
         val handleAnimator = PhysicsAnimator.getInstance(handle)
-        whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+        bubbleStashController.handleAnimator = handleAnimator
 
         var notifiedExpanded = false
         val onExpanded = Runnable { notifiedExpanded = true }
@@ -515,13 +508,11 @@
     @Test
     fun animateToInitialState_inApp() {
         setUpBubbleBar()
-        setUpBubbleStashController()
-        whenever(bubbleStashController.bubbleBarTranslationY)
-            .thenReturn(BAR_TRANSLATION_Y_FOR_TASKBAR)
+        bubbleStashController.launcherState = BubbleLauncherState.IN_APP
 
         val handle = View(context)
         val handleAnimator = PhysicsAnimator.getInstance(handle)
-        whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+        bubbleStashController.handleAnimator = handleAnimator
 
         val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
 
@@ -570,19 +561,17 @@
         assertThat(bubbleBarView.visibility).isEqualTo(VISIBLE)
         assertThat(notifiedBubbleBarVisible).isTrue()
 
-        verify(bubbleStashController).stashBubbleBarImmediate()
+        assertThat(bubbleStashController.isStashed).isTrue()
     }
 
     @Test
     fun animateToInitialState_whileDragging_inApp() {
         setUpBubbleBar()
-        setUpBubbleStashController()
-        whenever(bubbleStashController.bubbleBarTranslationY)
-            .thenReturn(BAR_TRANSLATION_Y_FOR_TASKBAR)
+        bubbleStashController.launcherState = BubbleLauncherState.IN_APP
 
         val handle = View(context)
         val handleAnimator = PhysicsAnimator.getInstance(handle)
-        whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+        bubbleStashController.handleAnimator = handleAnimator
 
         val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
 
@@ -634,20 +623,16 @@
         assertThat(handle.translationY).isEqualTo(0)
         assertThat(bubbleBarView.visibility).isEqualTo(VISIBLE)
         assertThat(notifiedBubbleBarVisible).isTrue()
-
-        verify(bubbleStashController, never()).stashBubbleBarImmediate()
     }
 
     @Test
     fun animateToInitialState_inApp_autoExpanding() {
         setUpBubbleBar()
-        setUpBubbleStashController()
-        whenever(bubbleStashController.bubbleBarTranslationY)
-            .thenReturn(BAR_TRANSLATION_Y_FOR_TASKBAR)
+        bubbleStashController.launcherState = BubbleLauncherState.IN_APP
 
         val handle = View(context)
         val handleAnimator = PhysicsAnimator.getInstance(handle)
-        whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+        bubbleStashController.handleAnimator = handleAnimator
 
         val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
 
@@ -677,16 +662,14 @@
         assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_TASKBAR)
 
         assertThat(animatorScheduler.delayedBlock).isNull()
-        verify(bubbleStashController).showBubbleBarImmediate()
+        assertThat(bubbleStashController.isStashed).isFalse()
         assertThat(notifiedExpanded).isTrue()
     }
 
     @Test
     fun animateToInitialState_inHome() {
         setUpBubbleBar()
-        setUpBubbleStashController()
-        whenever(bubbleStashController.bubbleBarTranslationY)
-            .thenReturn(BAR_TRANSLATION_Y_FOR_HOTSEAT)
+        bubbleStashController.launcherState = BubbleLauncherState.HOME
 
         val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
 
@@ -725,15 +708,13 @@
         assertThat(bubbleBarView.alpha).isEqualTo(1)
         assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_HOTSEAT)
 
-        verify(bubbleStashController).showBubbleBarImmediate()
+        assertThat(bubbleStashController.isStashed).isFalse()
     }
 
     @Test
     fun animateToInitialState_expandedWhileAnimatingIn() {
         setUpBubbleBar()
-        setUpBubbleStashController()
-        whenever(bubbleStashController.bubbleBarTranslationY)
-            .thenReturn(BAR_TRANSLATION_Y_FOR_HOTSEAT)
+        bubbleStashController.launcherState = BubbleLauncherState.HOME
 
         var notifiedExpanded = false
         val onExpanded = Runnable { notifiedExpanded = true }
@@ -775,16 +756,14 @@
 
         verifyBubbleBarIsExpandedWithTranslation(BAR_TRANSLATION_Y_FOR_HOTSEAT)
         assertThat(animator.isAnimating).isFalse()
-        verify(bubbleStashController).showBubbleBarImmediate()
+        assertThat(bubbleStashController.isStashed).isFalse()
         assertThat(notifiedExpanded).isTrue()
     }
 
     @Test
     fun animateToInitialState_expandedWhileFullyIn() {
         setUpBubbleBar()
-        setUpBubbleStashController()
-        whenever(bubbleStashController.bubbleBarTranslationY)
-            .thenReturn(BAR_TRANSLATION_Y_FOR_HOTSEAT)
+        bubbleStashController.launcherState = BubbleLauncherState.HOME
 
         var notifiedExpanded = false
         val onExpanded = Runnable { notifiedExpanded = true }
@@ -831,9 +810,7 @@
     @Test
     fun animateBubbleBarForCollapsed() {
         setUpBubbleBar()
-        setUpBubbleStashController()
-        whenever(bubbleStashController.bubbleBarTranslationY)
-            .thenReturn(BAR_TRANSLATION_Y_FOR_HOTSEAT)
+        bubbleStashController.launcherState = BubbleLauncherState.HOME
 
         val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
 
@@ -877,16 +854,13 @@
         assertThat(bubbleBarParentViewController.timesInvoked).isEqualTo(2)
         // the bubble bar translation y should be back to its initial value
         assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_HOTSEAT)
-
-        verify(bubbleStashController).showBubbleBarImmediate()
+        assertThat(bubbleStashController.isStashed).isFalse()
     }
 
     @Test
     fun animateBubbleBarForCollapsed_autoExpanding() {
         setUpBubbleBar()
-        setUpBubbleStashController()
-        whenever(bubbleStashController.bubbleBarTranslationY)
-            .thenReturn(BAR_TRANSLATION_Y_FOR_HOTSEAT)
+        bubbleStashController.launcherState = BubbleLauncherState.HOME
 
         val semaphore = Semaphore(0)
         var notifiedExpanded = false
@@ -933,16 +907,14 @@
 
         assertThat(animator.isAnimating).isFalse()
         assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_HOTSEAT)
-        verify(bubbleStashController).showBubbleBarImmediate()
+        assertThat(bubbleStashController.isStashed).isFalse()
         assertThat(notifiedExpanded).isTrue()
     }
 
     @Test
     fun animateBubbleBarForCollapsed_expandingWhileAnimatingIn() {
         setUpBubbleBar()
-        setUpBubbleStashController()
-        whenever(bubbleStashController.bubbleBarTranslationY)
-            .thenReturn(BAR_TRANSLATION_Y_FOR_HOTSEAT)
+        bubbleStashController.launcherState = BubbleLauncherState.HOME
 
         val semaphore = Semaphore(0)
         var notifiedExpanded = false
@@ -1001,16 +973,14 @@
         assertThat(animator.isAnimating).isFalse()
         assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_HOTSEAT)
         assertThat(bubbleBarView.isExpanded).isTrue()
-        verify(bubbleStashController).showBubbleBarImmediate()
+        assertThat(bubbleStashController.isStashed).isFalse()
         assertThat(notifiedExpanded).isTrue()
     }
 
     @Test
     fun animateBubbleBarForCollapsed_expandingWhileFullyIn() {
         setUpBubbleBar()
-        setUpBubbleStashController()
-        whenever(bubbleStashController.bubbleBarTranslationY)
-            .thenReturn(BAR_TRANSLATION_Y_FOR_HOTSEAT)
+        bubbleStashController.launcherState = BubbleLauncherState.HOME
 
         val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
 
@@ -1064,18 +1034,17 @@
         assertThat(animator.isAnimating).isFalse()
         assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_HOTSEAT)
         assertThat(bubbleBarView.isExpanded).isTrue()
-        verify(bubbleStashController).showBubbleBarImmediate()
+        assertThat(bubbleStashController.isStashed).isFalse()
         assertThat(notifiedExpanded).isTrue()
     }
 
     @Test
     fun interruptAnimation_whileAnimatingIn() {
         setUpBubbleBar()
-        setUpBubbleStashController()
 
         val handle = View(context)
         val handleAnimator = PhysicsAnimator.getInstance(handle)
-        whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+        bubbleStashController.handleAnimator = handleAnimator
 
         val animator =
             BubbleBarViewAnimator(
@@ -1136,17 +1105,16 @@
         assertThat(handle.translationY).isEqualTo(0)
         assertThat(bubbleBarView.alpha).isEqualTo(0)
         assertThat(animator.isAnimating).isFalse()
-        verify(bubbleStashController).stashBubbleBarImmediate()
+        assertThat(bubbleStashController.isStashed).isTrue()
     }
 
     @Test
     fun interruptAnimation_whileIn() {
         setUpBubbleBar()
-        setUpBubbleStashController()
 
         val handle = View(context)
         val handleAnimator = PhysicsAnimator.getInstance(handle)
-        whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+        bubbleStashController.handleAnimator = handleAnimator
 
         val animator =
             BubbleBarViewAnimator(
@@ -1213,17 +1181,16 @@
         assertThat(handle.translationY).isEqualTo(0)
         assertThat(bubbleBarView.alpha).isEqualTo(0)
         assertThat(animator.isAnimating).isFalse()
-        verify(bubbleStashController).stashBubbleBarImmediate()
+        assertThat(bubbleStashController.isStashed).isTrue()
     }
 
     @Test
     fun interruptAnimation_whileAnimatingOut_whileCollapsingFlyout() {
         setUpBubbleBar()
-        setUpBubbleStashController()
 
         val handle = View(context)
         val handleAnimator = PhysicsAnimator.getInstance(handle)
-        whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+        bubbleStashController.handleAnimator = handleAnimator
 
         val animator =
             BubbleBarViewAnimator(
@@ -1301,17 +1268,16 @@
         assertThat(handle.translationY).isEqualTo(0)
         assertThat(bubbleBarView.alpha).isEqualTo(0)
         assertThat(animator.isAnimating).isFalse()
-        verify(bubbleStashController).stashBubbleBarImmediate()
+        assertThat(bubbleStashController.isStashed).isTrue()
     }
 
     @Test
     fun interruptAnimation_whileAnimatingOut_barToHandle() {
         setUpBubbleBar()
-        setUpBubbleStashController()
 
         val handle = View(context)
         val handleAnimator = PhysicsAnimator.getInstance(handle)
-        whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+        bubbleStashController.handleAnimator = handleAnimator
 
         val animator =
             BubbleBarViewAnimator(
@@ -1416,17 +1382,16 @@
         assertThat(handle.translationY).isEqualTo(0)
         assertThat(bubbleBarView.alpha).isEqualTo(0)
         assertThat(animator.isAnimating).isFalse()
-        verify(bubbleStashController).stashBubbleBarImmediate()
+        assertThat(bubbleStashController.isStashed).isTrue()
     }
 
     @Test
     fun interruptForIme() {
         setUpBubbleBar()
-        setUpBubbleStashController()
 
         val handle = View(context)
         val handleAnimator = PhysicsAnimator.getInstance(handle)
-        whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+        bubbleStashController.handleAnimator = handleAnimator
 
         val animator =
             BubbleBarViewAnimator(
@@ -1457,7 +1422,8 @@
         // verify that the hide animation was canceled
         assertThat(animatorScheduler.delayedBlock).isNull()
         assertThat(animator.isAnimating).isFalse()
-        verify(bubbleStashController).onNewBubbleAnimationInterrupted(eq(true), any())
+        assertThat(bubbleStashController.animationInterrupted).isTrue()
+        assertThat(bubbleStashController.isStashed).isTrue()
 
         // PhysicsAnimatorTestUtils posts the cancellation to the main thread so we need to wait
         // again
@@ -1510,17 +1476,6 @@
         InstrumentationRegistry.getInstrumentation().waitForIdleSync()
     }
 
-    private fun setUpBubbleStashController() {
-        bubbleStashController = mock<BubbleStashController>()
-        whenever(bubbleStashController.isStashed).thenReturn(true)
-        whenever(bubbleStashController.getDiffBetweenHandleAndBarCenters())
-            .thenReturn(DIFF_BETWEEN_HANDLE_AND_BAR_CENTERS)
-        whenever(bubbleStashController.getStashedHandleTranslationForNewBubbleAnimation())
-            .thenReturn(HANDLE_TRANSLATION)
-        whenever(bubbleStashController.bubbleBarTranslationYForTaskbar)
-            .thenReturn(BAR_TRANSLATION_Y_FOR_TASKBAR)
-    }
-
     private fun setupFlyoutController() {
         flyoutContainer = FrameLayout(context)
         val flyoutPositioner =
@@ -1627,6 +1582,91 @@
             timesInvoked++
         }
     }
+
+    private class FakeBubbleStashController : BubbleStashController {
+
+        var handleAnimator: PhysicsAnimator<View>? = null
+        var taskbarTouchRegionUpdated = false
+            private set
+
+        var animationInterrupted = false
+            private set
+
+        private var _isStashed = true
+
+        override var launcherState = BubbleLauncherState.HOME
+        override val isStashed: Boolean
+            get() = _isStashed
+
+        override var bubbleBarVerticalCenterForHome = 0
+        override var isSysuiLocked = false
+        override val isTransientTaskBar = true
+        override val hasHandleView = true
+        override val bubbleBarTranslationYForTaskbar = BAR_TRANSLATION_Y_FOR_TASKBAR
+        override val bubbleBarTranslationYForHotseat = BAR_TRANSLATION_Y_FOR_HOTSEAT
+        override var inAppDisplayOverrideProgress = 0f
+
+        override fun init(
+            taskbarInsetsController: TaskbarInsetsController,
+            bubbleBarViewController: BubbleBarViewController,
+            bubbleStashedHandleViewController: BubbleStashedHandleViewController?,
+            controllersAfterInitAction: BubbleStashController.ControllersAfterInitAction,
+        ) {}
+
+        override fun showBubbleBarImmediate() {
+            _isStashed = false
+        }
+
+        override fun showBubbleBarImmediate(bubbleBarTranslationY: Float) {
+            _isStashed = false
+        }
+
+        override fun stashBubbleBarImmediate() {
+            _isStashed = true
+        }
+
+        override fun getTouchableHeight() = 100
+
+        override fun isBubbleBarVisible() = true
+
+        override fun onNewBubbleAnimationInterrupted(
+            isStashed: Boolean,
+            bubbleBarTranslationY: Float,
+        ) {
+            _isStashed = isStashed
+            animationInterrupted = true
+        }
+
+        override fun isEventOverBubbleBarViews(ev: MotionEvent) = false
+
+        override fun setBubbleBarLocation(bubbleBarLocation: BubbleBarLocation) {}
+
+        override fun stashBubbleBar() {
+            _isStashed = true
+        }
+
+        override fun showBubbleBar(expandBubbles: Boolean, bubbleBarGesture: Boolean) {
+            _isStashed = false
+        }
+
+        override fun getDiffBetweenHandleAndBarCenters() = DIFF_BETWEEN_HANDLE_AND_BAR_CENTERS
+
+        override fun getStashedHandleTranslationForNewBubbleAnimation() = HANDLE_TRANSLATION
+
+        override fun getStashedHandlePhysicsAnimator(): PhysicsAnimator<View>? {
+            return handleAnimator
+        }
+
+        override fun updateTaskbarTouchRegion() {
+            taskbarTouchRegionUpdated = true
+        }
+
+        override fun setHandleTranslationY(translationY: Float) {}
+
+        override fun getHandleTranslationY() = 0f
+
+        override fun getHandleBounds(bounds: Rect) {}
+    }
 }
 
 private const val DIFF_BETWEEN_HANDLE_AND_BAR_CENTERS = -20f
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/MockedRecentsModelHelper.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/MockedRecentsModelHelper.kt
index a7bfa9a..5f7b360 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/MockedRecentsModelHelper.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/MockedRecentsModelHelper.kt
@@ -19,6 +19,7 @@
 import com.android.quickstep.RecentsModel
 import com.android.quickstep.RecentsModel.RecentTasksChangedListener
 import com.android.quickstep.TaskIconCache
+import com.android.quickstep.TaskThumbnailCache
 import com.android.quickstep.util.GroupTask
 import java.util.function.Consumer
 import org.mockito.kotlin.any
@@ -27,9 +28,11 @@
 import org.mockito.kotlin.doReturn
 import org.mockito.kotlin.mock
 
-/** Helper class to mock the {@link RecentsModel} object in test */
+/** Helper class to mock the [RecentsModel] object in test */
 class MockedRecentsModelHelper {
     private val mockIconCache: TaskIconCache = mock()
+    private val mockThumbnailCache: TaskThumbnailCache = mock()
+
     var taskListId = 0
     var recentTasksChangedListener: RecentTasksChangedListener? = null
     var taskRequests: MutableList<(List<GroupTask>) -> Unit> = mutableListOf()
@@ -37,6 +40,8 @@
     val mockRecentsModel: RecentsModel = mock {
         on { iconCache } doReturn mockIconCache
 
+        on { thumbnailCache } doReturn mockThumbnailCache
+
         on { unregisterRecentTasksChangedListener() } doAnswer { recentTasksChangedListener = null }
 
         on { registerRecentTasksChangedListener(any<RecentTasksChangedListener>()) } doAnswer
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarModeRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarModeRule.kt
index 3cf912c..f225807 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarModeRule.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarModeRule.kt
@@ -20,7 +20,6 @@
 import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode
 import com.android.launcher3.taskbar.rules.TaskbarModeRule.TaskbarMode
 import com.android.launcher3.util.DisplayController
-import com.android.launcher3.util.MainThreadInitializedObject
 import com.android.launcher3.util.NavigationMode
 import org.junit.rules.TestRule
 import org.junit.runner.Description
@@ -31,8 +30,8 @@
 /**
  * Allows tests to specify which Taskbar [Mode] to run under.
  *
- * [context] should match the test's target context, so that [MainThreadInitializedObject] instances
- * are properly sandboxed.
+ * [context] should match the test's target context, so that Dagger singleton instances are properly
+ * sandboxed.
  *
  * Annotate tests with [TaskbarMode] to set a mode. If the annotation is omitted for any tests, this
  * rule is a no-op.
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
index 0204b2d..2dacf69 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
@@ -113,8 +113,8 @@
                                 object : TaskbarNavButtonCallbacks {},
                                 RecentsDisplayModel.INSTANCE.get(context),
                             ) {
-                            override fun recreateTaskbar() {
-                                super.recreateTaskbar()
+                            override fun recreateTaskbars() {
+                                super.recreateTaskbars()
                                 if (currentActivityContext != null) {
                                     injectControllers()
                                     controllerInjectionCallback.invoke()
@@ -146,7 +146,7 @@
     }
 
     /** Simulates Taskbar recreation lifecycle. */
-    fun recreateTaskbar() = instrumentation.runOnMainSync { taskbarManager.recreateTaskbar() }
+    fun recreateTaskbar() = instrumentation.runOnMainSync { taskbarManager.recreateTaskbars() }
 
     private fun injectControllers() {
         val bubbleControllerTypes =
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt
index 95e8980..d96e06e 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt
@@ -26,11 +26,11 @@
 import com.android.launcher3.dagger.ApplicationContext
 import com.android.launcher3.dagger.LauncherAppComponent
 import com.android.launcher3.dagger.LauncherAppSingleton
-import com.android.launcher3.util.AllModulesForTest
+import com.android.launcher3.statehandlers.DesktopVisibilityController
+import com.android.launcher3.util.AllModulesMinusWMProxy
 import com.android.launcher3.util.DaggerSingletonTracker
 import com.android.launcher3.util.DisplayController
 import com.android.launcher3.util.FakePrefsModule
-import com.android.launcher3.util.MainThreadInitializedObject.ObjectSandbox
 import com.android.launcher3.util.SandboxApplication
 import com.android.launcher3.util.SettingsCache
 import com.android.launcher3.util.SettingsCacheSandbox
@@ -40,12 +40,14 @@
 import dagger.BindsInstance
 import dagger.Component
 import dagger.Module
+import dagger.Provides
 import javax.inject.Inject
 import org.junit.rules.ExternalResource
 import org.junit.rules.RuleChain
 import org.junit.rules.TestRule
 import org.junit.runner.Description
 import org.junit.runners.model.Statement
+import org.mockito.kotlin.spy
 
 /**
  * [SandboxApplication] for running Taskbar tests.
@@ -58,7 +60,7 @@
     private val base: SandboxApplication,
     val virtualDisplay: VirtualDisplay,
     private val params: SandboxParams,
-) : ContextWrapper(base), ObjectSandbox by base, TestRule {
+) : ContextWrapper(base), TestRule {
 
     val settingsCacheSandbox = SettingsCacheSandbox()
 
@@ -136,9 +138,30 @@
     @Binds abstract fun bindDisplayController(controller: DisplayControllerSpy): DisplayController
 }
 
+@Module
+object DesktopVisibilityControllerModule {
+    @JvmStatic
+    @Provides
+    @LauncherAppSingleton
+    fun provideDesktopVisibilityController(
+        @ApplicationContext context: Context,
+        systemUiProxy: SystemUiProxy,
+        lifecycleTracker: DaggerSingletonTracker,
+    ): DesktopVisibilityController {
+        return spy(DesktopVisibilityController(context, systemUiProxy, lifecycleTracker))
+    }
+}
+
 @LauncherAppSingleton
 @Component(
-    modules = [AllModulesForTest::class, FakePrefsModule::class, DisplayControllerModule::class]
+    modules =
+        [
+            AllModulesMinusWMProxy::class,
+            FakePrefsModule::class,
+            DisplayControllerModule::class,
+            TaskbarSandboxModule::class,
+            DesktopVisibilityControllerModule::class,
+        ]
 )
 interface TaskbarSandboxComponent : LauncherAppComponent {
 
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
index 0c74610..6fbbd59 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
@@ -364,7 +364,7 @@
         float xVelocityPxPerMs = isQuickSwitch ? 100 : 0;
         float yVelocityPxPerMs = isQuickSwitch ? 0 : -100;
         swipeHandler.onGestureEnded(
-                yVelocityPxPerMs, new PointF(xVelocityPxPerMs, yVelocityPxPerMs));
+                yVelocityPxPerMs, new PointF(xVelocityPxPerMs, yVelocityPxPerMs), isQuickSwitch);
         swipeHandler.onCalculateEndTarget();
         runOnMainSync(swipeHandler::onSettledOnEndTarget);
 
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/DisplayModelTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/DisplayModelTest.kt
index a939e84..fa7907f 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/DisplayModelTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/DisplayModelTest.kt
@@ -21,6 +21,7 @@
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import java.io.PrintWriter
 import org.junit.Assert.assertNotNull
 import org.junit.Assert.assertNull
 import org.junit.Test
@@ -37,25 +38,29 @@
         override fun cleanup() {
             isCleanupCalled = true
         }
+
+        override fun dump(prefix: String, writer: PrintWriter) {
+            // No-Op
+        }
     }
 
     private val testableDisplayModel =
         object : DisplayModel<TestableResource>(context) {
-            override fun createDisplayResource(displayId: Int) {
-                displayResourceArray.put(displayId, TestableResource())
+            override fun createDisplayResource(display: Display): TestableResource {
+                return TestableResource()
             }
         }
 
     @Test
     fun testCreate() {
-        testableDisplayModel.createDisplayResource(Display.DEFAULT_DISPLAY)
+        testableDisplayModel.storeDisplayResource(Display.DEFAULT_DISPLAY)
         val resource = testableDisplayModel.getDisplayResource(Display.DEFAULT_DISPLAY)
         assertNotNull(resource)
     }
 
     @Test
     fun testCleanAndDelete() {
-        testableDisplayModel.createDisplayResource(Display.DEFAULT_DISPLAY)
+        testableDisplayModel.storeDisplayResource(Display.DEFAULT_DISPLAY)
         val resource = testableDisplayModel.getDisplayResource(Display.DEFAULT_DISPLAY)!!
         assertNotNull(resource)
         testableDisplayModel.deleteDisplayResource(Display.DEFAULT_DISPLAY)
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt
index af741f6..5661dcf 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt
@@ -91,7 +91,14 @@
                 .bindRotationHelper(mock(RotationTouchHelper::class.java))
                 .bindRecentsState(mock(RecentsAnimationDeviceState::class.java))
         )
-        gestureState = spy(GestureState(OverviewComponentObserver.INSTANCE.get(sandboxContext), 0))
+        gestureState =
+            spy(
+                GestureState(
+                    OverviewComponentObserver.INSTANCE.get(sandboxContext),
+                    DEFAULT_DISPLAY,
+                    0,
+                )
+            )
 
         underTest =
             LauncherSwipeHandlerV2(
@@ -109,14 +116,14 @@
     @Test
     fun goHomeFromAppByTrackpad_updateEduStats() {
         gestureState.setTrackpadGestureType(GestureState.TrackpadGestureType.THREE_FINGER)
-        underTest.onGestureEnded(flingSpeed, PointF())
+        underTest.onGestureEnded(flingSpeed, PointF(), /* horizontalTouchSlopPassed= */ false)
         verify(systemUiProxy)
             .updateContextualEduStats(/* isTrackpadGesture= */ eq(true), eq(GestureType.HOME))
     }
 
     @Test
     fun goHomeFromAppByTouch_updateEduStats() {
-        underTest.onGestureEnded(flingSpeed, PointF())
+        underTest.onGestureEnded(flingSpeed, PointF(), /* horizontalTouchSlopPassed= */ false)
         verify(systemUiProxy)
             .updateContextualEduStats(/* isTrackpadGesture= */ eq(false), eq(GestureType.HOME))
     }
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java
index ad9bbb9..9722e9d 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java
@@ -19,6 +19,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.launcher3.Flags.FLAG_ENABLE_SEPARATE_EXTERNAL_DISPLAY_TASKS;
+import static com.android.window.flags.Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -48,6 +49,8 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.R;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
+import com.android.launcher3.util.DaggerSingletonTracker;
 import com.android.launcher3.util.LooperExecutor;
 import com.android.quickstep.util.GroupTask;
 import com.android.quickstep.views.TaskViewType;
@@ -101,7 +104,9 @@
                 .thenReturn(true);
 
         mRecentTasksList = new RecentTasksList(mContext, mockMainThreadExecutor,
-                mockKeyguardManager, mSystemUiProxy, mTopTaskTracker);
+                mockKeyguardManager, mSystemUiProxy, mTopTaskTracker,
+                mock(DesktopVisibilityController.class),
+                mock(DaggerSingletonTracker.class));
     }
 
     @Test
@@ -201,6 +206,7 @@
 
     @Test
     @EnableFlags(FLAG_ENABLE_SEPARATE_EXTERNAL_DISPLAY_TASKS)
+    @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     public void loadTasksInBackground_freeformTask_createsDesktopTaskPerDisplay() throws Exception {
         List<TaskInfo> tasks = Arrays.asList(
                 createRecentTaskInfo(1 /* taskId */, DEFAULT_DISPLAY),
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsAnimationDeviceStateTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsAnimationDeviceStateTest.kt
index b652ee8..a7370b0 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsAnimationDeviceStateTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsAnimationDeviceStateTest.kt
@@ -1,5 +1,6 @@
 package com.android.quickstep
 
+import android.view.Display
 import androidx.test.annotation.UiThreadTest
 import androidx.test.filters.SmallTest
 import com.android.launcher3.dagger.LauncherComponentProvider
@@ -13,6 +14,7 @@
 import com.android.launcher3.util.NavigationMode
 import com.android.launcher3.util.SandboxApplication
 import com.android.quickstep.util.GestureExclusionManager
+import com.android.systemui.shared.system.QuickStepContract
 import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY
 import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING
 import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION
@@ -150,7 +152,7 @@
 
         allSysUiStates().forEach { state ->
             val canStartGesture = !disablingStates.contains(state)
-            underTest.setSystemUiFlags(state)
+            underTest.setSysUIStateFlagsForDisplay(state, Display.DEFAULT_DISPLAY)
             assertThat(underTest.canStartTrackpadGesture()).isEqualTo(canStartGesture)
         }
     }
@@ -166,7 +168,7 @@
             )
 
         stateToExpectedResult.forEach { (state, allowed) ->
-            underTest.setSystemUiFlags(state)
+            underTest.setSysUIStateFlagsForDisplay(state, Display.DEFAULT_DISPLAY)
             assertThat(underTest.canStartTrackpadGesture()).isEqualTo(allowed)
         }
     }
@@ -177,7 +179,7 @@
 
         allSysUiStates().forEach { state ->
             val canStartGesture = !disablingStates.contains(state)
-            underTest.setSystemUiFlags(state)
+            underTest.setSysUIStateFlagsForDisplay(state, Display.DEFAULT_DISPLAY)
             assertThat(underTest.canStartSystemGesture()).isEqualTo(canStartGesture)
         }
     }
@@ -197,11 +199,42 @@
             )
 
         stateToExpectedResult.forEach { (state, gestureAllowed) ->
-            underTest.setSystemUiFlags(state)
+            underTest.setSysUIStateFlagsForDisplay(state, Display.DEFAULT_DISPLAY)
             assertThat(underTest.canStartSystemGesture()).isEqualTo(gestureAllowed)
         }
     }
 
+    @Test
+    fun getSystemUiStateFlags_defaultAwake() {
+        val NOT_EXISTENT_DISPLAY = 2
+        assertThat(underTest.getSystemUiStateFlags(NOT_EXISTENT_DISPLAY))
+            .isEqualTo(QuickStepContract.SYSUI_STATE_AWAKE)
+    }
+
+    @Test
+    fun clearSysUIStateFlagsForDisplay_displayNotReturnedAnymore() {
+        underTest.setSysUIStateFlagsForDisplay(1, /* displayId= */ 1)
+
+        assertThat(underTest.displaysWithSysUIState).contains(1)
+        assertThat(underTest.getSystemUiStateFlags(1)).isEqualTo(1)
+
+        underTest.clearSysUIStateFlagsForDisplay(1)
+
+        assertThat(underTest.displaysWithSysUIState).doesNotContain(1)
+        assertThat(underTest.getSystemUiStateFlags(1))
+            .isEqualTo(QuickStepContract.SYSUI_STATE_AWAKE)
+    }
+
+    @Test
+    fun setSysUIStateFlagsForDisplay_setsCorrectly() {
+        underTest.setSysUIStateFlagsForDisplay(1, /* displayId= */ 1)
+        underTest.setSysUIStateFlagsForDisplay(2, /* displayId= */ 2)
+
+        assertThat(underTest.getSystemUiStateFlags(1)).isEqualTo(1)
+        assertThat(underTest.getSystemUiStateFlags(2)).isEqualTo(2)
+        assertThat(underTest.displaysWithSysUIState).containsAtLeast(1, 2)
+    }
+
     private fun allSysUiStates(): List<Long> {
         // SYSUI_STATES_* are binary flags
         return (0..SYSUI_STATES_COUNT).map { 1L shl it }
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java
index 99a34ea..cfeade8 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java
@@ -55,7 +55,7 @@
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.util.AllModulesForTest;
 import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
+import com.android.launcher3.util.SandboxContext;
 import com.android.quickstep.DeviceConfigWrapper;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
@@ -311,6 +311,18 @@
     }
 
     @Test
+    public void testTouchCancelWithoutTouchDown() {
+        mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_CANCEL));
+
+        assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
+        assertFalse(mLongPressTriggered.get());
+        verify(mNavHandleLongPressHandler, never()).onTouchStarted(any());
+        verify(mNavHandleLongPressHandler, times(1)).onTouchFinished(any(), any());
+        verifyNoMoreInteractions(mStatsLogger);
+        verifyNoMoreInteractions(mStatsLatencyLogger);
+    }
+
+    @Test
     public void testLongPressAbortedByTouchSlopPassedVertically() {
         mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
         sleep(MIN_TIME_TO_LOG_ABANDON_MS);
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt
index 7ca194a..b49923f 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt
@@ -25,10 +25,10 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.launcher3.Flags
 import com.android.quickstep.recents.ui.viewmodel.TaskData
+import com.android.quickstep.task.thumbnail.TaskHeaderUiState
 import com.android.quickstep.task.thumbnail.TaskThumbnailUiState
 import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
 import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
-import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.ThumbnailHeader
 import com.android.systemui.shared.recents.model.ThumbnailData
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
@@ -37,37 +37,21 @@
 @RunWith(AndroidJUnit4::class)
 class TaskUiStateMapperTest {
 
+    /** TaskHeaderUiState */
     @Test
-    fun taskData_isNull_returns_Uninitialized() {
+    fun taskData_isNull_returns_HideHeader() {
         val result =
-            TaskUiStateMapper.toTaskThumbnailUiState(
+            TaskUiStateMapper.toTaskHeaderState(
                 taskData = null,
-                isLiveTile = false,
                 hasHeader = false,
                 clickCloseListener = null,
             )
-        assertThat(result).isEqualTo(TaskThumbnailUiState.Uninitialized)
-    }
-
-    @Test
-    fun taskData_isLiveTile_returns_LiveTile() {
-        val inputs =
-            listOf(TASK_DATA, TASK_DATA.copy(thumbnailData = null), TASK_DATA.copy(isLocked = true))
-        inputs.forEach { input ->
-            val result =
-                TaskUiStateMapper.toTaskThumbnailUiState(
-                    taskData = input,
-                    isLiveTile = true,
-                    hasHeader = false,
-                    clickCloseListener = null,
-                )
-            assertThat(result).isEqualTo(LiveTile.WithoutHeader)
-        }
+        assertThat(result).isEqualTo(TaskHeaderUiState.HideHeader)
     }
 
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
     @Test
-    fun taskData_isLiveTileWithHeader_returns_LiveTileWithHeader() {
+    fun taskData_hasHeader_and_taskData_returnsShowHeader() {
         val inputs =
             listOf(
                 TASK_DATA,
@@ -77,14 +61,18 @@
             )
         val closeCallback = View.OnClickListener {}
         val expected =
-            LiveTile.WithHeader(
-                header = ThumbnailHeader(TASK_ICON, TASK_TITLE_DESCRIPTION, closeCallback)
+            TaskHeaderUiState.ShowHeader(
+                header =
+                    TaskHeaderUiState.ThumbnailHeader(
+                        TASK_ICON,
+                        TASK_TITLE_DESCRIPTION,
+                        closeCallback,
+                    )
             )
         inputs.forEach { taskData ->
             val result =
-                TaskUiStateMapper.toTaskThumbnailUiState(
+                TaskUiStateMapper.toTaskHeaderState(
                     taskData = taskData,
-                    isLiveTile = true,
                     hasHeader = true,
                     clickCloseListener = closeCallback,
                 )
@@ -94,7 +82,7 @@
 
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
     @Test
-    fun taskData_isLiveTileWithHeader_missingHeaderData_returns_LiveTileWithoutHeader() {
+    fun taskData_hasHeader_emptyTaskData_returns_HideHeader() {
         val inputs =
             listOf(
                 TASK_DATA.copy(icon = null),
@@ -104,30 +92,42 @@
 
         inputs.forEach { taskData ->
             val result =
-                TaskUiStateMapper.toTaskThumbnailUiState(
+                TaskUiStateMapper.toTaskHeaderState(
                     taskData = taskData,
-                    isLiveTile = true,
                     hasHeader = true,
                     clickCloseListener = {},
                 )
-            assertThat(result).isEqualTo(LiveTile.WithoutHeader)
+            assertThat(result).isEqualTo(TaskHeaderUiState.HideHeader)
+        }
+    }
+
+    /** TaskThumbnailUiState */
+    @Test
+    fun taskData_isNull_returns_Uninitialized() {
+        val result = TaskUiStateMapper.toTaskThumbnailUiState(taskData = null, isLiveTile = false)
+        assertThat(result).isEqualTo(TaskThumbnailUiState.Uninitialized)
+    }
+
+    @Test
+    fun taskData_isLiveTile_returns_LiveTile() {
+        val inputs =
+            listOf(TASK_DATA, TASK_DATA.copy(thumbnailData = null), TASK_DATA.copy(isLocked = true))
+        inputs.forEach { input ->
+            val result =
+                TaskUiStateMapper.toTaskThumbnailUiState(taskData = input, isLiveTile = true)
+            assertThat(result).isEqualTo(LiveTile)
         }
     }
 
     @Test
     fun taskData_isStaticTile_returns_SnapshotSplash() {
         val result =
-            TaskUiStateMapper.toTaskThumbnailUiState(
-                taskData = TASK_DATA,
-                isLiveTile = false,
-                hasHeader = false,
-                clickCloseListener = null,
-            )
+            TaskUiStateMapper.toTaskThumbnailUiState(taskData = TASK_DATA, isLiveTile = false)
 
         val expected =
             TaskThumbnailUiState.SnapshotSplash(
                 snapshot =
-                    Snapshot.WithoutHeader(
+                    Snapshot(
                         backgroundColor = TASK_BACKGROUND_COLOR,
                         bitmap = TASK_THUMBNAIL,
                         thumbnailRotation = Surface.ROTATION_0,
@@ -138,72 +138,12 @@
         assertThat(result).isEqualTo(expected)
     }
 
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
-    @Test
-    fun taskData_isStaticTile_withHeader_returns_SnapshotSplashWithHeader() {
-        val inputs = listOf(TASK_DATA, TASK_DATA.copy(title = null))
-        val closeCallback = View.OnClickListener {}
-        val expected =
-            TaskThumbnailUiState.SnapshotSplash(
-                snapshot =
-                    Snapshot.WithHeader(
-                        backgroundColor = TASK_BACKGROUND_COLOR,
-                        bitmap = TASK_THUMBNAIL,
-                        thumbnailRotation = Surface.ROTATION_0,
-                        header = ThumbnailHeader(TASK_ICON, TASK_TITLE_DESCRIPTION, closeCallback),
-                    ),
-                splash = TASK_ICON,
-            )
-        inputs.forEach { taskData ->
-            val result =
-                TaskUiStateMapper.toTaskThumbnailUiState(
-                    taskData = taskData,
-                    isLiveTile = false,
-                    hasHeader = true,
-                    clickCloseListener = closeCallback,
-                )
-            assertThat(result).isEqualTo(expected)
-        }
-    }
-
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
-    @Test
-    fun taskData_isStaticTile_missingHeaderData_returns_SnapshotSplashWithoutHeader() {
-        val inputs =
-            listOf(
-                TASK_DATA.copy(titleDescription = null, icon = null),
-                TASK_DATA.copy(titleDescription = null),
-                TASK_DATA.copy(icon = null),
-            )
-        val expected =
-            Snapshot.WithoutHeader(
-                backgroundColor = TASK_BACKGROUND_COLOR,
-                thumbnailRotation = Surface.ROTATION_0,
-                bitmap = TASK_THUMBNAIL,
-            )
-        inputs.forEach { taskData ->
-            val result =
-                TaskUiStateMapper.toTaskThumbnailUiState(
-                    taskData = taskData,
-                    isLiveTile = false,
-                    hasHeader = true,
-                    clickCloseListener = {},
-                )
-
-            assertThat(result).isInstanceOf(TaskThumbnailUiState.SnapshotSplash::class.java)
-            result as TaskThumbnailUiState.SnapshotSplash
-            assertThat(result.snapshot).isEqualTo(expected)
-        }
-    }
-
     @Test
     fun taskData_thumbnailIsNull_returns_BackgroundOnly() {
         val result =
             TaskUiStateMapper.toTaskThumbnailUiState(
                 taskData = TASK_DATA.copy(thumbnailData = null),
                 isLiveTile = false,
-                hasHeader = false,
-                clickCloseListener = null,
             )
 
         val expected = TaskThumbnailUiState.BackgroundOnly(TASK_BACKGROUND_COLOR)
@@ -216,8 +156,6 @@
             TaskUiStateMapper.toTaskThumbnailUiState(
                 taskData = TASK_DATA.copy(isLocked = true),
                 isLiveTile = false,
-                hasHeader = false,
-                clickCloseListener = null,
             )
 
         val expected = TaskThumbnailUiState.BackgroundOnly(TASK_BACKGROUND_COLOR)
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModelTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModelTest.kt
index a97ef0c..18b9fe9 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModelTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModelTest.kt
@@ -66,16 +66,7 @@
 
     @Before
     fun setUp() {
-        sut =
-            TaskViewModel(
-                taskViewType = TaskViewType.SINGLE,
-                recentsViewData = recentsViewData,
-                getTaskUseCase = getTaskUseCase,
-                getSysUiStatusNavFlagsUseCase = GetSysUiStatusNavFlagsUseCase(),
-                isThumbnailValidUseCase = isThumbnailValidUseCase,
-                getThumbnailPositionUseCase = getThumbnailPositionUseCase,
-                dispatcherProvider = TestDispatcherProvider(unconfinedTestDispatcher),
-            )
+        sut = createTaskViewModel(TaskViewType.SINGLE)
         whenever(getTaskUseCase.invoke(TASK_MODEL_1.id)).thenReturn(flow { emit(TASK_MODEL_1) })
         whenever(getTaskUseCase.invoke(TASK_MODEL_2.id)).thenReturn(flow { emit(TASK_MODEL_2) })
         whenever(getTaskUseCase.invoke(TASK_MODEL_3.id)).thenReturn(flow { emit(TASK_MODEL_3) })
@@ -93,6 +84,7 @@
                     isLiveTile = false,
                     hasHeader = false,
                     sysUiStatusNavFlags = FLAGS_APPEARANCE_LIGHT_THEME,
+                    taskOverlayEnabled = false,
                 )
             assertThat(sut.state.first()).isEqualTo(expectedResult)
         }
@@ -139,6 +131,7 @@
                     isLiveTile = false,
                     hasHeader = false,
                     sysUiStatusNavFlags = FLAGS_APPEARANCE_LIGHT_THEME,
+                    taskOverlayEnabled = false,
                 )
             assertThat(sut.state.first()).isEqualTo(expectedResult)
         }
@@ -161,6 +154,7 @@
                     isLiveTile = true,
                     hasHeader = false,
                     sysUiStatusNavFlags = FLAGS_APPEARANCE_LIGHT_THEME,
+                    taskOverlayEnabled = false,
                 )
             assertThat(sut.state.first()).isEqualTo(expectedResult)
         }
@@ -183,6 +177,7 @@
                     isLiveTile = false,
                     hasHeader = false,
                     sysUiStatusNavFlags = FLAGS_APPEARANCE_LIGHT_THEME,
+                    taskOverlayEnabled = false,
                 )
             assertThat(sut.state.first()).isEqualTo(expectedResult)
         }
@@ -204,6 +199,7 @@
                     isLiveTile = false,
                     hasHeader = false,
                     sysUiStatusNavFlags = FLAGS_APPEARANCE_LIGHT_THEME,
+                    taskOverlayEnabled = false,
                 )
             assertThat(sut.state.first()).isEqualTo(expectedResult)
         }
@@ -221,6 +217,7 @@
                     isLiveTile = false,
                     hasHeader = false,
                     sysUiStatusNavFlags = FLAGS_APPEARANCE_LIGHT_THEME,
+                    taskOverlayEnabled = false,
                 )
             assertThat(sut.state.first()).isEqualTo(expectedResult)
         }
@@ -235,11 +232,64 @@
                     isLiveTile = false,
                     hasHeader = false,
                     sysUiStatusNavFlags = FLAGS_APPEARANCE_DEFAULT,
+                    taskOverlayEnabled = false,
                 )
             assertThat(sut.state.first()).isEqualTo(expectedResult)
         }
 
     @Test
+    fun taskOverlayEnabled_when_OverlayIsEnabledForVisibleSingleTask() =
+        testScope.runTest {
+            sut.bind(TASK_MODEL_1.id)
+            recentsViewData.overlayEnabled.value = true
+            recentsViewData.settledFullyVisibleTaskIds.value = setOf(1)
+
+            assertThat(sut.state.first().taskOverlayEnabled).isTrue()
+        }
+
+    @Test
+    fun taskOverlayDisabled_when_usingGroupedTask() =
+        testScope.runTest {
+            sut = createTaskViewModel(TaskViewType.GROUPED)
+            sut.bind(TASK_MODEL_1.id)
+            recentsViewData.overlayEnabled.value = true
+            recentsViewData.settledFullyVisibleTaskIds.value = setOf(1)
+
+            assertThat(sut.state.first().taskOverlayEnabled).isFalse()
+        }
+
+    @Test
+    fun taskOverlayDisabled_when_usingDesktopTask() =
+        testScope.runTest {
+            sut = createTaskViewModel(TaskViewType.DESKTOP)
+            sut.bind(TASK_MODEL_1.id)
+            recentsViewData.overlayEnabled.value = true
+            recentsViewData.settledFullyVisibleTaskIds.value = setOf(1)
+
+            assertThat(sut.state.first().taskOverlayEnabled).isFalse()
+        }
+
+    @Test
+    fun taskOverlayDisabled_when_OverlayIsEnabledForInvisibleTask() =
+        testScope.runTest {
+            sut.bind(TASK_MODEL_1.id)
+            recentsViewData.overlayEnabled.value = true
+            recentsViewData.settledFullyVisibleTaskIds.value = setOf(2)
+
+            assertThat(sut.state.first().taskOverlayEnabled).isFalse()
+        }
+
+    @Test
+    fun taskOverlayDisabled_when_OverlayIsDisabledForVisibleTask() =
+        testScope.runTest {
+            sut.bind(TASK_MODEL_1.id)
+            recentsViewData.overlayEnabled.value = false
+            recentsViewData.settledFullyVisibleTaskIds.value = setOf(1)
+
+            assertThat(sut.state.first().taskOverlayEnabled).isFalse()
+        }
+
+    @Test
     fun shouldShowSplash_calls_useCase() {
         sut.isThumbnailValid(null, 0, 0)
         verify(isThumbnailValidUseCase).invoke(anyOrNull(), anyInt(), anyInt())
@@ -256,6 +306,17 @@
             isLocked = isLocked,
         )
 
+    private fun createTaskViewModel(taskViewType: TaskViewType) =
+        TaskViewModel(
+            taskViewType = taskViewType,
+            recentsViewData = recentsViewData,
+            getTaskUseCase = getTaskUseCase,
+            getSysUiStatusNavFlagsUseCase = GetSysUiStatusNavFlagsUseCase(),
+            isThumbnailValidUseCase = isThumbnailValidUseCase,
+            getThumbnailPositionUseCase = getThumbnailPositionUseCase,
+            dispatcherProvider = TestDispatcherProvider(unconfinedTestDispatcher),
+        )
+
     private companion object {
         const val INVALID_TASK_ID = -1
         const val FLAGS_APPEARANCE_LIGHT_THEME = FLAG_LIGHT_STATUS or FLAG_LIGHT_NAV
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/window/RecentsDisplayModelTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/window/RecentsDisplayModelTest.kt
index 44ea73e..0119679 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/window/RecentsDisplayModelTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/window/RecentsDisplayModelTest.kt
@@ -88,16 +88,16 @@
 
     @Test
     fun testCreateSeparateInstances() {
-        val display = Display.DEFAULT_DISPLAY + 1
-        runOnMainSync { recentsDisplayModel.createDisplayResource(display) }
+        val displayId = Display.DEFAULT_DISPLAY + 1
+        runOnMainSync { recentsDisplayModel.storeDisplayResource(displayId) }
 
         val defaultManager = recentsDisplayModel.getRecentsWindowManager(Display.DEFAULT_DISPLAY)
-        val secondaryManager = recentsDisplayModel.getRecentsWindowManager(display)
+        val secondaryManager = recentsDisplayModel.getRecentsWindowManager(displayId)
         Assert.assertNotSame(defaultManager, secondaryManager)
 
         val defaultInterface =
             recentsDisplayModel.getFallbackWindowInterface(Display.DEFAULT_DISPLAY)
-        val secondInterface = recentsDisplayModel.getFallbackWindowInterface(display)
+        val secondInterface = recentsDisplayModel.getFallbackWindowInterface(displayId)
         Assert.assertNotSame(defaultInterface, secondInterface)
     }
 
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/RecentOrientedStateHandlerTests.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/RecentOrientedStateHandlerTests.kt
new file mode 100644
index 0000000..3cdf608
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/RecentOrientedStateHandlerTests.kt
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2025 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.util
+
+import android.view.Surface
+import android.view.Surface.ROTATION_0
+import android.view.Surface.ROTATION_180
+import android.view.Surface.ROTATION_90
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.quickstep.FallbackActivityInterface
+import com.android.quickstep.orientation.RecentsPagedOrientationHandler
+import com.android.quickstep.orientation.RecentsPagedOrientationHandler.Companion.PORTRAIT
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
+
+/**
+ * Test all possible inputs to RecentsOrientedState.updateHandler. It tests all possible
+ * combinations of rotations and relevant methods (two methods that return boolean values) but it
+ * only provides the expected result when the final rotation is different from ROTATION_0 for
+ * simplicity. So any case not shown in resultMap you can assume results in ROTATION_0.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class RecentOrientedStateHandlerTests {
+
+    data class TestCase(
+        val recentsRotation: Int,
+        val displayRotation: Int,
+        val touchRotation: Int,
+        val isRotationAllowed: Boolean,
+        val isFixedLandscape: Boolean,
+    ) {
+        override fun toString(): String {
+            return "TestCase(recentsRotation=${Surface.rotationToString(recentsRotation)}, " +
+                "displayRotation=${Surface.rotationToString(displayRotation)}, " +
+                "touchRotation=${Surface.rotationToString(touchRotation)}, " +
+                "isRotationAllowed=$isRotationAllowed, " +
+                "isFixedLandscape=$isFixedLandscape)"
+        }
+    }
+
+    private fun runTestCase(testCase: TestCase, expectedHandler: RecentsPagedOrientationHandler) {
+        val recentOrientedState =
+            spy(
+                RecentsOrientedState(
+                    ApplicationProvider.getApplicationContext(),
+                    FallbackActivityInterface.INSTANCE,
+                ) {}
+            )
+        whenever(recentOrientedState.isRecentsActivityRotationAllowed).thenAnswer {
+            testCase.isRotationAllowed
+        }
+        whenever(recentOrientedState.isLauncherFixedLandscape).thenAnswer {
+            testCase.isFixedLandscape
+        }
+
+        recentOrientedState.update(testCase.displayRotation, testCase.touchRotation)
+        val rotation = recentOrientedState.orientationHandler.rotation
+        assertWithMessage("$testCase to ${Surface.rotationToString(rotation)},")
+            .that(rotation)
+            .isEqualTo(expectedHandler.rotation)
+    }
+
+    @Test
+    fun `test fixed landscape when device is portrait`() {
+        runTestCase(
+            TestCase(
+                recentsRotation = ROTATION_0,
+                displayRotation = -1,
+                touchRotation = ROTATION_0,
+                isRotationAllowed = false,
+                isFixedLandscape = true,
+            ),
+            PORTRAIT,
+        )
+    }
+
+    @Test
+    fun `test fixed landscape when device is landscape`() {
+        runTestCase(
+            TestCase(
+                recentsRotation = ROTATION_90,
+                displayRotation = -1,
+                touchRotation = ROTATION_0,
+                isRotationAllowed = false,
+                isFixedLandscape = true,
+            ),
+            PORTRAIT,
+        )
+    }
+
+    @Test
+    fun `test fixed landscape when device is seascape`() {
+        runTestCase(
+            TestCase(
+                recentsRotation = ROTATION_180,
+                displayRotation = -1,
+                touchRotation = ROTATION_0,
+                isRotationAllowed = false,
+                isFixedLandscape = true,
+            ),
+            PORTRAIT,
+        )
+    }
+
+    @Test
+    fun `test fixed landscape when device is portrait and display rotation is portrait`() {
+        runTestCase(
+            TestCase(
+                recentsRotation = ROTATION_0,
+                displayRotation = ROTATION_0,
+                touchRotation = ROTATION_0,
+                isRotationAllowed = false,
+                isFixedLandscape = true,
+            ),
+            PORTRAIT,
+        )
+    }
+
+    @Test
+    fun `test fixed landscape when device is landscape and display rotation is landscape `() {
+        runTestCase(
+            TestCase(
+                recentsRotation = ROTATION_90,
+                displayRotation = ROTATION_90,
+                touchRotation = ROTATION_0,
+                isRotationAllowed = false,
+                isFixedLandscape = true,
+            ),
+            PORTRAIT,
+        )
+    }
+
+    @Test
+    fun `test fixed landscape when device is seascape and display rotation is seascape`() {
+        runTestCase(
+            TestCase(
+                recentsRotation = ROTATION_180,
+                displayRotation = ROTATION_180,
+                touchRotation = ROTATION_0,
+                isRotationAllowed = false,
+                isFixedLandscape = true,
+            ),
+            PORTRAIT,
+        )
+    }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
index 9d000a4..c9d7e1d 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
@@ -94,6 +94,7 @@
         whenever(mockTaskContainer.task).thenReturn(mockTask)
         whenever(mockIconView.drawable).thenReturn(mockTaskViewDrawable)
         whenever(mockTaskView.taskContainers).thenReturn(List(1) { mockTaskContainer })
+        whenever(mockTaskView.firstTaskContainer).thenReturn(mockTaskContainer)
 
         whenever(splitSelectSource.drawable).thenReturn(mockSplitSourceDrawable)
         whenever(splitSelectSource.view).thenReturn(mockSplitSourceView)
diff --git a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
index d52d054..59ce637 100644
--- a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
+++ b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
@@ -34,7 +34,6 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
 
 import android.app.prediction.AppTarget;
 import android.app.prediction.AppTargetId;
@@ -46,7 +45,6 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.text.TextUtils;
 
@@ -71,8 +69,6 @@
 
 import java.util.Arrays;
 import java.util.List;
-import java.util.Set;
-import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 @SmallTest
@@ -234,50 +230,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_TIERED_WIDGETS_BY_DEFAULT_IN_PICKER)
-    public void widgetsRecommendationRan_keepsWidgetsNotOnWorkspace_addsWidgetsFromEligibleApps() {
-        runOnExecutorSync(MODEL_EXECUTOR, () -> {
-            WidgetsFilterDataProvider spiedFilterProvider = spy(
-                    mModelHelper.getModel().getWidgetsFilterDataProvider());
-            doAnswer(i -> new Predicate<WidgetItem>() {
-                @Override
-                public boolean test(WidgetItem widgetItem) {
-                    // app5's widget is already on workspace, but, app2 is not.
-                    // And app4's second widget is also not on workspace.
-                    return Set.of("app5", "app2", "app4").contains(
-                            widgetItem.componentName.getPackageName());
-                }
-            }).when(spiedFilterProvider).getPredictedWidgetsFilter();
-            mModelHelper.getBgDataModel().widgetsModel.updateWidgetFilters(spiedFilterProvider);
-            // App5's widget that's already on workspace.
-            AppTarget widget1 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
-                    mUserHandle);
-            // App4's widget eligible and not on workspace.
-            AppTarget widget2 = new AppTarget(new AppTargetId("app4"), "app4", "provider2",
-                    mUserHandle);
-
-            mCallback.mRecommendedWidgets = null;
-            mModelHelper.getModel().enqueueModelUpdateTask(
-                    newWidgetsPredicationTask(List.of(widget1, widget2)));
-            runOnExecutorSync(MAIN_EXECUTOR, () -> {
-            });
-
-            List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
-                    .stream()
-                    .map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
-                    .collect(Collectors.toList());
-            assertThat(recommendedWidgets).hasSize(2);
-            List<ComponentName> componentNames = recommendedWidgets.stream().map(
-                    w -> w.componentName).toList();
-            assertThat(componentNames).containsExactly(
-                    // Locally added, not on workspace, eligible app per filter
-                    mApp2Provider1.provider,
-                    // From prediction service, not on workspace, eligible app per filter
-                    mApp4Provider2.provider);
-        });
-    }
-
-    @Test
     public void widgetsRecommendations_excludesWidgetsHiddenForPicker() {
         runOnExecutorSync(MODEL_EXECUTOR, () -> {
 
diff --git a/quickstep/tests/src/com/android/quickstep/AbstractTaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/AbstractTaplTestsTaskbar.java
index fc757b4..0ccc76b 100644
--- a/quickstep/tests/src/com/android/quickstep/AbstractTaplTestsTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/AbstractTaplTestsTaskbar.java
@@ -55,7 +55,9 @@
                 "com.android.launcher3.testcomponent.BaseTestingActivity");
         mLauncherLayout = TestUtil.setLauncherDefaultLayout(mTargetContext, layoutBuilder);
         AbstractLauncherUiTest.initialize(this);
-        startAppFast(CALCULATOR_APP_PACKAGE);
+        if (startCalendarAppDuringSetup()) {
+            startAppFast(CALCULATOR_APP_PACKAGE);
+        }
         mLauncher.enableBlockTimeout(true);
         mLauncher.showTaskbarIfHidden();
     }
@@ -72,8 +74,20 @@
         return DisplayController.isTransientTaskbar(context);
     }
 
+    protected boolean startCalendarAppDuringSetup() {
+        return true;
+    }
+
+    protected boolean expectTaskbarIconsMatchHotseat() {
+        return true;
+    }
+
     protected Taskbar getTaskbar() {
         Taskbar taskbar = mLauncher.getLaunchedAppState().getTaskbar();
+        if (!expectTaskbarIconsMatchHotseat()) {
+            return taskbar;
+        }
+
         List<String> taskbarIconNames = taskbar.getIconNames();
         List<String> hotseatIconNames = mLauncher.getHotseatIconNames();
 
diff --git a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
index a523e02..7b73be7 100644
--- a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
@@ -36,6 +36,7 @@
 import com.android.launcher3.util.SplitConfigurationOptions
 import com.android.launcher3.util.TransformingTouchDelegate
 import com.android.quickstep.TaskOverlayFactory.TaskOverlay
+import com.android.quickstep.task.thumbnail.TaskContentView
 import com.android.quickstep.task.thumbnail.TaskThumbnailView
 import com.android.quickstep.views.LauncherRecentsView
 import com.android.quickstep.views.RecentsViewContainer
@@ -248,6 +249,7 @@
         TaskContainer(
             taskView,
             task,
+            mock<TaskContentView>(),
             if (enableRefactorTaskThumbnail()) mock<TaskThumbnailView>()
             else mock<TaskThumbnailViewDeprecated>(),
             mock<TaskViewIcon>(),
diff --git a/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt
index 2db94f6..5aaed7d 100644
--- a/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt
@@ -37,6 +37,7 @@
 import com.android.launcher3.util.SplitConfigurationOptions
 import com.android.launcher3.util.TransformingTouchDelegate
 import com.android.quickstep.TaskOverlayFactory.TaskOverlay
+import com.android.quickstep.task.thumbnail.TaskContentView
 import com.android.quickstep.task.thumbnail.TaskThumbnailView
 import com.android.quickstep.views.LauncherRecentsView
 import com.android.quickstep.views.RecentsViewContainer
@@ -246,6 +247,7 @@
         TaskContainer(
             taskView,
             task,
+            mock<TaskContentView>(),
             if (enableRefactorTaskThumbnail()) mock<TaskThumbnailView>()
             else mock<TaskThumbnailViewDeprecated>(),
             mock<TaskViewIcon>(),
diff --git a/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java b/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java
index e2ca91a..ef6f55e 100644
--- a/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java
@@ -30,6 +30,7 @@
 import android.annotation.Nullable;
 import android.os.Looper;
 import android.view.Choreographer;
+import android.view.Display;
 import android.view.MotionEvent;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -99,7 +100,8 @@
 
     @Rule public final SandboxApplication mContext = new SandboxApplication();
 
-    @NonNull private final InputMonitorCompat mInputMonitorCompat = new InputMonitorCompat("", 0);
+    @NonNull private final InputMonitorCompat mInputMonitorCompat =
+            new InputMonitorCompat("", Display.DEFAULT_DISPLAY);
 
     private TaskAnimationManager mTaskAnimationManager;
     private InputChannelCompat.InputEventReceiver mInputEventReceiver;
@@ -196,7 +198,6 @@
 
     @Before
     public void setupDeviceState() {
-        when(mDeviceState.getDisplayId()).thenReturn(0);
         when(mDeviceState.canStartTrackpadGesture()).thenReturn(true);
         when(mDeviceState.canStartSystemGesture()).thenReturn(true);
         when(mDeviceState.isFullyGesturalNavMode()).thenReturn(true);
diff --git a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
index a0ec635..154d86d 100644
--- a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
+++ b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
@@ -300,7 +300,7 @@
     @Test
     public void testSimpleOrientationTouchTransformer() {
         final DisplayController displayController = mock(DisplayController.class);
-        doReturn(mInfo).when(displayController).getInfo();
+        doReturn(mInfo).when(displayController).getInfoForDisplay(anyInt());
         final SimpleOrientationTouchTransformer transformer =
                 new SimpleOrientationTouchTransformer(getApplicationContext(), displayController,
                         mock(DaggerSingletonTracker.class));
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsLockedTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsLockedTaskbar.java
index 8fedf5c..b2617dd 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsLockedTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsLockedTaskbar.java
@@ -15,14 +15,18 @@
  */
 package com.android.quickstep;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static androidx.test.InstrumentationRegistry.getTargetContext;
 
+import static com.android.launcher3.util.TestConstants.AppNames.TEST_APP_NAME;
 import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT;
 import static com.android.wm.shell.shared.desktopmode.DesktopModeStatus.ENTER_DESKTOP_BY_DEFAULT_ON_FREEFORM_DISPLAY_SYS_PROP;
 
-import android.app.WindowConfiguration;
+import static com.google.common.truth.Truth.assertThat;
+
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.WindowManagerGlobal;
@@ -30,6 +34,7 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.launcher3.tapl.HomeAllApps;
 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
 import com.android.launcher3.util.rule.SetPropRule;
 import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
@@ -40,6 +45,7 @@
 import org.junit.Assume;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExternalResource;
 import org.junit.runner.RunWith;
 
 @LargeTest
@@ -51,25 +57,40 @@
     public SetPropRule mSetPropRule =
             new SetPropRule(ENTER_DESKTOP_BY_DEFAULT_ON_FREEFORM_DISPLAY_SYS_PROP, "true");
 
+    // Default-to-desktop feature requires the display to be freeform mode.
+    @Rule
+    public ExternalResource mFreeformDisplayRule = new ExternalResource() {
+        private int mOriginalWindowingMode = WINDOWING_MODE_UNDEFINED;
+
+        @Override
+        protected void before() {
+            mOriginalWindowingMode = setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
+        }
+
+        @Override
+        protected void after() {
+            if (mOriginalWindowingMode != WINDOWING_MODE_UNDEFINED) {
+                setDisplayWindowingMode(mOriginalWindowingMode);
+            }
+        }
+    };
+
     @Override
     public void setUp() throws Exception {
         Assume.assumeTrue(mLauncher.isTablet());
         Assume.assumeTrue(Flags.enterDesktopByDefaultOnFreeformDisplays());
         Assume.assumeTrue(DesktopModeStatus.canEnterDesktopMode(getTargetContext()));
         super.setUp();
-
-        // Default-to-desktop feature requires the display to be freeform mode.
-        setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
     }
 
     @Override
-    public void tearDown() throws Exception {
-        // Reset the display windowing mode to the device default.
-        setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_UNDEFINED);
+    protected boolean startCalendarAppDuringSetup() {
+        return false;
+    }
 
-        mLauncher.recreateTaskbar();
-
-        super.tearDown();
+    @Override
+    protected boolean expectTaskbarIconsMatchHotseat() {
+        return false;
     }
 
     @Test
@@ -87,10 +108,31 @@
         mLauncher.getLaunchedAppState().assertTaskbarVisible();
     }
 
-    private void setDisplayWindowingMode(int windowingMode) {
+    @Test
+    @PortraitLandscape
+    @NavigationModeSwitch
+    @TaskbarModeSwitch(mode = PERSISTENT)
+    public void testDragFromAllAppsToWorspace() {
+        mDevice.pressHome();
+        waitForResumed("Launcher internal state is still Background");
+
+        final HomeAllApps allApps = getTaskbar().openAllAppsOnHome();
+        allApps.freeze();
         try {
+            allApps.getAppIcon(TEST_APP_NAME).dragToWorkspace(false, false);
+            assertThat(mLauncher.getWorkspace().getWorkspaceAppIcon(TEST_APP_NAME)).isNotNull();
+        } finally {
+            allApps.unfreeze();
+        }
+    }
+
+    private int setDisplayWindowingMode(int windowingMode) {
+        try {
+            int originalWindowingMode =
+                    WindowManagerGlobal.getWindowManagerService().getWindowingMode(DEFAULT_DISPLAY);
             WindowManagerGlobal.getWindowManagerService().setWindowingMode(
                     DEFAULT_DISPLAY, windowingMode);
+            return originalWindowingMode;
         } catch (RemoteException e) {
             Log.e(TAG, "error setting windowing mode", e);
             throw new RuntimeException(e);
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 1c87bce..b207d4a 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -100,11 +100,9 @@
 
     @After
     public void tearDown() {
-        runOnRecentsView(recentsView -> {
-            if (recentsView != null) {
-                recentsView.getPagedViewOrientedState().forceAllowRotationForTesting(false);
-            }
-        });
+        runOnRecentsView(recentsView ->
+                recentsView.getPagedViewOrientedState().forceAllowRotationForTesting(false),
+                /* forTearDown= */ true);
     }
 
     public static void startTestApps() throws Exception {
@@ -433,18 +431,17 @@
                 (Math.abs(recentsView.getTopRowTaskCountForTablet()
                         - recentsView.getBottomRowTaskCountForTablet()) <= 1)));
 
-        // TODO(b/308841019): Re-enable after fixing Overview jank when dismiss
-//        // Test dismissing more tasks.
-//        assertIsInState(
-//                "Launcher internal state didn't remain in Overview", ExpectedState.OVERVIEW);
-//        overview.getCurrentTask().dismiss();
-//        assertIsInState(
-//                "Launcher internal state didn't remain in Overview", ExpectedState.OVERVIEW);
-//        overview.getCurrentTask().dismiss();
-//        runOnRecentsView(recentsView -> assertTrue(
-//                "Grid did not rebalance after multiple dismissals",
-//                (Math.abs(recentsView.getTopRowTaskCountForTablet()
-//                        - recentsView.getBottomRowTaskCountForTablet()) <= 1)));
+        // Test dismissing more tasks.
+        assertIsInState(
+                "Launcher internal state didn't remain in Overview", ExpectedState.OVERVIEW);
+        overview.getCurrentTask().dismiss();
+        assertIsInState(
+                "Launcher internal state didn't remain in Overview", ExpectedState.OVERVIEW);
+        overview.getCurrentTask().dismiss();
+        runOnRecentsView(recentsView -> assertTrue(
+                "Grid did not rebalance after multiple dismissals",
+                (Math.abs(recentsView.getTopRowTaskCountForTablet()
+                        - recentsView.getBottomRowTaskCountForTablet()) <= 1)));
 
         // Test dismissing all tasks.
         mLauncher.goHome().switchToOverview().dismissAllTasks();
@@ -662,18 +659,31 @@
     }
 
     private <T> T getFromRecentsView(Function<RecentsView, T> f) {
+        return getFromRecentsView(f, false);
+    }
+
+    private <T> T getFromRecentsView(Function<RecentsView, T> f, boolean forTearDown) {
         if (enableLauncherOverviewInWindow()) {
-            return getFromRecentsWindow(
-                    recentsWindowManager -> f.apply(recentsWindowManager.getOverviewPanel()));
+            return getFromRecentsWindow(recentsWindowManager ->
+                    (forTearDown && recentsWindowManager == null)
+                            ? null :  f.apply(recentsWindowManager.getOverviewPanel()));
         } else {
-            return getFromLauncher(launcher -> f.apply(launcher.getOverviewPanel()));
+            return getFromLauncher(launcher -> (forTearDown && launcher == null)
+                    ? null : f.apply(launcher.getOverviewPanel()));
         }
     }
 
     private void runOnRecentsView(Consumer<RecentsView> f) {
+        runOnRecentsView(f, false);
+    }
+
+    private void runOnRecentsView(Consumer<RecentsView> f, boolean forTearDown) {
         getFromRecentsView(recentsView -> {
+            if (forTearDown && recentsView == null) {
+                return null;
+            }
             f.accept(recentsView);
             return null;
-        });
+        }, forTearDown);
     }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
index 08ce5e7..ec245ee 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
@@ -24,7 +24,6 @@
 import androidx.test.filters.LargeTest;
 
 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
-import com.android.launcher3.util.rule.ScreenRecordRule;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -124,7 +123,6 @@
     }
 
     @Test
-    @ScreenRecordRule.ScreenRecord // b/373417111
     public void testLaunchShortcut_fromTaskbarAllApps() {
         getTaskbar().openAllApps()
                 .getAppIcon(TEST_APP_NAME)
diff --git a/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchAnimatorHelperTest.kt b/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchAnimatorHelperTest.kt
index b4d9f5b..47108e0 100644
--- a/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchAnimatorHelperTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchAnimatorHelperTest.kt
@@ -93,6 +93,21 @@
     }
 
     @Test
+    fun noLaunchTransition_returnsEmptyAnimatorsList() {
+        val pipChange =
+            TransitionInfo.Change(mock(), mock()).apply {
+                mode = WindowManager.TRANSIT_PIP
+                taskInfo = TASK_INFO_FREEFORM
+            }
+        val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
+        transitionInfo.addChange(pipChange)
+
+        val actual = helper.createAnimators(transitionInfo, finishCallback = {})
+
+        assertThat(actual).hasSize(0)
+    }
+
+    @Test
     fun minimizeTransition_returnsLaunchAndMinimizeAnimator() {
         val openChange =
             TransitionInfo.Change(mock(), mock()).apply {
diff --git a/res/drawable/ic_unpin.xml b/res/drawable/ic_unpin.xml
new file mode 100644
index 0000000..557b4f9
--- /dev/null
+++ b/res/drawable/ic_unpin.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <group>
+    <clip-path
+        android:pathData="M0,0h24v24h-24z"/>
+    <path
+        android:pathData="M17,3V5H16V13.175L14,11.175V5H10V7.175L7.825,5L7,4.175V3H17ZM12,23L11,22V16H6V14L8,12V10.85L1.4,4.2L2.8,2.8L21.2,21.2L19.75,22.6L13.15,16H13V22L12,23ZM8.85,14H11.15L10.05,12.9L10,12.85L8.85,14Z"
+        android:fillColor="#FF000000"/>
+  </group>
+</vector>
diff --git a/res/layout/all_apps_personal_work_tabs.xml b/res/layout/all_apps_personal_work_tabs.xml
index b6a8ed8..1435f82 100644
--- a/res/layout/all_apps_personal_work_tabs.xml
+++ b/res/layout/all_apps_personal_work_tabs.xml
@@ -34,6 +34,7 @@
         android:layout_weight="1"
         android:background="@drawable/all_apps_tabs_background"
         android:text="@string/all_apps_personal_tab"
+        android:contentDescription="@string/all_apps_personal_tab_content_description"
         android:textColor="@color/all_apps_tab_text"
         android:textSize="14sp"
         style="?android:attr/borderlessButtonStyle" />
@@ -46,6 +47,7 @@
         android:layout_weight="1"
         android:background="@drawable/all_apps_tabs_background"
         android:text="@string/all_apps_work_tab"
+        android:contentDescription="@string/all_apps_work_tab_content_description"
         android:textColor="@color/all_apps_tab_text"
         android:textSize="14sp"
         style="?android:attr/borderlessButtonStyle" />
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index ef34ea7..a2a1cf2 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Moenie voorstel nie"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Vasspeldvoorspelling"</string>
     <string name="bubble" msgid="3072951361014076670">"Borrel"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Speld aan taakbalk vas"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installeer kortpaaie"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Laat \'n app toe om kortpaaie by te voeg sonder gebruikerinmenging."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"lees tuis-instellings en -kortpaaie"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Bladsy %1$d van %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Tuisskerm %1$d van %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nuwe tuisskermbladsy"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Vouer oopgemaak, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Tik om die vouer toe te maak"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tik om nuwe naam te stoor"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Maak toe"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Persoonlik"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Werk"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Werkprofiel"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Werkprogramme het \'n kenteken en is sigbaar vir jou IT-administrateur"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Het dit"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 0d1d350..ac462ac 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"መተግበሪያውን አይጠቁሙ"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"የፒን ግምት"</string>
     <string name="bubble" msgid="3072951361014076670">"አረፋ"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"ተግባር አሞሌ ላይ ፒን አድርግ"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"አቋራጮችን ይጭናል"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"መተግበሪያው ያለተጠቃሚ ጣልቃ ገብነት አቋራጭ እንዲያክል ያስችለዋል።"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"የመነሻ ቅንብሮች እና አቋራጮችን ያነባል"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"ገፅ %1$d ከ%2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"መነሻ ማያ ገፅ %1$d ከ%2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"አዲስ የመነሻ ማያ ገፅ"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"አቃፊ ተከፍቷል፣ <xliff:g id="WIDTH">%1$d</xliff:g> በ<xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"አቃፊን ለመዝጋት መታ ያድርጉ"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"ዳግም የተሰጠውን ስም ለማስቀመጥ መታ ያድርጉ"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"ዝጋ"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"የግል"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"ሥራ"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"የሥራ መገለጫ"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"የሥራ መተግበሪያዎች ባጅ የተደረገባቸው እና ለእርስዎ የአይቲ አስተዳዳሪ የሚታዩ ናቸው"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"ገባኝ"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 2b7ec21..83b07ec 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"عدم اقتراح التطبيق"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"تثبيت التطبيق المتوقّع"</string>
     <string name="bubble" msgid="3072951361014076670">"فقاعة"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"التثبيت على \"شريط التطبيقات\""</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"تثبيت اختصارات"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"للسماح لتطبيق ما بإضافة اختصارات بدون تدخل المستخدم."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"الاطلاع على الإعدادات والاختصارات على الشاشة الرئيسية"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"‏الصفحة %1$d من %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"‏الشاشة الرئيسية %1$d من %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"صفحة الشاشة الرئيسية الجديدة"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"نشط"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"تم التصغير"</string>
     <string name="folder_opened" msgid="94695026776264709">"تم فتح المجلد، بمقاس <xliff:g id="WIDTH">%1$d</xliff:g> في <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"انقر لإغلاق المجلد"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"انقر لحفظ الاسم الجديد"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"إغلاق"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"شخصية"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"للعمل"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"ملف العمل"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"تحمل تطبيقات العمل مميّزة بشارة ومرئية لمشرف تكنولوجيا المعلومات."</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"حسنًا"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 7125153..6db0ffb 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"পৰামৰ্শ নিদিব"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"পূৰ্বানুমান কৰা এপ্‌টো পিন কৰক"</string>
     <string name="bubble" msgid="3072951361014076670">"বাবল"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"টাস্কবাৰত পিন কৰক"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"শ্বৰ্টকাট ইনষ্টল কৰিব পাৰে"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ব্য়ৱহাৰকাৰীৰ হস্তক্ষেপ অবিহনেই কোনো এপক শ্বৰ্টকাটবোৰ যোগ কৰাৰ অনুমতি দিয়ে।"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"গৃহ স্ক্ৰীনত ছেটিং আৰু শ্বৰ্টকাটসমূহ পঢ়া"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$dৰ %1$d পৃষ্ঠা"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"গৃহ স্ক্ৰীন %2$dৰ %1$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"গৃহ স্ক্ৰীনৰ নতুন পৃষ্ঠা"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"ফ’ল্ডাৰ খোলা হ’ল, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"ফ\'ল্ডাৰ বন্ধ কৰিবলৈ টিপক"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"সলনি কৰা নাম ছেভ কৰিবলৈ টিপক"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"বন্ধ কৰক"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"ব্যক্তিগত"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"কৰ্মস্থান"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"কৰ্মস্থানৰ এপ্‌সমূহ প্ৰতীকেৰে চিহ্নিত কৰা হয় আৰু সেইবোৰ আপোনাৰ আইটি প্ৰশাসকৰ বাবে দৃশ্যমান হয়"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"বুজি পালোঁ"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 7905400..28035df 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Tətbiq təklif olunmasın"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Proqnozlaşdırılan tətbiqi bərkidin"</string>
     <string name="bubble" msgid="3072951361014076670">"Qabarcıq"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Tapşırıq panelinə bərkidin"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"qısayolları quraşdır"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Tətbiqə istifadəçi müdaxiləsi olmadan qısayolları əlavə etməyə icazə verir."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"Əsas səhifə ayarlarını və qısayollarını oxumaq"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Səhifə %1$d of %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Əsas Səhifə ekranı %1$d of %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Yeni əsas ekran səhifəsi"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Qovluq açıldı, <xliff:g id="HEIGHT">%2$d</xliff:g> hündürlük ilə <xliff:g id="WIDTH">%1$d</xliff:g> enində"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Qovluq bağlamaq üçün toxunun"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Ad dəyişikliyini yadda saxlamaq üçün toxunun"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Bağlayın"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Şəxsi"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"İş"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"İş profili"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"İş tətbiqləri nişanlanıb və İT administratorunuza görünür"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Anladım"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index d1ecaeb..c8782e2 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlaži aplikaciju"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Zakači predviđanje"</string>
     <string name="bubble" msgid="3072951361014076670">"Oblačić"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Zakači za traku zad."</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliranje prečica"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Dozvoljava aplikaciji da dodaje prečice bez intervencije korisnika."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"čitanje podešavanja i prečica na početnom ekranu"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d. stranica od %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d. početni ekran od %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nova stranica početnog ekrana"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"Aktivno"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"Smanjeno"</string>
     <string name="folder_opened" msgid="94695026776264709">"Folder je otvoren, <xliff:g id="WIDTH">%1$d</xliff:g> puta <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Dodirnite da biste zatvorili folder"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Dodirnite da biste sačuvali preimenovanje"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Zatvori"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Lično"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Posao"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Poslovni profil"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Poslovne aplikacije su označene značkom i IT administrator može da ih vidi"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Važi"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 3c579cb..ffb3d91 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Не прапаноўваць праграму"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Замацаваць прапанаваную праграму"</string>
     <string name="bubble" msgid="3072951361014076670">"Бурбалка"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Замацаваць на панэлі"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Стварэнне ярлыкоў"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Дазваляе праграмам дадаваць ярлыкі без умяшання карыстальніка."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"счытваць налады і ярлыкі на галоўным экране"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Старонка %1$d з %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Галоўны экран %1$d з %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Новая старонка галоўнага экрана"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Папка адкрыта, <xliff:g id="WIDTH">%1$d</xliff:g> на <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Краніце, каб закрыць папку"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Краніце, каб захаваць новую назву"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Закрыць"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Асабістыя"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Працоўныя"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Працоўны профіль"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Працоўныя праграмы пазначаны спецыяльнымі значкамі, а таксама бачныя IT-адміністратару"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Зразумела"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 98571add..a4c3faf 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Без предлагане на приложение"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Фиксиране на предвиждането"</string>
     <string name="bubble" msgid="3072951361014076670">"Балонче"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Фикс. в лентата на задач."</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталиране на преки пътища"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Разрешава на приложението да добавя преки пътища без намеса на потребителя."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"четене на настройките и преките пътища на началния екран"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Страница %1$d от %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Начален екран %1$d от %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Нова страница на началния екран"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"Активно"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"Намалено"</string>
     <string name="folder_opened" msgid="94695026776264709">"Папката е отворена – <xliff:g id="WIDTH">%1$d</xliff:g> на <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Докоснете, за да затворите папката"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Докоснете, за да запазите новото име"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Затваряне"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Лични"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Служебни"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Служебен потребителски профил"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Служебните приложения са означени със значка и са видими за системния администратор"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Разбрах"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index b070133..6bc2483 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"অ্যাপ সাজেস্ট করবেন না"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"আপনার প্রয়োজন হতে পারে এমন অ্যাপ পিন করুন"</string>
     <string name="bubble" msgid="3072951361014076670">"বাবল"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"টাস্কবারে পিন করুন"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"শর্টকাটগুলি ইনস্টল করে"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"একটি অ্যাপ্লিকেশানকে ব্যবহারকারীর হস্তক্ষেপ ছাড়াই শর্টকাটগুলি যোগ করার অনুমতি দেয়৷"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"হোম স্ক্রিনে সেটিংস ও শর্টকাট পড়ুন"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$dটির মধ্যে %1$dটি পৃষ্ঠা"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dটির %1$d নম্বর হোম স্ক্রিন"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"নতুন হোম স্ক্রীনের পৃষ্ঠা"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"ফোল্ডার খোলা হয়েছে, <xliff:g id="WIDTH">%1$d</xliff:g> বাই <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"ফোল্ডার বন্ধ করতে আলতো চাপ দিন"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"পুনঃনামকরণ সংরক্ষণ করতে আলতো চাপ দিন"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"বন্ধ করুন"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"ব্যক্তিগত"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"অফিস"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"অফিসের প্রোফাইল"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"অফিস অ্যাপে ব্যাজ যোগ করা হয়েছে এবং আপনার আইটি অ্যাডমিন সেগুলি দেখতে পাবেন"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"বুঝেছি"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index c0568bf..394e30f 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlaži aplikaciju"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Zakači predviđanje"</string>
     <string name="bubble" msgid="3072951361014076670">"Oblačić"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Zakači na traku zadataka"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliraj prečice"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Dopušta aplikaciji dodavanje prečica bez posredovanja korisnika."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"čita postavke na početnom ekranu i prečice"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Strana %1$d od %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Početni ekran %1$d od %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nova stranica početnog ekrana"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Folder je otvoren, (š) <xliff:g id="WIDTH">%1$d</xliff:g> (v) <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Dodirnite da zatvorite folder"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Dodirnite da sačuvate promjenu naziva"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Zatvaranje"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Lično"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Poslovno"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Radni profil"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Poslovne aplikacije su označene i vaš IT administrator ih može vidjeti"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Razumijem"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index c5daecc..07a21ab 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"No suggereixis l\'aplicació"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Fixa la predicció"</string>
     <string name="bubble" msgid="3072951361014076670">"Bombolla"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Fixa a barra tasques"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instal·la dreceres"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permet que una aplicació afegeixi dreceres sense la intervenció de l\'usuari."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"llegir la configuració i les dreceres de la pantalla d\'inici"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Pàgina %1$d de %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla d\'inici %1$d de %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Pàgina de la pantalla d\'inici nova"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"Actiu"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"Minimitzat"</string>
     <string name="folder_opened" msgid="94695026776264709">"S\'ha obert la carpeta, <xliff:g id="WIDTH">%1$d</xliff:g> per <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Toca per tancar la carpeta"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Toca per desar el nom nou"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Tanca"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Treball"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de treball"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Les aplicacions de treball tenen una insígnia i el teu administrador de TI les pot veure"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Entesos"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index f96b907..82bbd12 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Nenavrhovat aplikaci"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Připnout předpověď"</string>
     <string name="bubble" msgid="3072951361014076670">"Bublat"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Připnout na panel"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalace zástupce"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Umožňuje aplikaci přidat zástupce bez zásahu uživatele."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"čtení nastavení a zkratek plochy"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Strana %1$d z %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Plocha %1$d z %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nová stránka plochy"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Složka otevřena, rozměry <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Klepnutím složku zavřete"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Klepnutím změnu názvu uložíte"</string>
@@ -127,7 +130,7 @@
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Složka: <xliff:g id="NAME">%1$s</xliff:g>, počet položek: <xliff:g id="SIZE">%2$d</xliff:g> nebo více"</string>
     <string name="unnamed_folder" msgid="2420192029474044442">"Nepojmenovaná složka"</string>
     <string name="app_pair_name_format" msgid="8134106404716224054">"Dvojice aplikací: <xliff:g id="APP1">%1$s</xliff:g> a <xliff:g id="APP2">%2$s</xliff:g>"</string>
-    <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Tapeta a styl"</string>
+    <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Tapety a styl"</string>
     <string name="edit_home_screen" msgid="8947858375782098427">"Upravit plochu"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Nastavení plochy"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Zakázáno administrátorem"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Zavřít"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobní"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Pracovní"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Pracovní profil"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Pracovní aplikace jsou označené a váš administrátor IT je vidí"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Rozumím"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 7979c20..1485f75 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Foreslå ikke en app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Fastgør forslaget"</string>
     <string name="bubble" msgid="3072951361014076670">"Boble"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Fastgør til proceslinje"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installere genveje"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Tillader, at en app tilføjer genveje uden brugerens indgriben."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"læs indstillinger og genveje for startskærm"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Side %1$d ud af %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Startskærm %1$d ud af %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Ny startskærm"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Mappen er åben, <xliff:g id="WIDTH">%1$d</xliff:g> gange <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Tryk for at lukke mappen"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tryk for at gemme omdøbningen"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Luk"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personlig"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Arbejde"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Arbejdsprofil"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Arbejdsapps har badges og kan ses af din it-administrator"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 098c9c1..4a7fb7f 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"App nicht vorschlagen"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Vorgeschlagene App fixieren"</string>
     <string name="bubble" msgid="3072951361014076670">"Bubble"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"An Taskleiste pinnen"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Verknüpfungen installieren"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Ermöglicht einer App das Hinzufügen von Verknüpfungen ohne Eingreifen des Nutzers"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"Einstellungen und Verknüpfungen auf dem Startbildschirm lesen"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Seite %1$d von %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Startbildschirm %1$d von %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Neue Startbildschirmseite"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Ordner geöffnet, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Ordner zum Schließen antippen"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Neuen Namen zum Speichern antippen"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Schließen"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Privat"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Geschäftlich"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Arbeitsprofil"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Geschäftliche Apps sind gekennzeichnet und für deinen IT-Administrator sichtbar"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 0c10f0d..de2bdc7 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Να μην προτείνεται"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Καρφίτσωμα πρόβλεψης"</string>
     <string name="bubble" msgid="3072951361014076670">"Συννεφάκι"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Καρφ. σε γρ. εργαλείων"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"εγκατάσταση συντομεύσεων"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Επιτρέπει σε μια εφαρμογή την προσθήκη συντομεύσεων χωρίς την παρέμβαση του χρήστη."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ανάγνωση ρυθμίσεων και συντομεύσεων αρχικής οθόνης"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Σελίδα %1$d από %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Αρχική οθόνη %1$d από %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Νέα σελίδα αρχικής οθόνης"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Άνοιγμα φακέλου, <xliff:g id="WIDTH">%1$d</xliff:g> επί <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Πατήστε για να κλείσετε το φάκελο"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Πατήστε για να αποθηκεύσετε τη νέα ονομασία"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Κλείσιμο"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Προσωπικές"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Εργασίας"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Προφίλ εργασίας"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Οι εφαρμογές εργασιών φέρουν σήμα και είναι ορατές στον διαχειριστή IT σας"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Το κατάλαβα"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 5a92bf4..7ce9c0c 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Pin prediction"</string>
     <string name="bubble" msgid="3072951361014076670">"Bubble"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Pin to taskbar"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"read Home settings and shortcuts"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d of %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d of %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"New home screen page"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"Active"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"Minimised"</string>
     <string name="folder_opened" msgid="94695026776264709">"Folder opened, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Tap to close folder"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tap to save rename"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Close"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Work profile"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Work apps are badged and visible to your IT admin"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index a3d0e0b..71fd15a 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Pin Prediction"</string>
     <string name="bubble" msgid="3072951361014076670">"Bubble"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Pin to taskbar"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"read home settings and shortcuts"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d of %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d of %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"New home screen page"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"Active"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"Minimized"</string>
     <string name="folder_opened" msgid="94695026776264709">"Folder opened, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Tap to close folder"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tap to save rename"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Close"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Work profile"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Work apps are badged and visible to your IT admin"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Got it"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 5a92bf4..7ce9c0c 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Pin prediction"</string>
     <string name="bubble" msgid="3072951361014076670">"Bubble"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Pin to taskbar"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"read Home settings and shortcuts"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d of %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d of %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"New home screen page"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"Active"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"Minimised"</string>
     <string name="folder_opened" msgid="94695026776264709">"Folder opened, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Tap to close folder"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tap to save rename"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Close"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Work profile"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Work apps are badged and visible to your IT admin"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 5a92bf4..7ce9c0c 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Pin prediction"</string>
     <string name="bubble" msgid="3072951361014076670">"Bubble"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Pin to taskbar"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"read Home settings and shortcuts"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d of %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d of %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"New home screen page"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"Active"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"Minimised"</string>
     <string name="folder_opened" msgid="94695026776264709">"Folder opened, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Tap to close folder"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tap to save rename"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Close"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Work profile"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Work apps are badged and visible to your IT admin"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 1d337e0..ef5d483 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"No sugerir app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Fijar predicción"</string>
     <string name="bubble" msgid="3072951361014076670">"Burbuja"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Fijar a barra"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar accesos directos"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que una aplicación agregue accesos directos sin que el usuario intervenga."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"leer parámetros de configuración y accesos directos de la página principal"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla principal %1$d de %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nueva página en la pantalla principal"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"Activo"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"Minimizado"</string>
     <string name="folder_opened" msgid="94695026776264709">"Carpeta abierta, <xliff:g id="WIDTH">%1$d</xliff:g> por <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Presiona para cerrar la carpeta"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Presiona para guardar el cambio de nombre"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Cerrar"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Trabajo"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabajo"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Las apps de trabajo tienen una insignia y el administrador de TI las puede ver"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Entendido"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 0dbcbe5..a1ed2ed 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"No sugerir aplicación"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Fijar predicción"</string>
     <string name="bubble" msgid="3072951361014076670">"Burbuja"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Fijar a barra de tareas"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar accesos directos"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que una aplicación añada accesos directos sin intervención del usuario."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"leer ajustes y accesos directos de la pantalla de inicio"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla de inicio %1$d de %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nueva página de pantalla de inicio"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Carpeta abierta, <xliff:g id="WIDTH">%1$d</xliff:g> por <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Toca para cerrar la carpeta"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Toca para guardar el nuevo nombre"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Cerrar"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Trabajo"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabajo"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Las aplicaciones de trabajo tienen una insignia, y tu administrador de TI las puede ver"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Entendido"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index f242f1e..5300ec8 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ära soovita rakendust"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Kinnita ennustus"</string>
     <string name="bubble" msgid="3072951361014076670">"Mull"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Kinnita tegumiribale"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installi otseteed"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Võimaldab rakendusel lisada otseteid kasutaja sekkumiseta."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"avakuva seadete ja otseteede lugemine"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Leht %1$d/%2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Avakuva %1$d/%2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Uus avakuva leht"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Kaust on avatud, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Puudutage kausta sulgemiseks"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Puudutage ümbernimetamise salvestamiseks"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Sule"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Isiklik"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Töö"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Tööprofiil"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Töörakendustel on märk ja need on teie IT-administraatorile nähtavad"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Selge"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 1fcd004..013c88e 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ez iradoki aplikazioa"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Ainguratu iragarpena"</string>
     <string name="bubble" msgid="3072951361014076670">"Burbuila"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Ainguratu zereginen barran"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Instalatu lasterbideak"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Erabiltzaileak ezer egin gabe lasterbideak gehitzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"irakurri hasierako pantailako ezarpenak eta lasterbideak"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d/%2$d orria"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d/%2$d orri nagusi"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Orri nagusiaren orri berria"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Karpeta ireki da: <xliff:g id="WIDTH">%1$d</xliff:g> × <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Karpeta ixteko, sakatu hau"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Izen berria gordetzeko, sakatu hau"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Itxi"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Pertsonalak"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Lanekoak"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Laneko profila"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Laneko aplikazioek bereizgarriak dituzte, eta IKT saileko administratzaileak ikus ditzake"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Ados"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index c45d372..578d35d 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -64,8 +64,8 @@
     <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"پاک کردن نوشتار از چارگوش جستجو"</string>
     <string name="no_widgets_available" msgid="4337693382501046170">"ابزاره و میان‌بری دردسترس نیست"</string>
     <string name="no_search_results" msgid="3787956167293097509">"هیچ ابزاره یا میان‌بری پیدا نشد"</string>
-    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ابزاره‌های شخصی"</string>
-    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ابزاره‌های کاری"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"شخصی"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"کاری"</string>
     <string name="widget_category_conversations" msgid="8894438636213590446">"مکالمه‌ها"</string>
     <string name="widget_category_note_taking" msgid="3469689394504266039">"یادداشت‌برداری"</string>
     <string name="widget_cell_tap_to_show_add_button_label" msgid="4354194214317043581">"نشان دادن دکمه افزودن"</string>
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"برنامه پیشنهاد داده نشود"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"سنجاق کردن پیشنهاد"</string>
     <string name="bubble" msgid="3072951361014076670">"حبابک"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"سنجاق به نوار وظیفه"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"نصب میان‌برها"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"به برنامه اجازه می‌دهد میان‌برها را بدون دخالت کاربر اضافه کند."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"خواندن تنظیمات و میان‌برهای صفحه اصلی"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"‏صفحه %1$d از %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"‏صفحه اصلی %1$d از %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"صفحه اصلی جدید"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"پوشه باز شده، <xliff:g id="WIDTH">%1$d</xliff:g> در <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"برای بستن پوشه، تک‌ضرب بزنید"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"برای ذخیره تغییر نام، تک‌ضرب بزنید"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"بستن"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"شخصی"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"کاری"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"نمایه کاری"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"برنامه‌های کاری نشان‌دار هستند و سرپرست فناوری اطلاعات می‌تواند آن‌ها را ببیند"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"متوجهم"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 2c43a25..29d04b2 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Älä ehdota sovellusta"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Kiinnitä sovellus"</string>
     <string name="bubble" msgid="3072951361014076670">"Kupla"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Kiinnitä t.palkkiin"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"asenna pikakuvakkeita"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Antaa sovelluksen lisätä pikakuvakkeita itsenäisesti ilman käyttäjän valintaa."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"lukea aloitusnäytön asetuksia ja pikakuvakkeita"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Sivu %1$d / %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Aloitusruutu %1$d/%2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Uusi aloitusnäytön sivu"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Kansio avattu, koko <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Sulje kansio koskettamalla."</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tallenna uusi nimi koskettamalla."</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Sulje"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Henkilökohtaiset"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Työsovellukset"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Työprofiili"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Työsovellukset on merkitty sellaisiksi ja näkyvät IT-järjestelmänvalvojille"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Selvä"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index c191aae..eb9360f 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne pas suggérer d\'appli"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Épingler la prédiction"</string>
     <string name="bubble" msgid="3072951361014076670">"Bulle"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Éping. (barre tâche)"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installer des raccourcis"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permet à une appli d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"lire les paramètres et les raccourcis de la page d\'accueil"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d sur %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Écran d\'accueil %1$d sur %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nouvelle page d\'écran d\'accueil"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Dossier ouvert, <xliff:g id="WIDTH">%1$d</xliff:g> par <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Touchez pour fermer le dossier"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Touchez pour enregistrer le nouveau nom"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Fermer"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personnel"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Travail"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil professionnel"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Les applis professionnelles sont indiquées par un badge et elles sont visibles pour votre administrateur informatique"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index e4c40e5..fea7ff1 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne pas suggérer d\'appli"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Épingler la prédiction"</string>
     <string name="bubble" msgid="3072951361014076670">"Bulle"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Épingler à la barre"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installer des raccourcis"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permettre à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"Lire les paramètres et les raccourcis de la page d\'accueil"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d sur %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Écran d\'accueil %1$d sur %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nouvelle page d\'écran d\'accueil"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Dossier ouvert, <xliff:g id="WIDTH">%1$d</xliff:g> par <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Appuyez pour fermer le dossier."</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Appuyez pour enregistrer le nouveau nom du dossier."</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Fermer"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personnel"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Professionnel"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil professionnel"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Les applis professionnelles sont identifiées par un badge et votre administrateur informatique peut les voir"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 4ded582..513083a 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Non suxerir app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Fixar predición"</string>
     <string name="bubble" msgid="3072951361014076670">"Burbulla"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Fixar na barra"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atallos"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a unha aplicación engadir atallos sen intervención do usuario."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ler a configuración e os atallos da pantalla de inicio"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Páxina %1$d de %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla de inicio %1$d de %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nova páxina da pantalla de inicio"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Abriuse o cartafol, <xliff:g id="WIDTH">%1$d</xliff:g> por <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Toca fóra para pechar o cartafol"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Toca fóra para cambiar o nome do cartafol"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Pechar"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Persoal"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Traballo"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de traballo"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"O administrador de TI pode ver as aplicacións do traballo e engadirlles indicadores"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Entendido"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 5a33983..d31f291 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ઍપ સૂચવશો નહીં"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"પૂર્વાનુમાનને પિન કરો"</string>
     <string name="bubble" msgid="3072951361014076670">"બબલ"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"ટાસ્કબારમાં પિન કરો"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"શૉર્ટકટ ઇન્સ્ટૉલ કરો"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"એપ્લિકેશનને વપરાશકર્તા હસ્તક્ષેપ વગર શોર્ટકટ્સ ઉમેરવાની મંજૂરી આપે છે."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"હોમ સેટિંગ અને શૉર્ટકટ વાંચો"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$d માંથી %1$d પૃષ્ઠ"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d માંથી %1$d હોમ સ્ક્રીન"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"નવું હોમ સ્ક્રીન પૃષ્ઠ"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"<xliff:g id="WIDTH">%1$d</xliff:g> બાય <xliff:g id="HEIGHT">%2$d</xliff:g> નું ફોલ્ડર ખોલ્યું"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"ફોલ્ડર બંધ કરવા માટે ટૅપ કરો"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"નામ બદલવાનું સાચવવા માટે ટૅપ કરો"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"બંધ કરો"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"વ્યક્તિગત ઍપ"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"ઑફિસની ઍપ"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"ઑફિસની પ્રોફાઇલ"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"ઑફિસની ઍપને બૅજ આપેલા હોય છે અને તમારા IT ઍડમિન તેમને જોઈ શકે છે"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"સમજાઈ ગયું"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 8696ff8..360ef33 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ऐप्लिकेशन का सुझाव न दें"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"सुझाए गए ऐप पिन करें"</string>
     <string name="bubble" msgid="3072951361014076670">"बबल"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"टास्कबार में पिन करें"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"शॉर्टकट इंस्‍टॉल करें"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ऐप को उपयोगकर्ता के हस्‍तक्षेप के बिना शॉर्टकट जोड़ने देती है."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"होम स्क्रीन की सेटिंग और शॉर्टकट पढ़ने की अनुमति"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"पेज %2$d में से %1$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"होम स्क्रीन %2$d में से %1$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"नया होम स्‍क्रीन पेज"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"फ़ोल्डर खोला गया, <xliff:g id="WIDTH">%1$d</xliff:g> गुणा <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"फ़ोल्डर बंद करने के लिए टैप करें"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"नाम बदलना सहेजने के लिए टैप करें"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"बंद करें"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"निजी ऐप्लिकेशन"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"वर्क ऐप्लिकेशन"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"वर्क प्रोफ़ाइल"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"वर्क ऐप्लिकेशन बैज किए गए हैं. आईटी एडमिन इन्हें देख सकता है"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"ठीक है"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index d859f58..db82c46 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlaži aplikaciju"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Prikvači predviđenu apl."</string>
     <string name="bubble" msgid="3072951361014076670">"Oblačić"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Prikvači na traku"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliranje prečaca"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Aplikaciji omogućuje dodavanje prečaca bez intervencije korisnika."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"čitati postavke i prečace početnog zaslona"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Stranica %1$d od %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Početni zaslon %1$d od %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nova stranica početnog zaslona"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Mapa je otvorena, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Dodirnite da biste zatvorili mapu"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Dodirnite da biste spremili promijenjeni naziv"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Zatvori"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobno"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Posao"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Poslovni profil"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Poslovne su aplikacije označene i vidljive vašem IT administratoru"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Shvaćam"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 1a1f405..04e43a7 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne javasoljon appot"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Várható kitűzése"</string>
     <string name="bubble" msgid="3072951361014076670">"Buborék"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Rögzítés a tálcán"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"parancsikonok telepítése"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Lehetővé teszi egy alkalmazás számára, hogy felhasználói beavatkozás nélkül adjon hozzá parancsikonokat."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"kezdőképernyő beállításainak és parancsikonjainak olvasása"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$d/%1$d. oldal"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d/%1$d. kezdőképernyő"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Új kezdőképernyő oldal"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Mappa megnyitva – szélesség: <xliff:g id="WIDTH">%1$d</xliff:g>; magasság: <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Érintse meg a mappa bezárásához"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Koppintson ide az átnevezés mentéséhez"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Bezárás"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Személyes"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Munkahelyi"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Munkaprofil"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"A munkahelyi alkalmazások jelvénnyel vannak megjelölve, és ezeket láthatja a rendszergazda"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Értem"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 036e9c9..7d805a6 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Չառաջարկել"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Ամրացնել առաջարկվող հավելվածը"</string>
     <string name="bubble" msgid="3072951361014076670">"Ամպիկ"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Ամրացնել վահանակում"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Դյուրանցումների տեղադրում"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Հավելվածին թույլ է տալիս ավելացնել դյուրանցումներ՝ առանց օգտագործողի միջամտության:"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"կարդալ հիմնական էկրանի կարգավորումներն ու դյուրանցումները"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Էջ %1$d՝ %2$d-ից"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Հիմնական էկրան %1$d` %2$d-ից"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Հիմնական էկրանի նոր էջ"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"Ակտիվ է"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"Նվազեցվել է"</string>
     <string name="folder_opened" msgid="94695026776264709">"Պանակը բաց է, <xliff:g id="WIDTH">%1$d</xliff:g>-ից <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Հպեք՝ պանակը փակելու համար"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Հպեք՝ նոր անվանումը պահելու համար"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Փակել"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Անձնական"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Աշխատանքային"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Աշխատանքային պրոֆիլ"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Աշխատանքային հավելվածները հատուկ նշանակ ունեն և տեսանելի են ՏՏ ադմինիստրատորին"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Եղավ"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 2ba2997..28a01ac 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Jangan sarankan apl"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Pin Prediksi"</string>
     <string name="bubble" msgid="3072951361014076670">"Balon"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Sematkan ke taskbar"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"memasang pintasan"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Mengizinkan aplikasi menambahkan pintasan tanpa campur tangan pengguna."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"membaca setelan dan pintasan layar utama"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Halaman %1$d dari %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Layar utama %1$d dari %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Halaman layar utama baru"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Folder dibuka, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Ketuk untuk menutup folder"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Ketuk untuk menyimpan ganti nama"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Tutup"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Pribadi"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Kerja"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil kerja"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Aplikasi kerja diberi badge dan terlihat oleh admin IT"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Oke"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 9316f90..a87df93 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ekki fá tillögu að forriti"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Festa tillögu"</string>
     <string name="bubble" msgid="3072951361014076670">"Blaðra"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Festa á forritastiku"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"setja upp flýtileiðir"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Leyfir forriti að bæta við flýtileiðum án íhlutunar notanda."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"lesa stillingar og flýtileiðir heimaskjás"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Síða %1$d af %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Heimaskjár %1$d af %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Ný síða á heimaskjá"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"Virkt"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"Minnkað"</string>
     <string name="folder_opened" msgid="94695026776264709">"Mappa opnuð, <xliff:g id="WIDTH">%1$d</xliff:g> sinnum <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Ýttu til að loka möppunni"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Ýttu til að vista breytt heiti"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Loka"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Persónulegt"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Vinna"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Vinnusnið"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Vinnuforrit eru merkt og kerfisstjórinn getur séð þau"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Ég skil"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 3145133..ffe7436 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Non suggerire app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Blocca previsione"</string>
     <string name="bubble" msgid="3072951361014076670">"Fumetto"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Fissa alla barra app"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Aggiunta di scorciatoie"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Consente a un\'app di aggiungere scorciatoie automaticamente."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"leggere le impostazioni e le scorciatoie nella schermata Home"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d di %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Schermata Home %1$d di %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nuova pagina Schermata Home"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"Attiva"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"Ridotta a icona"</string>
     <string name="folder_opened" msgid="94695026776264709">"Cartella aperta, <xliff:g id="WIDTH">%1$d</xliff:g> per <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Tocca per chiudere la cartella"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tocca per salvare il nuovo nome"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Esci"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personali"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Lavoro"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profilo di lavoro"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Le app di lavoro sono contrassegnate con un badge e visibili all\'amministratore IT"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 34bfbad..dd496c8 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"בלי להציע את האפליקציה"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"הצמדת החיזוי"</string>
     <string name="bubble" msgid="3072951361014076670">"בועה"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"הצמדה לשורת המשימות"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"התקנת קיצורי דרך"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"מאפשר לאפליקציה להוסיף קיצורי דרך ללא התערבות המשתמש."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"קריאת ההגדרות וקיצורי הדרך בדף הבית"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"‏דף %1$d מתוך %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"‏מסך הבית %1$d מתוך %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"מסך הבית חדש"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"תיקייה פתוחה, <xliff:g id="WIDTH">%1$d</xliff:g> על <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"יש ללחוץ כדי לסגור את התיקייה"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"יש ללחוץ כדי לשמור שינוי שם"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"סגירה"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"אישי"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"עבודה"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"פרופיל עבודה"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"‏האפליקציות לעבודה מתויגות ומוצגות למנהל ה-IT"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"הבנתי"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 3d42652..9000081 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"アプリを表示しない"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"アプリの候補を固定"</string>
     <string name="bubble" msgid="3072951361014076670">"ふきだし"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"タスクバーに固定"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ショートカットのインストール"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ユーザー操作なしでショートカットを追加することをアプリに許可します。"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ホームの設定とショートカットの読み取り"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d/%2$dページ"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"ホーム画面: %1$d/%2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"新しいホーム画面ページ"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"有効"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"最小化"</string>
     <string name="folder_opened" msgid="94695026776264709">"フォルダが開いています。<xliff:g id="WIDTH">%1$d</xliff:g>x<xliff:g id="HEIGHT">%2$d</xliff:g>の大きさです"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"タップしてフォルダを閉じます"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"タップして変更後の名前を保存します"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"閉じる"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"個人用"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"仕事用"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"仕事用プロファイル"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"仕事用アプリはバッジ付きで表示され、IT 管理者に公開されます"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 6a6d30d..2fd6b77 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"არ შემომთავაზო აპი"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"ჩამაგრების პროგნოზირება"</string>
     <string name="bubble" msgid="3072951361014076670">"ბუშტი"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"ამოცანათა ზოლში ჩამაგრება"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"მალსახმობების დაყენება"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"აპისთვის მალსახმობების დამოუკიდებლად დამატების უფლების მიცემა."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"მთავარი ეკრანის პარამეტრებისა და მალსახმობების წაკითხვა"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"გვერდი %1$d %2$d-დან"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"მთავარი ეკრანი %1$d, %2$d-დან"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"მთავარი ეკრანის ახალი გვერდი"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"აქტიური"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"დაპატარავებული"</string>
     <string name="folder_opened" msgid="94695026776264709">"საქაღალდე გახსნილია, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"შეეხეთ საქაღალდის დასახურად"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"შეეხეთ გადარქმეული სახელის შესანახად"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"დახურვა"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"პირადი"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"სამსახური"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"სამსახურის პროფილი"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"სამსახურის აპები ბეჯით არის მონიშნული და ხილულია თქვენი IT ადმინისტრატორისთვის"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"გასაგებია"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 4f457ee..fee3a9a 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Қолданба ұсынбау"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Болжамды бекіту"</string>
     <string name="bubble" msgid="3072951361014076670">"Қалқыма терезе"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Тапсырмалар жолағына бекіту"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"таңбаша орнату"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Қолданбаға пайдаланушының қатысуынсыз төте пернелерді қосу мүмкіндігін береді."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"негізгі экран параметрлері мен таңбашаларын оқу"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d бет, барлығы %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d негізгі экран, барлығы %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Жаңа негізгі экран беті"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Қалта ашылды, <xliff:g id="WIDTH">%1$d</xliff:g> және <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Қалтаны жабу үшін түртіңіз"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Қайта атауды сақтау үшін түртіңіз"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Жабу"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Жеке"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Жұмыс"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Жұмыс профилі"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Жұмыс қолданбаларының танымбелгілері бар және олар әкімшіңізге көрінеді."</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Түсінікті"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 6f3fb5c..cc8539e 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"កុំណែនាំកម្មវិធី"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"ខ្ទាស់ការ​ព្យាករ"</string>
     <string name="bubble" msgid="3072951361014076670">"ពពុះ"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"ខ្ទាស់ទៅរបារកិច្ចការ"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ដំឡើង​ផ្លូវកាត់"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"អនុញ្ញាត​ឲ្យ​កម្មវិធី​បន្ថែម​ផ្លូវកាត់​ ដោយ​មិន​ចាំបាច់​​អំពើ​ពី​អ្នក​ប្រើ។"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"អានការកំណត់ និងផ្លូវកាត់របស់អេក្រង់ដើម"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"ទំព័រ %1$d នៃ %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"អេក្រង់​ដើម %1$d នៃ %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"ទំព័រអេក្រង់ដើមថ្មី"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"បាន​បើក​ថត <xliff:g id="WIDTH">%1$d</xliff:g> ដោយ <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"ប៉ះ ដើម្បីបិទថត"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"ប៉ះដើម្បីរក្សាទុកឈ្មោះដែលបានប្តូរ"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"បិទ"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"ផ្ទាល់ខ្លួន"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"ការងារ"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"កម្រងព័ត៌មានការងារ"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"កម្មវិធីការងារ​ត្រូវបានដាក់​គ្រឿងសម្គាល់ ហើយ​អ្នកគ្រប់គ្រង​ផ្នែកព័ត៌មានវិទ្យា​របស់អ្នក​អាចមើលឃើញ"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"យល់ហើយ"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 5cb996c..cd5d59d 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ಆ್ಯಪ್ ಅನ್ನು ಸೂಚಿಸಬೇಡಿ"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"ಮುನ್ನೋಟ ಪಿನ್ ಮಾಡಿ"</string>
     <string name="bubble" msgid="3072951361014076670">"ಬಬಲ್"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"ಟಾಸ್ಕ್‌ಬಾರ್‌ಗೆ ಪಿನ್‌ ಮಾಡಿ"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಸ್ಥಾಪಿಸಿ"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ಬಳಕೆದಾರರ ಹಸ್ತಕ್ಷೇಪವಿಲ್ಲದೆ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಸೇರಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ಹೋಮ್ ಸ್ಕ್ರೀನ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಓದಿ"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$d ರಲ್ಲಿ %1$d ಪುಟ"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d ರಲ್ಲಿ %1$d ಮುಖಪುಟದ ಸ್ಕ್ರೀನ್"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"ಹೊಸ ಮುಖಪುಟ ಸ್ಕ್ರೀನ್"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"ಸಕ್ರಿಯವಾಗಿದೆ"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"ಮಿನಿಮೈಸ್ ಮಾಡಲಾಗಿದೆ"</string>
     <string name="folder_opened" msgid="94695026776264709">"ಫೋಲ್ಡರ್ ತೆರೆಯಲಾಗಿದೆ, <xliff:g id="WIDTH">%1$d</xliff:g> ಬೈ <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"ಫೋಲ್ಡರ್‌ ಮುಚ್ಚಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"ಮರುಹೆಸರನ್ನು ಉಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"ಮುಚ್ಚಿರಿ"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"ವೈಯಕ್ತಿಕ"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"ಕೆಲಸ"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್‌ಗಳನ್ನು ಬ್ಯಾಡ್ಜ್ ಮಾಡಲಾಗಿದೆ ಮತ್ತು ಅವುಗಳು ನಿಮ್ಮ IT ನಿರ್ವಾಹಕರಿಗೆ ಗೋಚರಿಸುತ್ತವೆ"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"ಸರಿ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index e5cae63..e588634 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"앱 제안 받지 않음"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"예상 앱 고정"</string>
     <string name="bubble" msgid="3072951361014076670">"풍선"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"태스크 바에 고정"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"바로가기 설치"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"앱이 사용자의 작업 없이 바로가기를 추가할 수 있도록 합니다."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"홈 설정 및 바로가기 읽기"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"페이지 %1$d/%2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"홈 화면 %1$d/%2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"새로운 홈 화면 페이지"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"폴더 열림(<xliff:g id="WIDTH">%1$d</xliff:g>X<xliff:g id="HEIGHT">%2$d</xliff:g>)"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"탭하여 폴더 닫기"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"탭하여 변경된 이름 저장"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"닫기"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"개인"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"직장"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"직장 프로필"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"직장 앱에는 배지가 있으며, IT 관리자는 직장 앱을 확인할 수 있습니다"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"확인"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index b98b61d..fb1675e 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Cунушталбасын"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Божомолдонгон колдонмону кадап коюу"</string>
     <string name="bubble" msgid="3072951361014076670">"Көбүкчө"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Тапшырмалар панелине кадоо"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"тез чакырмаларды орнотуу"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Колдонмого колдонуучуга кайрылбастан тез чакырма кошууга уруксат берет."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"үйдүн параметрлерин жана ыкчам баскычтарын окуу"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$d ичинен %1$d барак"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Үй экраны %2$d ичинен %1$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Жаңы башкы экран барагы"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Фолдер ачылды, туурасы <xliff:g id="WIDTH">%1$d</xliff:g>, бийиктиги <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Куржунду жабуу үчүн таптаңыз"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Өзгөртүлгөн аталышын сактоо үчүн таптаңыз"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Жабуу"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Жеке колдонмолор"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Жумуш колдонмолору"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Жумуш профили"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Жумуш колдонмолору белгиленип, аларды IT администраторлору көрөт"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Түшүндүм"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 457c185..2aaf40b 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ຢ່າແນະນຳແອັບ"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"ປັກໝຸດການຄາດເດົາ"</string>
     <string name="bubble" msgid="3072951361014076670">"ຟອງ"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"ປັກໝຸດໃສ່ແຖບໜ້າວຽກ"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ຕິດຕັ້ງທາງລັດ"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ອະນຸຍາດໃຫ້ແອັບຯ ເພີ່ມທາງລັດໂດຍບໍ່ຕ້ອງຮັບການຢືນຢັນຈາກຜູ່ໃຊ້."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ອ່ານການຕັ້ງຄ່າໜ້າຫຼັກ ແລະ ທາງລັດ"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"ໜ້າ %1$d ຈາກ %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"ໜ້າຈໍຫຼັກ %1$d ໃນ %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"ໜ້າ​ຂອງ​ໜ້າ​ຈໍ​ຫຼັກ​ໃໝ່"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"ນຳໃຊ້ຢູ່"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"ຫຍໍ້ລົງແລ້ວ"</string>
     <string name="folder_opened" msgid="94695026776264709">"ເປີດໂຟນເດີແລ້ວ, <xliff:g id="WIDTH">%1$d</xliff:g> ຄູນ <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"ແຕະເພື່ອປິດໂຟນເດີ"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"ແຕະເພື່ອບັນທຶກການປ່ຽນຊື່"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"ປິດ"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"ສ່ວນຕົວ"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"ວຽກ"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"ແອັບບ່ອນເຮັດວຽກແມ່ນຖືກຕິດປ້າຍ ແລະ ສະແດງໃຫ້ຜູ້ເບິ່ງແຍງໄອທີຂອງທ່ານເຫັນ"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"ເຂົ້າໃຈແລ້ວ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 1a4a3f2..aeba444 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Nesiūlyti programos"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Prisegti numatymą"</string>
     <string name="bubble" msgid="3072951361014076670">"Debesėlis"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Pris. prie užd. j."</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"įdiegti sparčiuosius klavišus"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Programai leidžiama pridėti sparčiuosius klavišus be naudotojo įsikišimo."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"skaityti pagrindinio ekrano nustatymus ir sparčiuosius klavišus"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d psl. iš %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d pagrindinis ekranas iš %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Naujas pagrindinio ekrano puslapis"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"Aktyvi"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"Sumažinta"</string>
     <string name="folder_opened" msgid="94695026776264709">"Atidarytas aplankas, <xliff:g id="WIDTH">%1$d</xliff:g> ir <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Palieskite, kad uždarytumėte aplanką"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Palieskite, kad išsaugotumėte pakeistą pavadinimą"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Uždaryti"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Asmeninės"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Darbo"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Darbo profilis"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Darbo programos yra pažymėtos ženkleliu ir matomos IT administratoriui"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Supratau"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 857b492..0ba127d 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Neieteikt lietotni"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Piespraust prognozēto lietotni"</string>
     <string name="bubble" msgid="3072951361014076670">"Burbulis"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Pie uzdevumu joslas"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalēt saīsnes"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Ļauj lietotnei pievienot saīsnes, nejautājot lietotājam."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"sākuma ekrāna iestatījumu un saīšņu lasīšana"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d. lapa no %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Sākuma ekrāns: %1$d no %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Jauna sākuma ekrāna lapa"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Atvērta mape: <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Pieskarieties, lai aizvērtu mapi."</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Pieskarieties, lai saglabātu jauno nosaukumu."</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Aizvērt"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personīgās lietotnes"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Darba lietotnes"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Darba profils"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Darba lietotnēm ir pievienota emblēma, un tās ir redzamas jūsu IT administratoram"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Labi"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 450d5d6..ba119f7 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Не предлагај апл."</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Закачи го предвидувањето"</string>
     <string name="bubble" msgid="3072951361014076670">"Балонче"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Закачи на лентата"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталирање кратенки"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Овозможува апликацијата да додава кратенки без интервенција на корисникот."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"да чита поставки и кратенки на почетна страница"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Страница %1$d од %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Екран на почетна страница %1$d од %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Нова страница на почетен екран"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Отворена е папка, <xliff:g id="WIDTH">%1$d</xliff:g> на <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Допрете за да ја затворите папката"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Допрете за да го зачувате преименувањето"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Затвори"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Лично"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"За работа"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Работен профил"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Работните апликации имаат значка и се видливи за IT-администраторот"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Сфатив"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 4d65869..2a2e050 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ആപ്പ് നിർദ്ദേശിക്കേണ്ട"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"പ്രവചനം പിൻ ചെയ്യുക"</string>
     <string name="bubble" msgid="3072951361014076670">"ബബിൾ"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"ടാസ്ക്ബാറിൽ പിൻ ചെയ്യൂ"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"കുറുക്കുവഴികൾ ഇൻസ്റ്റാളുചെയ്യുക"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ഉപയോക്തൃ ഇടപെടൽ ഇല്ലാതെ കുറുക്കുവഴികൾ ചേർക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ഹോം ക്രമീകരണവും കുറുക്കുവഴികളും വായിക്കുക"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"പേജ് %1$d / %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"ഹോം സ്‌ക്രീൻ %1$d / %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"പുതിയ ഹോം സ്ക്രീൻ പേജ്"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"ഫോൾഡർ തുറന്നു, <xliff:g id="WIDTH">%1$d</xliff:g> / <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"ഫോൾഡർ അടയ്ക്കുന്നതിന് ടാപ്പുചെയ്യുക"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"പേരുമാറ്റം സംരക്ഷിക്കുന്നതിന് ടാപ്പുചെയ്യുക"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"അടയ്ക്കൂ"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"വ്യക്തിപരം"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"ഔദ്യോഗിക ആപ്പുകൾക്ക് ബാഡ്‌ജ് നൽകിയിരിക്കുന്നു, അവ നിങ്ങളുടെ ഐടി അഡ്‌മിന് കാണാനുമാകും"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"മനസ്സിലായി"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 9e40f08..ffe1b2c 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Апп бүү санал болго"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Таамаглалыг бэхлэх"</string>
     <string name="bubble" msgid="3072951361014076670">"Бөмбөлөг"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Ажлын хэсэгт бэхлэх"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"товчлол суулгах"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Апп нь хэрэглэгчийн оролцоогүйгээр товчлолыг нэмэж чадна"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"нүүрний тохиргоо болон товчлолыг унших"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$d-н %1$d хуудас"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d-н Нүүр дэлгэц %1$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Шинэ үндсэн нүүр хуудас"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"<xliff:g id="WIDTH">%1$d</xliff:g> <xliff:g id="HEIGHT">%2$d</xliff:g> фолдер нээгдэв"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Фолдерийг хаахын тулд дарна уу"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Шинэ нэрийг хадгалахын тулд дарна уу."</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Хаах"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Хувийн"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Ажил"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Ажлын профайл"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Ажлын аппуудыг тэмдэглэсэн бөгөөд танай IT админд харагдана"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Ойлголоо"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 8907bcf..b249150 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ॲप सुचवू नका"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"पूर्वानुमान पिन करा"</string>
     <string name="bubble" msgid="3072951361014076670">"बबल"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"टास्कबारवर पिन करा"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"शॉर्टकट इंस्टॉल करा"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"वापरकर्ता हस्तक्षेपाशिवाय शॉर्टकट जोडण्यास अ‍ॅप ला अनुमती देते."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"होम सेटिंग्ज आणि शॉर्टकट वाचा"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$d पैकी %1$d पेज"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d पैकी %1$d मुख्य स्क्रीन"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"नवीन होम स्क्रीन पेज"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"फोल्डर उघडले, <xliff:g id="WIDTH">%1$d</xliff:g> बाय <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"फोल्डर बंद करण्यासाठी टॅप करा"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"पुनर्नामित करणे सेव्ह करण्यासाठी टॅप करा"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"बंद करा"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"वैयक्तिक"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"कार्य"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"कार्य प्रोफाइल"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"कार्य ॲप्स ही बॅज केलेली असून तुमच्या आयटी ॲडमिनला दृश्यमान आहेत"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"समजले"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 90245af..3585ec7 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Jangan cadangkan apl"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Sematkan Ramalan"</string>
     <string name="bubble" msgid="3072951361014076670">"Gelembung"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Semat pada bar tugas"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"pasang pintasan"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Membenarkan apl menambah pintasan tanpa campur tangan pengguna."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"membaca tetapan dan pintasan skrin utama"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Halaman %1$d daripada %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Skrin Laman Utama %1$d daripada %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Halaman skrin utama baharu"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"Aktif"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"Minimumkan"</string>
     <string name="folder_opened" msgid="94695026776264709">"Folder dibuka, <xliff:g id="WIDTH">%1$d</xliff:g> kali <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Ketik untuk menutup folder"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Ketik untuk menyimpan penamaan semula"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Tutup"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Peribadi"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Kerja"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil kerja"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Apl kerja mempunyai lencana dan kelihatan kepada pentadbir IT anda"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index d6410da..0116834 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"အက်ပ်အကြံမပြုပါနှင့်"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"ခန့်မှန်းချက်ကို ပင်ထိုးရန်"</string>
     <string name="bubble" msgid="3072951361014076670">"ပူဖောင်းကွက်"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Taskbar ၌ပင်ထိုးရန်"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ဖြတ်လမ်းလင့်ခ်များ ထည့်သွင်းခြင်း"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"အသုံးပြုသူ လုပ်ဆောင်မှုမရှိပဲ အပ်ပလီကေးရှင်းကို အတိုကောက်မှတ်သားမှုများ ပြုလုပ်ခွင့် ပေးခြင်း"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ပင်မဆက်တင်နှင့် ဖြတ်လမ်းလင့်ခ်များ ဖတ်ခြင်း"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"စာမျက်နှာ %1$d မှ %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"ပင်မစာမျက်နှာ %1$d မှ %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"ပင်မမျက်နှာပြင် စာမျက်နှာသစ်"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"ဖွင့်ထားသောအကန့်, <xliff:g id="WIDTH">%1$d</xliff:g> နှင့် <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"ဖိုင်တွဲကို ပိတ်ရန် တို့ပါ"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"အမည်ပြောင်းခြင်းကို သိမ်းရန် တို့ပါ"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"ပိတ်ရန်"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"ကိုယ်ပိုင်"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"အလုပ်"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"အလုပ်ပရိုဖိုင်"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"အလုပ်သုံးအက်ပ်များကို တံဆိပ်တပ်ထားပြီး သင်၏ IT စီမံခန့်ခွဲသူက မြင်နိုင်ပါသည်"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"ရပါပြီ"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index bdce1f4..47c6abb 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ikke foreslå app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Fest forslaget"</string>
     <string name="bubble" msgid="3072951361014076670">"Boble"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Fest til oppgavelinjen"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installere snarveier"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Gir apper tillatelse til å legge til snarveier uten innblanding fra brukeren."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"lese startsideinnstillinger og -snarveier"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Side %1$d av %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Startside %1$d av %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Ny side på startskjermen"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Mappen er åpnet – <xliff:g id="WIDTH">%1$d</xliff:g> ganger <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Trykk for å lukke mappen"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Trykk for å lagre det nye navnet"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Lukk"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personlig"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Jobb"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Jobbprofil"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Jobbapper er merket og synlige for IT-administratoren"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Greit"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index de509df..e451926 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -97,11 +97,10 @@
     <string name="app_info_drop_target_label" msgid="692894985365717661">"एपसम्बन्धी जानकारी"</string>
     <string name="install_private_system_shortcut_label" msgid="1616889277073184841">"निजी प्रोफाइलमा इन्स्टल गर्नुहोस्"</string>
     <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"एप अनइन्स्टल गर्नुहोस्"</string>
-    <string name="install_drop_target_label" msgid="2539096853673231757">"स्थापना गर्नुहोस्"</string>
+    <string name="install_drop_target_label" msgid="2539096853673231757">"इन्स्टल गर्नुहोस्"</string>
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"एप सिफारिस नगर्नुहोस्"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"सिफारिस गरिएको एप पिन गर्नुहोस्"</string>
     <string name="bubble" msgid="3072951361014076670">"बबल"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"टास्कबारमा पिन गर्नुहोस्"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"सर्टकट स्थापना गर्नेहोस्"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"प्रयोगकर्ताको हस्तक्षेप बिना एउटा एपलाई सर्टकटमा थप्नको लागि अनुमति दिनुहोस्।"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"होम स्क्रिनका सेटिङ र सर्टकटहरू रिड गर्नुहोस्"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"पृष्ठ %2$d को %1$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"होम स्क्रिन %1$d को %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"नयाँ होम स्क्रिन पृष्ठ"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"फोल्डर खुल्यो <xliff:g id="WIDTH">%1$d</xliff:g> बाट <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"फोल्डरलाई बन्द गर्न ट्याप गर्नुहोस्"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"पुनःनामाकरणलाई सुरक्षित गर्न ट्याप गर्नुहोस्"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"बन्द गर्नुहोस्"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"व्यक्तिगत"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"कामसम्बन्धी"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"कार्य प्रोफाइल"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"कामसम्बन्धी एपहरूमा ब्याज अङ्कित हुन्छ र तपाईंका IT एड्मिन ती एप हेर्न सक्छन्"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"बुझेँ"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 92b0c6d..75793b0 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Geen app voorstellen"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Voorspelling vastzetten"</string>
     <string name="bubble" msgid="3072951361014076670">"Bubbel"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Vastzetten op taakbalk"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Snelle links instellen"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Een app toestaan snelkoppelingen toe te voegen zonder tussenkomst van de gebruiker."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"instellingen en snelkoppelingen op startscherm lezen"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d van %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Startscherm %1$d van %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nieuwe startschermpagina"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"Actief"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"Geminimaliseerd"</string>
     <string name="folder_opened" msgid="94695026776264709">"Map geopend, <xliff:g id="WIDTH">%1$d</xliff:g> bij <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Tik om de map te sluiten"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tik om de gewijzigde naam op te slaan"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Sluiten"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Privé"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Werk"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Werkprofiel"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Werk-apps hebben badges en zijn zichtbaar voor je IT-beheerder"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 3eb680f..98ccaf1 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ଆପ ପରାମର୍ଶ ଦିଅନ୍ତୁ ନାହିଁ"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"ପୂର୍ବାନୁମାନକୁ ପିନ୍ କରନ୍ତୁ"</string>
     <string name="bubble" msgid="3072951361014076670">"ବବଲ"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"ଟାସ୍କବାରରେ ପିନ କର"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ସର୍ଟକଟ୍‍ ଇନଷ୍ଟଲ୍‌ କରନ୍ତୁ"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ୟୁଜରଙ୍କ ବିନା ହସ୍ତକ୍ଷେପରେ ଶର୍ଟକଟ୍‌ ଯୋଡ଼ିବାକୁ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ହୋମ ସେଟିଂସ ଏବଂ ସର୍ଟକଟଗୁଡ଼ିକୁ ପଢ଼ନ୍ତୁ"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"ମୋଟ %2$dରୁ %1$d ନମ୍ବର ପୃଷ୍ଠା"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dରୁ %1$d ହୋମ ସ୍କ୍ରିନ"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"ନୂଆ ହୋମ ସ୍କ୍ରିନ ପେଜ"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"<xliff:g id="HEIGHT">%2$d</xliff:g> / <xliff:g id="WIDTH">%1$d</xliff:g>ର ଫୋଲ୍ଡର ଖୋଲାଗଲା"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"ଫୋଲ୍ଡର୍‌ ବନ୍ଦ କରିବାକୁ ଟାପ୍‌ କରନ୍ତୁ"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"ନାମ ବଦଳାଇବା ସେଭ୍ କରିବାକୁ ଟାପ୍‌ କରନ୍ତୁ"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"ବ୍ୟକ୍ତିଗତ"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"ୱାର୍କ"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"ୱର୍କ ପ୍ରୋଫାଇଲ୍‌"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"ୱାର୍କ ଆପ୍ସକୁ ବେଜ କରାଯାଇଛି ଏବଂ ସେଗୁଡ଼ିକ ଆପଣଙ୍କ IT ଆଡମିନଙ୍କୁ ଦେଖାଯାଉଛି"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"ବୁଝିଗଲି"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 17ecb78..3904473 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ਐਪ ਦਾ ਸੁਝਾਅ ਨਾ ਦਿਓ"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"ਪੂਰਵ-ਅਨੁਮਾਨ ਪਿੰਨ ਕਰੋ"</string>
     <string name="bubble" msgid="3072951361014076670">"ਬਬਲ"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"ਟਾਸਕਬਾਰ \'ਤੇ ਪਿੰਨ ਕਰੋ"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ਸ਼ਾਰਟਕੱਟ ਸਥਾਪਤ ਕਰੋ"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ਇੱਕ ਐਪ ਨੂੰ ਵਰਤੋਂਕਾਰ ਦੇ ਦਖ਼ਲ ਤੋਂ ਬਿਨਾਂ ਸ਼ਾਰਟਕੱਟ ਸ਼ਾਮਲ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਪੜ੍ਹੋ"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"ਸਫ਼ਾ %2$d ਦਾ %1$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"ਹੋਮ ਸਕ੍ਰੀਨ %2$d ਦੀ %1$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"ਨਵਾਂ ਹੋਮ ਸਕ੍ਰੀਨ ਸਫ਼ਾ"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"ਫੋਲਡਰ ਖੋਲ੍ਹਿਆ, <xliff:g id="WIDTH">%1$d</xliff:g> ਬਾਇ <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"ਫੋਲਡਰ ਬੰਦ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"ਬਦਲੇ ਗਏ ਨਾਮ ਨੂੰ ਰੱਖਿਅਤ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"ਬੰਦ ਕਰੋ"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"ਨਿੱਜੀ"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"ਕੰਮ ਸੰਬੰਧੀ"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਨੂੰ ਬੈਜ ਕੀਤਾ ਗਿਆ ਹੈ ਅਤੇ ਇਹ ਤੁਹਾਡੇ ਆਈ.ਟੀ. ਪ੍ਰਸ਼ਾਸਕ ਨੂੰ ਦਿਸਣਗੀਆਂ"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"ਸਮਝ ਲਿਆ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 1dad001..f7f71f4 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Nie proponuj aplikacji"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Przypnij podpowiedź"</string>
     <string name="bubble" msgid="3072951361014076670">"Dymek"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Przypnij na pasku zadań"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Instalowanie skrótów"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Pozwala aplikacji dodawać skróty bez interwencji użytkownika."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"Odczytuje ustawienia i skróty na ekranie głównym"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Strona %1$d z %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Ekran główny %1$d z %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nowa strona ekranu głównego"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Folder otwarty, <xliff:g id="WIDTH">%1$d</xliff:g> na <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Kliknij, by zamknąć folder"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Kliknij, by zapisać nową nazwę"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Zamknij"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobiste"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Służbowe"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil służbowy"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Aplikacje służbowe mają plakietki i są widoczne dla administratora IT"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 701bb25..24235d1 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -64,7 +64,7 @@
     <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Limpe o texto da caixa de pesquisa"</string>
     <string name="no_widgets_available" msgid="4337693382501046170">"Os widgets e os atalhos não estão disponíveis"</string>
     <string name="no_search_results" msgid="3787956167293097509">"Nenhum widget ou atalho encontrado"</string>
-    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Pessoais"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Pessoal"</string>
     <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Trabalho"</string>
     <string name="widget_category_conversations" msgid="8894438636213590446">"Conversas"</string>
     <string name="widget_category_note_taking" msgid="3469689394504266039">"Tomar notas"</string>
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Não sugerir app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Fixar previsão"</string>
     <string name="bubble" msgid="3072951361014076670">"Balão"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Afixar na barra tar."</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atalhos"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a uma app adicionar atalhos sem a intervenção do utilizador."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ler definições e atalhos do ecrã Principal"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Ecrã principal %1$d de %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nova página do ecrã principal"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"Ativa"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"Minimizada"</string>
     <string name="folder_opened" msgid="94695026776264709">"Pasta aberta, <xliff:g id="WIDTH">%1$d</xliff:g> por <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Tocar para fechar a pasta"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tocar para guardar o nome novo"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Fechar"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Pessoal"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Trabalho"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabalho"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"As apps de trabalho têm um emblema e estão visíveis para o seu administrador de TI"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index dc792f1..d0e1178 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Não sugerir esse app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Fixar previsão"</string>
     <string name="bubble" msgid="3072951361014076670">"Balão"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Fixar na barra de tarefas"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atalhos"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que um app adicione atalhos sem intervenção do usuário."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ler configurações e atalhos da tela inicial"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Tela inicial %1$d de %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nova página na tela inicial"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Pasta aberta, <xliff:g id="WIDTH">%1$d</xliff:g> por <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Toque para fechar a pasta"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Toque para salvar o novo nome"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Fechar"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Pessoais"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Trabalho"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabalho"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Os apps de trabalho são identificados e ficam visíveis para o adm. de TI"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Ok"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 03e26e1..9022d6f 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Nu sugera aplicația"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Fixează predicția"</string>
     <string name="bubble" msgid="3072951361014076670">"Balon"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Fixează pe bara de activități"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalează comenzi rapide"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite unei aplicații să adauge comenzi rapide fără intervenția utilizatorului."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"citește setările și comenzile rapide de pe ecranul de pornire"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d din %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Ecranul de pornire %1$d din %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Pagină nouă pe ecranul de pornire"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Dosar deschis, <xliff:g id="WIDTH">%1$d</xliff:g> pe <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Atinge pentru a închide dosarul"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Atinge pentru a salva noul nume"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Închide"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personale"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Profesionale"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil de serviciu"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Aplicațiile pentru lucru sunt marcate și vizibile pentru administratorul IT"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index f019544..a0230e2 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Не рекомендовать"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Закрепить рекомендацию"</string>
     <string name="bubble" msgid="3072951361014076670">"Подсказка"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Закрепить на панели"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Создание ярлыков"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Приложение сможет самостоятельно добавлять ярлыки."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"Доступ к данным о настройках и ярлыках на главном экране"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Стр. %1$d из %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Главный экран %1$d из %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Новый экран"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"Активно"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"Свернуто"</string>
     <string name="folder_opened" msgid="94695026776264709">"Папка открыта, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Нажмите, чтобы закрыть папку"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Нажмите, чтобы подтвердить переименование"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Закрыть"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Личные"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Рабочие"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Рабочий профиль"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"У рабочих приложений есть специальный значок. Они видны системному администратору."</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"ОК"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index fe27579..7e64c0d 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"යෙදුම යෝජනා නොකරන්න"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"පුරෝකථනය අමුණන්න"</string>
     <string name="bubble" msgid="3072951361014076670">"බුබුළ"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"කාර්ය තීරුවට අමුණන්න"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"කෙටිමං ස්ථාපනය කරන්න"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"පරිශීලක මැදිහත්වීමෙන් තොරව කෙටිමං එක් කිරීමට යෙදුමකට අවසර දෙයි."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"මුල් පිටු සැකසීම් සහ කෙටි මං කියවන්න"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$d හි %1$d පිටුව"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"මුල් පිටු තිරය %2$d හි %1$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"නව මුල් පිටුව"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"ෆෝල්ඩරය විවෘත විය, <xliff:g id="WIDTH">%1$d</xliff:g> හි <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"ෆෝල්ඩරය වැසීමට තට්ටු කරන්න"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"යළි නම් කිරීම සුරැකීමට තට්ටු කරන්න"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"වසන්න"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"පුද්ගලික"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"කාර්යාලය"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"කාර්යාල පැතිකඩ"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"කාර්යාල යෙදුම්වලට ලාංඡන යොදා ඇති අතර ඔබගේ IT පරිපාලකට දෘශ්‍යමාන වේ"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"තේරුණා"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 7410051..ba174ad 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Nenavrhovať aplikáciu"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Pripnúť predpoveď"</string>
     <string name="bubble" msgid="3072951361014076670">"Bublina"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Pripnúť na panel"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"inštalácia odkazov"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Povoľuje aplikácii pridať odkazy bez zásahu používateľa."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"čítanie nastavení a odkazov plochy"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Stránka %1$d z %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Plocha %1$d z %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nová stránka plochy"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"Aktívne"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"Minimalizované"</string>
     <string name="folder_opened" msgid="94695026776264709">"Otvorený priečinok, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Priečinok zavriete klepnutím"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Nový názov uložíte klepnutím"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Zavrieť"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobné"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Pracovné"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Pracovný profil"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Pracovné aplikácie majú odznak a zobrazujú sa správcovi IT"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Dobre"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 9e2307a..9fabdcf 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlagaj"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Predvidevanje pripenjanja"</string>
     <string name="bubble" msgid="3072951361014076670">"Mehurček"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Pripni v opravilno vrstico"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"namestitev bližnjic"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Aplikaciji dovoli dodajanje bližnjic brez posredovanja uporabnika."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"branje nastavitev in bližnjic na začetnem zaslonu"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Stran %1$d od %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Začetni zaslon %1$d od %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nova stran na začetnem zaslonu"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"Aktivno"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"Minimirano"</string>
     <string name="folder_opened" msgid="94695026776264709">"Mapa je odprta, <xliff:g id="WIDTH">%1$d</xliff:g> krat <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Dotaknite se, da zaprete mapo"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Dotaknite se, da shranite preimenovanje"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Zapri"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Osebno"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Delo"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Delovni profil"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Delovne aplikacije so označene z značko in vidne skrbniku za IT."</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Razumem"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index eb6660c..ac47b0a 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Mos sugjero aplikacion"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Gozhdo parashikimin"</string>
     <string name="bubble" msgid="3072951361014076670">"Flluskë"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Gozhdoje te shiriti i detyrave"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalimi i shkurtoreve"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Lejon një aplikacion të shtojë shkurtore pa ndërhyrjen e përdoruesit."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"lexo cilësimet dhe shkurtoret e ekranit bazë"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Faqja: %1$d nga gjithsej %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Ekrani bazë: %1$d nga gjithsej %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Faqja e ekranit të ri kryesor"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Dosja u hap, <xliff:g id="WIDTH">%1$d</xliff:g> me <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Trokit për të mbyllur dosjen"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Trokit për të ruajtur riemërtimin"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Mbyll"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personale"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Punë"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profili i punës"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Aplikacionet e punës janë të shënuara dhe të dukshme për administratorin e teknologjisë së informacionit"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"E kuptova"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index e97e39b..fea7de5 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Не предлажи апликацију"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Закачи предвиђање"</string>
     <string name="bubble" msgid="3072951361014076670">"Облачић"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Закачи за траку зад."</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталирање пречица"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Дозвољава апликацији да додаје пречице без интервенције корисника."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"читање подешавања и пречица на почетном екрану"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d. страница од %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d. почетни екран од %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Нова страница почетног екрана"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"Активно"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"Смањено"</string>
     <string name="folder_opened" msgid="94695026776264709">"Фолдер је отворен, <xliff:g id="WIDTH">%1$d</xliff:g> пута <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Додирните да бисте затворили фолдер"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Додирните да бисте сачували преименовање"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Затвори"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Лично"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Посао"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Пословни профил"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Пословне апликације су означене значком и ИТ администратор може да их види"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Важи"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index ad5e281..7c676e9 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -64,8 +64,8 @@
     <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Rensa texten från sökrutan"</string>
     <string name="no_widgets_available" msgid="4337693382501046170">"Widgetar och genvägar är inte tillgängliga"</string>
     <string name="no_search_results" msgid="3787956167293097509">"Inga widgetar eller genvägar hittades"</string>
-    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Privat"</string>
-    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Arbete"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personligt"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Jobb"</string>
     <string name="widget_category_conversations" msgid="8894438636213590446">"Konversationer"</string>
     <string name="widget_category_note_taking" msgid="3469689394504266039">"Anteckna"</string>
     <string name="widget_cell_tap_to_show_add_button_label" msgid="4354194214317043581">"Visa knappen Lägg till"</string>
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Föreslå inte app"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Fäst förslag"</string>
     <string name="bubble" msgid="3072951361014076670">"Bubbla"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Fäst i aktivitetsfält"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installera genvägar"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Tillåter att en app lägger till genvägar utan åtgärd från användaren."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"läsa inställningar och genvägar på startskärmen"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Sidan %1$d av %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Startskärmen %1$d av %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Ny sida på startskärmen"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Mappen är öppen, <xliff:g id="WIDTH">%1$d</xliff:g> gånger <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Tryck för att stänga mappen"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tryck för att spara namnändringen"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Stäng"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Privat"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Arbete"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Jobbprofil"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Jobbappar är märkta och synliga för IT-administratören"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index bd0029c..06ba981 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Isipendekeze programu"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Bandika Utabiri"</string>
     <string name="bubble" msgid="3072951361014076670">"Kiputo"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Bandika kwa upauzana"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"kuweka njia za mkato"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Huruhusu programu kuongeza njia za mkato bila mtumiaji kuingilia kati."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"kusoma mipangilio ya skrini ya kwanza na njia za mkato"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Ukurasa%1$d wa %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Skrini ya mwanzo %1$d ya %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Ukurasa mpya wa skrini ya kwanza"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Folda imefunguliwa, <xliff:g id="WIDTH">%1$d</xliff:g> kwa <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Gusa ili ufunge folda"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Gusa ili ubadilishe jina"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Funga"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Binafsi"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Kazini"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Wasifu wa kazini"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Programu za kazini zina beji na msimamizi wako wa TEHAMA anaziona"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Nimeelewa"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 30dbbb3..4bb86da 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"பரிந்துரைக்காதே"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"கணிக்கப்பட்டதைப் பின் செய்"</string>
     <string name="bubble" msgid="3072951361014076670">"குமிழ்"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"செயல்பட்டியில் பின் செய்"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"குறுக்குவழிகளை நிறுவுதல்"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"பயனரின் அனுமதி இல்லாமல் குறுக்குவழிகளைச் சேர்க்கப் ஆப்ஸை அனுமதிக்கிறது."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"முகப்புத் திரையின் அமைப்புகளையும் ஷார்ட்கட்களையும் படித்தல்"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"பக்கம் %1$d / %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"முகப்புத் திரை %1$d of %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"புதிய முகப்புத் திரை பக்கம்"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"திறக்கப்பட்ட ஃபோல்டர், <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"ஃபோல்டரை மூட, தட்டவும்"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"மாற்றிய பெயரைச் சேமிக்க, தட்டவும்"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"மூடும் பட்டன்"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"தனிப்பட்டவை"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"பணி"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"பணிக் கணக்கு"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"பணி ஆப்ஸில் பேட்ஜ் இடப்பட்டிருக்கும், உங்கள் IT நிர்வாகியால் பணி ஆப்ஸைப் பார்க்க முடியும்"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"முடிந்தது"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 4b25912..cd4f456 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"యాప్ సూచించకు"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"సూచనను పిన్ చేయండి"</string>
     <string name="bubble" msgid="3072951361014076670">"బబుల్"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"టాస్క్‌బార్‌కు పిన్"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"షార్ట్‌కట్‌లను ఇన్‌స్టాల్ చేయడం"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"వినియోగదారు ప్రమేయం లేకుండా షార్ట్‌కట్‌లను జోడించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"హోమ్ సెట్టింగ్‌లు, షార్ట్‌కట్‌లను చదవండి"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$dలో %1$dవ పేజీ"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dలో %1$dవ హోమ్ స్క్రీన్"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"కొత్త హోమ్ స్క్రీన్ పేజీ"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"యాక్టివ్"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"కుదించబడింది"</string>
     <string name="folder_opened" msgid="94695026776264709">"ఫోల్డర్ తెరవబడింది, <xliff:g id="WIDTH">%1$d</xliff:g> X <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"ఫోల్డర్‌ను మూసివేయడానికి నొక్కండి"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"పేరు మార్పును సేవ్ చేయడానికి నొక్కండి"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"మూసివేస్తుంది"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"వ్యక్తిగతం"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"వర్క్"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"కార్యాలయ ప్రొఫైల్"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"వర్క్ యాప్‌లకు బ్యాడ్జ్ ఉంటుంది, అవి మీ IT అడ్మిన్‌కు కనిపిస్తాయి"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"అర్థమైంది"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index f5fd93d..1aa4a97 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ไม่ต้องแนะนำแอป"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"ปักหมุดแอปที่คาดการณ์ไว้"</string>
     <string name="bubble" msgid="3072951361014076670">"บับเบิล"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"ปักหมุดไปยังแถบงาน"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ติดตั้งทางลัด"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"อนุญาตให้แอปเพิ่มทางลัดโดยไม่ต้องให้ผู้ใช้จัดการ"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"อ่านการตั้งค่าและทางลัดในหน้าแรก"</string>
@@ -118,6 +117,8 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"หน้า %1$d จาก %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"หน้าจอหลัก %1$d จาก %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"หน้าใหม่ในหน้าจอหลัก"</string>
+    <string name="app_running_state_description" msgid="5645053189564740904">"ใช้งานอยู่"</string>
+    <string name="app_minimized_state_description" msgid="710740620044902509">"ลดขนาดเล็กสุด"</string>
     <string name="folder_opened" msgid="94695026776264709">"เปิดโฟลเดอร์ <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"แตะเพื่อปิดโฟลเดอร์"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"แตะเพื่อบันทึกการเปลี่ยนชื่อ"</string>
@@ -187,6 +188,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"ปิด"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"ส่วนตัว"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"งาน"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"โปรไฟล์งาน"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"แอปงานจะติดป้ายไว้และผู้ดูแลระบบไอทีจะมองเห็น"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"รับทราบ"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 4d9a340..b8a0df4 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Huwag magmungkahi"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"I-pin ang Hula"</string>
     <string name="bubble" msgid="3072951361014076670">"Bubble"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"I-pin sa taskbar"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"i-install ang mga shortcut"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Pinapayagan ang isang app na magdagdag ng mga shortcut nang walang panghihimasok ng user."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"basahin ang mga setting at shortcut ng home"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Pahina %1$d ng %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d ng %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Bagong page ng home screen"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Binuksan ang folder, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"I-tap upang isara ang folder"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"I-tap upang i-save ang bagong pangalan"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Isara"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Trabaho"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profile sa trabaho"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"May badge at nakikita ng iyong IT admin ang mga app para sa trabaho"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 3fdae59..5bd7c52 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Uygulamayı önerme"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Tahmini Sabitle"</string>
     <string name="bubble" msgid="3072951361014076670">"Balon"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Çubuğa sabitle"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"kısayolları yükle"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Uygulamaya, kullanıcı müdahalesi olmadan kısayol ekleme izni verir."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ana ekran ayarlarını ve kısayollarını oku"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Sayfa %1$d / %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Ana ekran %1$d / %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Yeni ana ekran sayfası"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Klasör açıldı, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Klasörü kapatmak için dokunun"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Yeni adın kaydedilmesi için dokunun"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Kapat"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Kişisel"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"İş"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"İş profili"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"İş uygulamaları rozetle işaretlenmiş olup BT yöneticisi tarafından görülebilir"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Anladım"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index fd3855d..798c567 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Не пропонувати додаток"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Закріпити передбачений додаток"</string>
     <string name="bubble" msgid="3072951361014076670">"Повідомлення"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"На панель завдань"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"створення ярликів"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Дозволяє програмі самостійно додавати ярлики."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"читати налаштування та ярлики головного екрана"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Сторінка %1$d з %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Головний екран %1$d з %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Нова сторінка головного екрана"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Папку відкрито (<xliff:g id="WIDTH">%1$d</xliff:g> х <xliff:g id="HEIGHT">%2$d</xliff:g>)"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Торкніться, щоб закрити папку"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Торкніться, щоб зберегти зміни"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Закрити"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Особисті додатки"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Робочі додатки"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Робочий профіль"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Робочі додатки мають спеціальну позначку. Їх бачить системний адміністратор."</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 855a8c3..ec5129d 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ایپ تجویز نہ کریں"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"پیشگوئی پن کریں"</string>
     <string name="bubble" msgid="3072951361014076670">"بلبلہ"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"ٹاسک بار میں پن کریں"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"شارٹ کٹس انسٹال کریں"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"کسی ایپ کو صارف کی مداخلت کے بغیر شارٹ کٹس شامل کرنے کی اجازت دیتا ہے۔"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"ہوم ترتیبات اور شارٹ کٹس کو پڑھیں"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"‏صفحہ ‎%1$d از ‎%2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"‏ہوم اسکرین ‎%1$d از ‎%2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"نیا ہوم اسکرین صفحہ"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"فولڈر کھولا گیا، <xliff:g id="WIDTH">%1$d</xliff:g> × <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"فولڈر کو بند کرنے کیلئے تھپتھپائیں"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"نام کی تبدیلی محفوظ کرنے کیلئے تھپتھپائیں"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"بند کریں"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"ذاتی"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"دفتری"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"دفتری پروفائل"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"‏ورک ایپس پر بَیج لگا ہوتا ہے اور آپ کا IT منتظم انہیں دیکھ سکتا ہے"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"سمجھ آ گئی"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index f96b6a2..48068d6 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Tavsiya qilinmasin"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Tavsiyani mahkamlash"</string>
     <string name="bubble" msgid="3072951361014076670">"Pufaklar"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Panelga qadash"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"yorliqlar yaratish"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Ilovalarga foydalanuvchidan so‘ramasdan yorliqlar qo‘shishga ruxsat beradi."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"Bosh ekrandagi sozlamalar va yorliqlarni koʻrish"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$ddan %1$d ta sahifa"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Uy ekrani %2$ddan %1$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Yangi bosh ekran sahifasi"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Jild ochildi, <xliff:g id="WIDTH">%1$d</xliff:g> ga <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Jildni yopish uchun ustiga bosing"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"O‘zgarishni saqlash uchun ustiga bosing"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Yopish"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Shaxsiy"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Ish"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Ish profili"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Ishga oid ilovalarning maxsus belgisi bor hamda ular administratoringizga koʻrinadi"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 1d886b2..5b88099 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Không gợi ý ứng dụng"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Ghim ứng dụng dự đoán"</string>
     <string name="bubble" msgid="3072951361014076670">"Bong bóng"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Ghim vào thanh tác vụ"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"cài đặt lối tắt"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Cho phép ứng dụng thêm lối tắt mà không cần sự can thiệp của người dùng."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"đọc lối tắt và các chế độ cài đặt trên màn hình chính"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Trang %1$d / %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Màn hình chính %1$d / %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Trang màn hình chính mới"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Đã mở thư mục, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Nhấn để đóng thư mục"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Nhấn để lưu đổi tên"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Đóng"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Cá nhân"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Công việc"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Hồ sơ công việc"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Các ứng dụng công việc được gắn huy hiệu và quản trị viên CNTT có thể nhìn thấy"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index febc8c2..7b51d1d 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -62,7 +62,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"微件"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"搜索"</string>
     <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"清除搜索框中的文字"</string>
-    <string name="no_widgets_available" msgid="4337693382501046170">"无法使用微件和快捷方式"</string>
+    <string name="no_widgets_available" msgid="4337693382501046170">"没有可用的微件和快捷方式"</string>
     <string name="no_search_results" msgid="3787956167293097509">"未找到任何微件或快捷方式"</string>
     <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"个人"</string>
     <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"工作"</string>
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"不要推荐此应用"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"固定预测的应用"</string>
     <string name="bubble" msgid="3072951361014076670">"气泡框"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"固定到任务栏"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"安装快捷方式"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"允许应用自行添加快捷方式。"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"读取主屏幕设置和快捷方式"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"第%1$d页,共%2$d页"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"主屏幕:第%1$d屏,共%2$d屏"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"主屏幕新页面"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"文件夹已打开,大小为<xliff:g id="WIDTH">%1$d</xliff:g>×<xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"点按可关闭文件夹"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"点按可保存新名称"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"关闭"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"个人"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"工作"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"工作资料"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"工作应用都有相应的标志,且您的 IT 管理员可以看到它们"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"知道了"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 5a573a1..0dd1801 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"不要提供應用程式建議"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"固定預測"</string>
     <string name="bubble" msgid="3072951361014076670">"氣泡"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"固定至工作列"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"安裝捷徑"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"允許應用程式無需使用者許可也可新增捷徑。"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"讀取主畫面設定和捷徑"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"第 %1$d 頁,共 %2$d 頁"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"主畫面 %1$d,共 %2$d 個"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"新主畫面頁面"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"資料夾已開啟 (<xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>)"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"輕按即可關閉資料夾"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"輕按即可儲存新名稱"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"關閉"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"個人"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"工作"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"工作設定檔"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"工作應用程式均加有標誌。你的 IT 管理員可以看到這些應用程式"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"知道了"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 6cf783c..424939b 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"不要建議此應用程式"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"固定預測的應用程式"</string>
     <string name="bubble" msgid="3072951361014076670">"泡泡"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"固定到工作列"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"安裝捷徑"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"允許應用程式自動新增捷徑。"</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"讀取主畫面設定和捷徑"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"第 %1$d 頁,共 %2$d 頁"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"主畫面:第 %1$d 頁,共 %2$d 頁"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"新的主畫面頁面"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"資料夾已開啟 (<xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>)"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"輕觸即可關閉資料夾"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"輕觸即可儲存新名稱"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"關閉"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"個人"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"工作"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"工作資料夾"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"工作應用程式會加上標記,且你的 IT 管理員可以看到這類應用程式"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"我知道了"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 58d1188..918b7ca 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -101,7 +101,6 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ungaphakamisi uhlelo lokusebenza"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Ukubikezela Iphinikhodi"</string>
     <string name="bubble" msgid="3072951361014076670">"Ibhamuza"</string>
-    <string name="pin_to_taskbar" msgid="1281337899690299038">"Phina kutaskbar"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"faka izinqamuleli"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Ivumela uhlelo lokusebenza ukufaka izinqamuleli ngaphandle kokungenelela komsebenzisi."</string>
     <string name="permlab_read_settings" msgid="5136500343007704955">"funda amasethingi wasekhaya nezinqamuleli"</string>
@@ -118,6 +117,10 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"Ikhasi elingu-%1$d kwangu-%2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Isikrini sasekhaya esingu-%1$d se-%2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Ikhasi elisha lesikrini sasekhaya"</string>
+    <!-- no translation found for app_running_state_description (5645053189564740904) -->
+    <skip />
+    <!-- no translation found for app_minimized_state_description (710740620044902509) -->
+    <skip />
     <string name="folder_opened" msgid="94695026776264709">"Ifolda ivuliwe, <xliff:g id="WIDTH">%1$d</xliff:g> nge-<xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Thepa ukuze uvale ifolda"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Thepha ukuze ulondoloze ukuqamba kabusha"</string>
@@ -187,6 +190,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Vala"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Okomuntu siqu"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Umsebenzi"</string>
+    <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+    <skip />
+    <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+    <skip />
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Iphrofayela yomsebenzi"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Ama-app omsebenzi anebheji futhi ayabonakala kumphathi wakho we-IT"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Ngiyezwa"</string>
diff --git a/res/values/config.xml b/res/values/config.xml
index 1a2ac9e..d65580c 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -80,9 +80,6 @@
     <string name="local_colors_extraction_class" translatable="false"></string>
     <string name="search_session_manager_class" translatable="false"></string>
 
-    <!-- Filters for widgets displayed in the widget picker  -->
-    <string name="widgets_filter_data_provider_class" translatable="false"></string>
-
     <!-- Scalable Grid configuration -->
     <!-- This is a float because it is converted to dp later in DeviceProfile -->
     <dimen name="hotseat_bar_bottom_space_default">48</dimen>
@@ -117,6 +114,8 @@
     <!-- Expressive Dismiss -->
     <item name="expressive_dismiss_task_trans_y_damping_ratio" type="dimen" format="float">0.6</item>
     <item name="expressive_dismiss_task_trans_y_stiffness" type="dimen" format="float">900</item>
+    <item name="expressive_dismiss_task_trans_x_damping_ratio" type="dimen" format="float">0.8</item>
+    <item name="expressive_dismiss_task_trans_x_stiffness" type="dimen" format="float">900</item>
 
     <!-- Taskbar -->
     <!-- This is a float because it is converted to dp later in DeviceProfile -->
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index c3cb31d..a15c130 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -23,6 +23,11 @@
     <dimen name="dynamic_grid_left_right_margin">8dp</dimen>
     <!-- Minimum amount of next page visible in spring loaded mode -->
     <dimen name="dynamic_grid_spring_loaded_min_next_space_visible">48dp</dimen>
+    <item name="aspect_ratio_portrait" format="float" type="dimen">1.0</item>
+    <!-- 1.05 was the constant we found to validate square-ish devices
+    to load portrait orientation specs for responsive grids -->
+    <item name="aspect_ratio_portrait_and_square" format="float" type="dimen">1.05</item>
+    <item name="aspect_ratio_landscape" format="float" type="dimen">10</item>
 
     <dimen name="dynamic_grid_cell_border_spacing">16dp</dimen>
     <dimen name="cell_layout_padding">10.77dp</dimen>
@@ -307,6 +312,7 @@
 
     <!-- Folders -->
     <dimen name="page_indicator_dot_size">6dp</dimen>
+    <dimen name="page_indicator_gap_width">4dp</dimen>
     <dimen name="page_indicator_size">10dp</dimen>
 
 
@@ -439,7 +445,6 @@
     <dimen name="taskbar_running_app_indicator_height">0dp</dimen>
     <dimen name="taskbar_running_app_indicator_width">0dp</dimen>
     <dimen name="taskbar_running_app_indicator_top_margin">0dp</dimen>
-    <dimen name="taskbar_minimized_app_indicator_width">0dp</dimen>
 
     <!-- Transient taskbar (placeholders to compile in Launcher3 without Quickstep) -->
     <dimen name="transient_taskbar_padding">0dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a02516a..cc740a5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -235,8 +235,6 @@
     <string name="pin_prediction">Pin Prediction</string>
     <!-- Label for bubbling a launcher item. [CHAR_LIMIT=20] -->
     <string name="bubble">Bubble</string>
-    <!-- Label for pinning an item to the taskbar. [CHAR_LIMIT=20] -->
-    <string name="pin_to_taskbar">Pin to taskbar</string>
 
     <!-- Permissions: -->
     <skip />
@@ -293,6 +291,9 @@
     <!-- Description for a new page on homescreen[CHAR_LIMIT=none] -->
     <string name="workspace_new_page">New home screen page</string>
 
+    <string name="app_running_state_description">Active</string>
+    <string name="app_minimized_state_description">Minimized</string>
+
     <!-- Folder accessibility -->
     <!-- The format string for when a folder is opened, speaks the dimensions -->
     <string name="folder_opened">Folder opened, <xliff:g id="width" example="5">%1$d</xliff:g> by <xliff:g id="height" example="3">%2$d</xliff:g></string>
@@ -476,9 +477,12 @@
 
     <!-- Label of tab to indicate personal apps -->
     <string name="all_apps_personal_tab">Personal</string>
-
     <!-- Label of tab to indicate work apps -->
     <string name="all_apps_work_tab">Work</string>
+    <!-- Content description of personal tab to indicate personal apps -->
+    <string name="all_apps_personal_tab_content_description">Personal apps tab</string>
+    <!-- Content description of work tab to indicate work apps -->
+    <string name="all_apps_work_tab_content_description">Work apps tab</string>
 
     <!-- This string is in the work profile tab when a user has All Apps open on their phone. This is a label for a toggle to turn the work profile on and off. "Work profile" means a separate profile on a user's phone that's specifically for their work apps and managed by their company. "Work" is used as an adjective.-->
     <string name="work_profile_toggle_label">Work profile</string>
diff --git a/res/xml/default_workspace_7x3.xml b/res/xml/default_workspace_7x3.xml
new file mode 100644
index 0000000..d17491e
--- /dev/null
+++ b/res/xml/default_workspace_7x3.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 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.
+-->
+<!-- Google-specific version of Launcher3/res/xml/default_workspace.xml -->
+<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
+
+    <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
+    <!-- Dialer Messaging Play Chrome Camera -->
+    <favorite
+        launcher:container="-101"
+        launcher:screen="0"
+        launcher:x="0"
+        launcher:y="0"
+        launcher:className="com.google.android.dialer.extensions.GoogleDialtactsActivity"
+        launcher:packageName="com.google.android.dialer" />
+
+    <favorite
+        launcher:container="-101"
+        launcher:screen="1"
+        launcher:x="1"
+        launcher:y="0"
+        launcher:className="com.google.android.apps.messaging.ui.ConversationListActivity"
+        launcher:packageName="com.google.android.apps.messaging" />
+
+    <favorite
+        launcher:container="-101"
+        launcher:screen="2"
+        launcher:x="2"
+        launcher:y="0"
+        launcher:className="com.android.vending.AssetBrowserActivity"
+        launcher:packageName="com.android.vending" />
+
+    <favorite
+        launcher:container="-101"
+        launcher:screen="3"
+        launcher:x="3"
+        launcher:y="0"
+        launcher:className="com.google.android.apps.chrome.Main"
+        launcher:packageName="com.android.chrome" />
+
+    <!-- Resolve camera intent if GoogleCamera is not available e.g. on emulator -->
+    <resolve
+        launcher:container="-101"
+        launcher:screen="4"
+        launcher:x="4"
+        launcher:y="0" >
+        <favorite
+            launcher:className="com.android.camera.CameraLauncher"
+            launcher:packageName="com.google.android.GoogleCamera" />
+        <favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
+        <favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
+    </resolve>
+
+    <!-- Bottom row -->
+    <!-- [space] [space] [space] [space] [space] -->
+
+</favorites>
diff --git a/res/xml/default_workspace_8x3.xml b/res/xml/default_workspace_8x3.xml
new file mode 100644
index 0000000..d17491e
--- /dev/null
+++ b/res/xml/default_workspace_8x3.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 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.
+-->
+<!-- Google-specific version of Launcher3/res/xml/default_workspace.xml -->
+<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
+
+    <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
+    <!-- Dialer Messaging Play Chrome Camera -->
+    <favorite
+        launcher:container="-101"
+        launcher:screen="0"
+        launcher:x="0"
+        launcher:y="0"
+        launcher:className="com.google.android.dialer.extensions.GoogleDialtactsActivity"
+        launcher:packageName="com.google.android.dialer" />
+
+    <favorite
+        launcher:container="-101"
+        launcher:screen="1"
+        launcher:x="1"
+        launcher:y="0"
+        launcher:className="com.google.android.apps.messaging.ui.ConversationListActivity"
+        launcher:packageName="com.google.android.apps.messaging" />
+
+    <favorite
+        launcher:container="-101"
+        launcher:screen="2"
+        launcher:x="2"
+        launcher:y="0"
+        launcher:className="com.android.vending.AssetBrowserActivity"
+        launcher:packageName="com.android.vending" />
+
+    <favorite
+        launcher:container="-101"
+        launcher:screen="3"
+        launcher:x="3"
+        launcher:y="0"
+        launcher:className="com.google.android.apps.chrome.Main"
+        launcher:packageName="com.android.chrome" />
+
+    <!-- Resolve camera intent if GoogleCamera is not available e.g. on emulator -->
+    <resolve
+        launcher:container="-101"
+        launcher:screen="4"
+        launcher:x="4"
+        launcher:y="0" >
+        <favorite
+            launcher:className="com.android.camera.CameraLauncher"
+            launcher:packageName="com.google.android.GoogleCamera" />
+        <favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
+        <favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
+    </resolve>
+
+    <!-- Bottom row -->
+    <!-- [space] [space] [space] [space] [space] -->
+
+</favorites>
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 1d0dbff..5fac66f 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -207,4 +207,45 @@
 
     </grid-option>
 
+    <grid-option
+        launcher:name="fixed_landscape_mode"
+        launcher:numFolderRows="2"
+        launcher:numFolderColumns="3"
+        launcher:numFolderRowsLandscape="2"
+        launcher:folderStyle="@style/FolderStyleDefault"
+        launcher:numHotseatIcons="4"
+        launcher:numExtendedHotseatIcons="8"
+        launcher:numAllAppsColumns="8"
+        launcher:numExtendedAllAppsColumns="8"
+        launcher:workspaceSpecsId="@xml/spec_handheld_workspace_3_row"
+        launcher:allAppsSpecsId="@xml/spec_handheld_all_apps_3_row"
+        launcher:folderSpecsId="@xml/spec_handheld_folder_3_row"
+        launcher:hotseatSpecsId="@xml/spec_handheld_hotseat_3_row"
+        launcher:workspaceCellSpecsId="@xml/spec_handheld_workspace_cell_3_row"
+        launcher:allAppsCellSpecsId="@xml/spec_all_apps_cell_match_workspace"
+        launcher:isScalable="true"
+        launcher:inlineQsb="landscape"
+        launcher:devicePaddingId="@xml/paddings_3_row"
+        launcher:gridSizeSpecsId="@xml/spec_col_count_3_row"
+        launcher:isFixedLandscape="true"
+        launcher:defaultLayoutId="@xml/default_workspace_8x3"
+        launcher:deviceCategory="phone">
+
+        <display-option
+            launcher:name="Fixed Landscape"
+            launcher:minWidthDps="387"
+            launcher:minHeightDps="750"
+            launcher:minCellHeight="108"
+            launcher:minCellWidth="61"
+            launcher:borderSpace="16"
+            launcher:horizontalMargin="22"
+            launcher:iconImageSize="57"
+            launcher:iconSizeLandscape="59"
+            launcher:iconTextSize="12"
+            launcher:allAppsBorderSpace="16"
+            launcher:allAppsCellHeight="104"
+            launcher:canBeDefault="true" />
+
+    </grid-option>
+
 </profiles>
\ No newline at end of file
diff --git a/res/xml/paddings_3_row.xml b/res/xml/paddings_3_row.xml
new file mode 100644
index 0000000..147c9d1
--- /dev/null
+++ b/res/xml/paddings_3_row.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<device-paddings xmlns:launcher="http://schemas.android.com/apk/res-auto" >
+
+    <device-padding
+        launcher:maxEmptySpace="100dp">
+        <workspaceTopPadding
+            launcher:a="0"
+            launcher:b="0"/>
+        <workspaceBottomPadding
+            launcher:a="0"
+            launcher:b="0"/>
+        <hotseatBottomPadding
+            launcher:a="0"
+            launcher:b="0"/>
+    </device-padding>
+
+    <device-padding
+        launcher:maxEmptySpace="160dp">
+        <workspaceTopPadding
+            launcher:a="0"
+            launcher:b="0"/>
+        <workspaceBottomPadding
+            launcher:a="0"
+            launcher:b="0"/>
+        <hotseatBottomPadding
+            launcher:a="0"
+            launcher:b="0"/>
+    </device-padding>
+
+    <device-padding
+        launcher:maxEmptySpace="9999dp">
+        <workspaceTopPadding
+            launcher:a="0"
+            launcher:b="0"/>
+        <workspaceBottomPadding
+            launcher:a="0"
+            launcher:b="0"/>
+        <hotseatBottomPadding
+            launcher:a="0"
+            launcher:b="0"/>
+    </device-padding>
+</device-paddings>
\ No newline at end of file
diff --git a/res/xml/spec_all_apps_cell_match_workspace.xml b/res/xml/spec_all_apps_cell_match_workspace.xml
new file mode 100644
index 0000000..5843490
--- /dev/null
+++ b/res/xml/spec_all_apps_cell_match_workspace.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<cellSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+    <specs launcher:maxAspectRatio="@dimen/aspect_ratio_landscape">
+        <cellSpec
+            launcher:dimensionType="height"
+            launcher:maxAvailableSize="9999dp">
+            <iconDrawablePadding launcher:matchWorkspace="true" />
+            <iconSize launcher:matchWorkspace="true" />
+            <iconTextSize launcher:matchWorkspace="true" />
+        </cellSpec>
+    </specs>
+</cellSpecs>
\ No newline at end of file
diff --git a/res/xml/spec_col_count_3_row.xml b/res/xml/spec_col_count_3_row.xml
new file mode 100644
index 0000000..bbde542
--- /dev/null
+++ b/res/xml/spec_col_count_3_row.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 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.
+  -->
+<GridSizeSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+    <GridSize
+        launcher:numGridColumns="7"
+        launcher:numGridRows="3"
+        launcher:dbFile="launcher_7_by_3.db"
+        launcher:defaultLayoutId="@xml/default_workspace_7x3"
+        launcher:minDeviceWidthPx="0"
+        launcher:minDeviceHeightPx="0"
+        />
+    <GridSize
+        launcher:numGridColumns="8"
+        launcher:numGridRows="3"
+        launcher:dbFile="launcher_8_by_3.db"
+        launcher:defaultLayoutId="@xml/default_workspace_8x3"
+        launcher:minDeviceWidthPx="0"
+        launcher:minDeviceHeightPx="2093"
+        />
+</GridSizeSpecs>
\ No newline at end of file
diff --git a/res/xml/spec_handheld_all_apps_3_row.xml b/res/xml/spec_handheld_all_apps_3_row.xml
new file mode 100644
index 0000000..00b3310
--- /dev/null
+++ b/res/xml/spec_handheld_all_apps_3_row.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<allAppsSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+    <!-- landscape -->
+    <specs launcher:maxAspectRatio="@dimen/aspect_ratio_landscape">
+        <allAppsSpec
+            launcher:dimensionType="height"
+            launcher:maxAvailableSize="9999dp">
+            <startPadding launcher:fixedSize="0dp" />
+            <endPadding launcher:fixedSize="0dp" />
+            <gutter launcher:matchWorkspace="true" />
+            <cellSize launcher:matchWorkspace="true" />
+        </allAppsSpec>
+
+        <allAppsSpec
+            launcher:dimensionType="width"
+            launcher:maxAvailableSize="9999dp">
+            <startPadding launcher:ofRemainderSpace="0.5" />
+            <endPadding launcher:ofRemainderSpace="0.5" />
+            <gutter launcher:matchWorkspace="true" />
+            <cellSize launcher:matchWorkspace="true" />
+        </allAppsSpec>
+    </specs>
+</allAppsSpecs>
diff --git a/res/xml/spec_handheld_folder_3_row.xml b/res/xml/spec_handheld_folder_3_row.xml
new file mode 100644
index 0000000..2614d96
--- /dev/null
+++ b/res/xml/spec_handheld_folder_3_row.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Handheld folders 7x3 -->
+<folderSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+    <!-- landscape -->
+    <specs launcher:maxAspectRatio="@dimen/aspect_ratio_landscape">
+        <folderSpec launcher:dimensionType="width" launcher:maxAvailableSize="9999dp">
+            <startPadding launcher:fixedSize="16dp" />
+            <endPadding launcher:fixedSize="16dp" />
+            <gutter launcher:fixedSize="16dp" />
+            <cellSize launcher:fixedSize="66dp" />
+        </folderSpec>
+        <folderSpec launcher:dimensionType="height" launcher:maxAvailableSize="9999dp">
+            <startPadding launcher:fixedSize="16dp" />
+            <!-- mapped to footer height size -->
+            <endPadding launcher:fixedSize="48dp" />
+            <gutter launcher:fixedSize="0dp" />
+            <cellSize launcher:fixedSize="80dp" />
+        </folderSpec>
+    </specs>
+</folderSpecs>
diff --git a/res/xml/spec_handheld_hotseat_3_row.xml b/res/xml/spec_handheld_hotseat_3_row.xml
new file mode 100644
index 0000000..bd47c90
--- /dev/null
+++ b/res/xml/spec_handheld_hotseat_3_row.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<hotseatSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+    <!-- landscape -->
+    <specs launcher:maxAspectRatio="@dimen/aspect_ratio_landscape">
+        <hotseatSpec
+            launcher:maxAvailableSize="9999dp"
+            launcher:dimensionType="width">
+            <hotseatQsbSpace launcher:fixedSize="0dp" />
+            <edgePadding launcher:fixedSize="0dp" />
+        </hotseatSpec>
+    </specs>
+</hotseatSpecs>
\ No newline at end of file
diff --git a/res/xml/spec_handheld_workspace_3_row.xml b/res/xml/spec_handheld_workspace_3_row.xml
new file mode 100644
index 0000000..8e024d3
--- /dev/null
+++ b/res/xml/spec_handheld_workspace_3_row.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<workspaceSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+    <!-- landscape -->
+    <specs launcher:maxAspectRatio="@dimen/aspect_ratio_landscape">
+        <workspaceSpec
+            launcher:dimensionType="height"
+            launcher:maxAvailableSize="9999dp">
+            <startPadding launcher:ofAvailableSpace="0.01" />
+            <endPadding launcher:ofAvailableSpace="0.055" />
+            <gutter launcher:ofAvailableSpace="0.02" />
+            <cellSize launcher:ofRemainderSpace="1" />
+        </workspaceSpec>
+        <workspaceSpec
+            launcher:dimensionType="width"
+            launcher:maxAvailableSize="9999dp">
+            <startPadding launcher:ofAvailableSpace="0.0660867583" />
+            <endPadding launcher:ofAvailableSpace="0.0660867583" />
+            <gutter launcher:ofAvailableSpace="0.0125" />
+            <cellSize launcher:ofRemainderSpace="1" />
+        </workspaceSpec>
+    </specs>
+</workspaceSpecs>
diff --git a/res/xml/spec_handheld_workspace_cell_3_row.xml b/res/xml/spec_handheld_workspace_cell_3_row.xml
new file mode 100644
index 0000000..607dbb9
--- /dev/null
+++ b/res/xml/spec_handheld_workspace_cell_3_row.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<cellSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+    <!-- portrait TODO: remove portrait specs-->
+    <specs launcher:maxAspectRatio="@dimen/aspect_ratio_portrait">
+        <cellSpec
+            launcher:dimensionType="height"
+            launcher:maxAvailableSize="9999dp">
+            <iconDrawablePadding launcher:fixedSize="0dp" />
+            <iconSize launcher:fixedSize="@dimen/iconSize52dp" />
+            <iconTextSize launcher:fixedSize="12sp" />
+        </cellSpec>
+    </specs>
+    <!-- landscape -->
+    <specs launcher:maxAspectRatio="@dimen/aspect_ratio_landscape">
+        <cellSpec
+            launcher:dimensionType="height"
+            launcher:maxAvailableSize="9999dp">
+            <iconDrawablePadding launcher:fixedSize="4dp" />
+            <iconSize launcher:fixedSize="@dimen/iconSize52dp" />
+            <iconTextSize launcher:fixedSize="12sp" />
+        </cellSpec>
+    </specs>
+</cellSpecs>
\ No newline at end of file
diff --git a/shared/src/com/android/launcher3/testing/shared/TestProtocol.java b/shared/src/com/android/launcher3/testing/shared/TestProtocol.java
index 0583d6d..cdeab95 100644
--- a/shared/src/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/shared/src/com/android/launcher3/testing/shared/TestProtocol.java
@@ -125,6 +125,8 @@
             "is-predictive-back-swipe-enabled";
     public static final String REQUEST_ENABLE_TASKBAR_NAVBAR_UNIFICATION =
             "enable-taskbar-navbar-unification";
+    public static final String REQUEST_TASKBAR_SHOWN_ON_HOME =
+            "taskbar-shown-on-home";
     public static final String REQUEST_NUM_ALL_APPS_COLUMNS = "num-all-apps-columns";
     public static final String REQUEST_IS_TWO_PANELS = "is-two-panel";
     public static final String REQUEST_CELL_LAYOUT_BOARDER_HEIGHT = "cell-layout-boarder-height";
diff --git a/src/android/os/BinderUtils.kt b/src/android/os/BinderUtils.kt
new file mode 100644
index 0000000..0536283
--- /dev/null
+++ b/src/android/os/BinderUtils.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2025 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 android.os
+
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import com.android.launcher3.util.WeakCleanupSet
+import com.android.launcher3.util.WeakCleanupSet.OnOwnerDestroyedCallback
+
+/** Utility methods related to Binder */
+object BinderUtils {
+
+    /** Creates a binder wrapper which is tied to the [lifecycle] */
+    @JvmStatic
+    fun <T : Binder> T.wrapLifecycle(cleanupSet: WeakCleanupSet): Binder =
+        LifecycleBinderWrapper(this, cleanupSet)
+
+    private class LifecycleBinderWrapper<T : Binder>(
+        private var realBinder: T?,
+        cleanupSet: WeakCleanupSet,
+    ) : Binder(realBinder?.interfaceDescriptor), OnOwnerDestroyedCallback {
+
+        init {
+            MAIN_EXECUTOR.execute { cleanupSet.addOnOwnerDestroyedCallback(this) }
+        }
+
+        override fun queryLocalInterface(descriptor: String): IInterface? =
+            realBinder?.queryLocalInterface(descriptor)
+
+        override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean =
+            realBinder?.transact(code, data, reply, flags)
+                ?: throw RemoteException("Original binder cleaned up")
+
+        override fun onOwnerDestroyed() {
+            realBinder = null
+        }
+    }
+}
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 753b2e2..b90200b 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -126,7 +126,8 @@
     /** Type of popups that should get exclusive accessibility focus. */
     public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE & ~TYPE_LISTENER
             & ~TYPE_ALL_APPS_EDU & ~TYPE_TASKBAR_ALL_APPS & ~TYPE_PIN_IME_POPUP
-            & ~TYPE_WIDGET_RESIZE_FRAME & ~TYPE_ONE_GRID_MIGRATION_EDU & ~TYPE_ON_BOARD_POPUP;
+            & ~TYPE_WIDGET_RESIZE_FRAME & ~TYPE_ONE_GRID_MIGRATION_EDU & ~TYPE_ON_BOARD_POPUP
+            & ~TYPE_TASKBAR_OVERLAY_PROXY;
 
     // These view all have particular operation associated with swipe down interaction.
     public static final int TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW = TYPE_WIDGETS_BOTTOM_SHEET |
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 6277e41..2426a61 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -37,6 +37,10 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleRegistry;
+import androidx.savedstate.SavedStateRegistry;
+import androidx.savedstate.SavedStateRegistryController;
 
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.logging.StatsLogManager;
@@ -47,9 +51,11 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
 import com.android.launcher3.util.DisplayController.Info;
+import com.android.launcher3.util.LifecycleHelper;
 import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.ViewCache;
+import com.android.launcher3.util.WeakCleanupSet;
 import com.android.launcher3.util.WindowBounds;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.ScrimView;
@@ -97,11 +103,15 @@
     private final ArrayList<MultiWindowModeChangedListener> mMultiWindowModeChangedListeners =
             new ArrayList<>();
 
+    private final SavedStateRegistryController mSavedStateRegistryController =
+            SavedStateRegistryController.create(this);
+    private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
+    private final WeakCleanupSet mCleanupSet = new WeakCleanupSet(this);
+
     protected DeviceProfile mDeviceProfile;
     protected SystemUiController mSystemUiController;
     private StatsLogManager mStatsLogManager;
 
-
     public static final int ACTIVITY_STATE_STARTED = 1 << 0;
     public static final int ACTIVITY_STATE_RESUMED = 1 << 1;
 
@@ -178,6 +188,12 @@
 
     private ActionMode mCurrentActionMode;
 
+    public BaseActivity() {
+        mSavedStateRegistryController.performAttach();
+        registerActivityLifecycleCallbacks(
+                new LifecycleHelper(this, mSavedStateRegistryController, mLifecycleRegistry));
+    }
+
     @Override
     public ViewCache getViewCache() {
         return mViewCache;
@@ -478,13 +494,28 @@
 
     protected void reapplyUi() {}
 
+    @NonNull
+    @Override
+    public SavedStateRegistry getSavedStateRegistry() {
+        return mSavedStateRegistryController.getSavedStateRegistry();
+    }
+
+    @NonNull
+    @Override
+    public Lifecycle getLifecycle() {
+        return mLifecycleRegistry;
+    }
+
+    @Override
+    public WeakCleanupSet getOwnerCleanupSet() {
+        return mCleanupSet;
+    }
+
     public static <T extends BaseActivity> T fromContext(Context context) {
         if (context instanceof BaseActivity) {
             return (T) context;
-        } else if (context instanceof ActivityContextDelegate) {
-            return (T) ((ActivityContextDelegate) context).mDelegate;
-        } else if (context instanceof ContextWrapper) {
-            return fromContext(((ContextWrapper) context).getBaseContext());
+        } else if (context instanceof ContextWrapper cw) {
+            return fromContext(cw.getBaseContext());
         } else {
             throw new IllegalArgumentException("Cannot find BaseActivity in parent tree");
         }
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index bd42b2b..30e3a2b 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -20,8 +20,13 @@
 import static android.graphics.fonts.FontStyle.FONT_WEIGHT_NORMAL;
 import static android.text.Layout.Alignment.ALIGN_NORMAL;
 
+import static com.android.app.animation.Interpolators.EMPHASIZED;
+import static com.android.launcher3.BubbleTextView.RunningAppState.RUNNING;
+import static com.android.launcher3.BubbleTextView.RunningAppState.NOT_RUNNING;
+import static com.android.launcher3.BubbleTextView.RunningAppState.MINIMIZED;
 import static com.android.launcher3.Flags.enableContrastTiles;
 import static com.android.launcher3.Flags.enableCursorHoverStates;
+import static com.android.launcher3.allapps.AlphabeticalAppsList.PRIVATE_SPACE_PACKAGE;
 import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
 import static com.android.launcher3.icons.BitmapInfo.FLAG_NO_BADGE;
 import static com.android.launcher3.icons.BitmapInfo.FLAG_SKIP_USER_BADGE;
@@ -34,6 +39,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -99,6 +105,7 @@
 import java.text.NumberFormat;
 import java.util.HashMap;
 import java.util.Locale;
+import java.util.Objects;
 
 /**
  * TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan
@@ -127,6 +134,9 @@
             StringMatcherUtility.StringMatcher.getInstance();
     private static final int BOLD_TEXT_ADJUSTMENT = FONT_WEIGHT_BOLD - FONT_WEIGHT_NORMAL;
 
+    public static final int LINE_INDICATOR_ANIM_DURATION = 150;
+    private static final float MINIMIZED_APP_INDICATOR_SCALE = 0.5f;
+
     private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
 
     private float mScaleForReorderBounce = 1f;
@@ -162,6 +172,36 @@
         }
     };
 
+    private static final Property<BubbleTextView, Integer> LINE_INDICATOR_COLOR_PROPERTY =
+            new Property<>(Integer.class, "lineIndicatorColor") {
+
+                @Override
+                public Integer get(BubbleTextView bubbleTextView) {
+                    return bubbleTextView.mLineIndicatorColor;
+                }
+
+                @Override
+                public void set(BubbleTextView bubbleTextView, Integer color) {
+                    bubbleTextView.mLineIndicatorColor = color;
+                    bubbleTextView.invalidate();
+                }
+            };
+
+    private static final Property<BubbleTextView, Float> LINE_INDICATOR_SCALE_PROPERTY =
+            new Property<>(Float.TYPE, "lineIndicatorScale") {
+
+                @Override
+                public Float get(BubbleTextView bubbleTextView) {
+                    return bubbleTextView.mLineIndicatorScale;
+                }
+
+                @Override
+                public void set(BubbleTextView bubbleTextView, Float scale) {
+                    bubbleTextView.mLineIndicatorScale = scale;
+                    bubbleTextView.invalidate();
+                }
+            };
+
     private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this);
     protected final ActivityContext mActivity;
     private FastBitmapDrawable mIcon;
@@ -199,7 +239,6 @@
 
     // These fields, related to showing running apps, are only used for Taskbar.
     private final int mRunningAppIndicatorWidth;
-    private final int mMinimizedAppIndicatorWidth;
     private final int mRunningAppIndicatorHeight;
     private final int mRunningAppIndicatorTopMargin;
     private final Paint mRunningAppIndicatorPaint;
@@ -207,6 +246,15 @@
     private RunningAppState mRunningAppState;
     private final int mRunningAppIndicatorColor;
     private final int mMinimizedAppIndicatorColor;
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private int mLineIndicatorColor;
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private float mLineIndicatorScale;
+    private int mLineIndicatorAnimStartDelay;
+    private Animator mLineIndicatorAnim;
+
+    private final String mMinimizedStateDescription;
+    private final String mRunningStateDescription;
 
     /**
      * Various options for the running state of an app.
@@ -240,6 +288,9 @@
         super(context, attrs, defStyle);
         mActivity = ActivityContext.lookupContext(context);
         FastBitmapDrawable.setFlagHoverEnabled(enableCursorHoverStates());
+        mMinimizedStateDescription = getContext().getString(
+                R.string.app_minimized_state_description);
+        mRunningStateDescription = getContext().getString(R.string.app_running_state_description);
 
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.BubbleTextView, defStyle, 0);
@@ -285,8 +336,6 @@
 
         mRunningAppIndicatorWidth =
                 getResources().getDimensionPixelSize(R.dimen.taskbar_running_app_indicator_width);
-        mMinimizedAppIndicatorWidth =
-                getResources().getDimensionPixelSize(R.dimen.taskbar_minimized_app_indicator_width);
         mRunningAppIndicatorHeight =
                 getResources().getDimensionPixelSize(R.dimen.taskbar_running_app_indicator_height);
         mRunningAppIndicatorTopMargin =
@@ -335,6 +384,11 @@
         mForceHideDot = false;
         setBackground(null);
 
+        mLineIndicatorColor = Color.TRANSPARENT;
+        mLineIndicatorScale = 0;
+        mLineIndicatorAnimStartDelay = 0;
+        cancelLineIndicatorAnim();
+
         setTag(null);
         if (mIconLoadRequest != null) {
             mIconLoadRequest.cancel();
@@ -427,9 +481,63 @@
 
     /** Updates whether the app this view represents is currently running. */
     @UiThread
-    public void updateRunningState(RunningAppState runningAppState) {
+    public void updateRunningState(RunningAppState runningAppState, boolean animate) {
+        if (runningAppState.equals(mRunningAppState)) {
+            return;
+        }
         mRunningAppState = runningAppState;
-        invalidate();
+        cancelLineIndicatorAnim();
+
+        int color = switch (mRunningAppState) {
+            case NOT_RUNNING -> Color.TRANSPARENT;
+            case RUNNING -> mRunningAppIndicatorColor;
+            case MINIMIZED -> mMinimizedAppIndicatorColor;
+        };
+        float scale = switch (mRunningAppState) {
+            case NOT_RUNNING -> 0;
+            case RUNNING -> 1;
+            case MINIMIZED -> MINIMIZED_APP_INDICATOR_SCALE;
+        };
+
+        if (!animate) {
+            mLineIndicatorColor = color;
+            mLineIndicatorScale = scale;
+            invalidate();
+            return;
+        }
+
+        AnimatorSet lineIndicatorAnim  = new AnimatorSet();
+        mLineIndicatorAnim = lineIndicatorAnim;
+        Animator colorAnimator = ObjectAnimator.ofArgb(this, LINE_INDICATOR_COLOR_PROPERTY, color);
+        Animator scaleAnimator = ObjectAnimator.ofFloat(this, LINE_INDICATOR_SCALE_PROPERTY, scale);
+        lineIndicatorAnim.playTogether(colorAnimator, scaleAnimator);
+
+        lineIndicatorAnim.setInterpolator(EMPHASIZED);
+        lineIndicatorAnim.setStartDelay(mLineIndicatorAnimStartDelay);
+        lineIndicatorAnim.setDuration(LINE_INDICATOR_ANIM_DURATION).start();
+    }
+
+    public void setLineIndicatorAnimStartDelay(int lineIndicatorAnimStartDelay) {
+        mLineIndicatorAnimStartDelay = lineIndicatorAnimStartDelay;
+    }
+
+    private void cancelLineIndicatorAnim() {
+        if (mLineIndicatorAnim != null) {
+            mLineIndicatorAnim.cancel();
+        }
+    }
+
+    /**
+     * Returns state description of this icon.
+     */
+    public String getIconStateDescription() {
+        if (mRunningAppState == MINIMIZED) {
+            return mMinimizedStateDescription;
+        } else if (mRunningAppState == RUNNING) {
+            return mRunningStateDescription;
+        } else {
+            return "";
+        }
     }
 
     protected void setItemInfo(ItemInfoWithIcon itemInfo) {
@@ -483,7 +591,9 @@
     }
 
     private void setNonPendingIcon(ItemInfoWithIcon info) {
-        int flags = shouldUseTheme() ? FLAG_THEMED : 0;
+        // Set nonPendingIcon acts as a restart which should refresh the flag state when applicable.
+        int flags = Objects.equals(info.getTargetPackage(), PRIVATE_SPACE_PACKAGE)
+                ? info.bitmap.creationFlags : shouldUseTheme() ? FLAG_THEMED : 0;
         // Remove badge on icons smaller than 48dp.
         if (mHideBadge || mDisplay == DISPLAY_SEARCH_RESULT_SMALL) {
             flags |= FLAG_NO_BADGE;
@@ -768,19 +878,18 @@
 
     /** Draws a line under the app icon if this is representing a running app in Desktop Mode. */
     protected void drawRunningAppIndicatorIfNecessary(Canvas canvas) {
-        if (mRunningAppState == RunningAppState.NOT_RUNNING || mDisplay != DISPLAY_TASKBAR) {
+        if (mDisplay != DISPLAY_TASKBAR
+                || mLineIndicatorScale == 0
+                || mLineIndicatorColor == Color.TRANSPARENT) {
             return;
         }
         getIconBounds(mRunningAppIconBounds);
         Utilities.scaleRectAboutCenter(mRunningAppIconBounds, ICON_VISIBLE_AREA_FACTOR);
 
-        final boolean isMinimized = mRunningAppState == RunningAppState.MINIMIZED;
         final int indicatorTop = mRunningAppIconBounds.bottom + mRunningAppIndicatorTopMargin;
-        final int indicatorWidth =
-                isMinimized ? mMinimizedAppIndicatorWidth : mRunningAppIndicatorWidth;
+        final float indicatorWidth = mRunningAppIndicatorWidth * mLineIndicatorScale;
         final float cornerRadius = mRunningAppIndicatorHeight / 2f;
-        mRunningAppIndicatorPaint.setColor(
-                isMinimized ? mMinimizedAppIndicatorColor : mRunningAppIndicatorColor);
+        mRunningAppIndicatorPaint.setColor(mLineIndicatorColor);
 
         canvas.drawRoundRect(
                 mRunningAppIconBounds.centerX() - indicatorWidth / 2f,
@@ -961,11 +1070,14 @@
 
     @Override
     public void setTextColor(ColorStateList colors) {
-        mTextColor = (shouldDrawAppContrastTile() && !TextUtils.isEmpty(getText()))
-                ? PillColorProvider.getInstance(
-                getContext()).getAppTitleTextPaint().getColor()
-                : colors.getDefaultColor();
-        mTextColorStateList = colors;
+        if (shouldDrawAppContrastTile()) {
+            mTextColor = PillColorProvider.getInstance(
+                    getContext()).getAppTitleTextPaint().getColor();
+        } else {
+            mTextColor = colors.getDefaultColor();
+            mTextColorStateList = colors;
+        }
+
         if (Float.compare(mTextAlpha, 1) == 0) {
             super.setTextColor(colors);
         } else {
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index 2d99510..7f0c7b5 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.graphics.Rect;
+import android.view.View;
 
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
 import com.android.launcher3.dragndrop.DragOptions;
@@ -145,4 +146,9 @@
 
     // These methods are implemented in Views
     void getHitRectRelativeToDragLayer(Rect outRect);
+
+    /** Returns the drop target view. By default, the implementor class is cast to the view. */
+    default View getDropView() {
+        return (View) this;
+    }
 }
diff --git a/src/com/android/launcher3/DropTargetHandler.kt b/src/com/android/launcher3/DropTargetHandler.kt
index 0cc7fc7..3c162a2 100644
--- a/src/com/android/launcher3/DropTargetHandler.kt
+++ b/src/com/android/launcher3/DropTargetHandler.kt
@@ -72,9 +72,10 @@
             AbstractFloatingView.TYPE_WIDGET_RESIZE_FRAME,
         )
         var pageItem: ItemInfo = item
-        if (item.container <= 0) {
-            val v = mLauncher.workspace.getHomescreenIconByItemId(item.container)
-            v?.let { pageItem = v.tag as ItemInfo }
+        if (item.container >= 0) {
+            mLauncher.workspace.getViewByItemId(item.container)?.let {
+                pageItem = it.tag as ItemInfo
+            }
         }
         val pageIds =
             if (pageItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP)
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 3edba99..289f175 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -71,9 +71,11 @@
 import static com.android.launcher3.LauncherState.NO_SCALE;
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
 import static com.android.launcher3.Utilities.postAsyncCallback;
+import static com.android.launcher3.Workspace.mapOverCellLayouts;
 import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
 import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
 import static com.android.launcher3.config.FeatureFlags.MULTI_SELECT_EDIT_MODE;
+import static com.android.launcher3.icons.BitmapRenderer.createHardwareBitmap;
 import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.HIDE;
 import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.SHOW;
 import static com.android.launcher3.logging.StatsLogManager.EventEnum;
@@ -125,7 +127,6 @@
 import android.content.res.Configuration;
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.RectF;
@@ -163,7 +164,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
-import androidx.annotation.StringRes;
 import androidx.annotation.UiThread;
 import androidx.annotation.VisibleForTesting;
 import androidx.core.os.BuildCompat;
@@ -172,7 +172,6 @@
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.allapps.ActivityAllAppsContainerView;
-import com.android.launcher3.allapps.AllAppsRecyclerView;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.allapps.DiscoveryBounce;
 import com.android.launcher3.anim.AnimationSuccessListener;
@@ -185,7 +184,6 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.debug.TestEventEmitter;
 import com.android.launcher3.debug.TestEventEmitter.TestEvent;
-import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragView;
@@ -237,6 +235,7 @@
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ItemInflater;
 import com.android.launcher3.util.KeyboardShortcutsDelegate;
+import com.android.launcher3.util.LauncherBindableItemsContainer;
 import com.android.launcher3.util.LockedUserState;
 import com.android.launcher3.util.MSDLPlayerWrapper;
 import com.android.launcher3.util.PackageUserKey;
@@ -253,7 +252,6 @@
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.views.ActivityContext;
-import com.android.launcher3.views.ComposeInitializer;
 import com.android.launcher3.views.FloatingIconView;
 import com.android.launcher3.views.FloatingSurfaceView;
 import com.android.launcher3.views.OptionsPopupView;
@@ -544,8 +542,8 @@
         mItemInflater = new ItemInflater<>(this, mAppWidgetHolder, getItemOnClickListener(),
                 mFocusHandler, new CellLayout(mWorkspace.getContext(), mWorkspace));
 
-        mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
-        mWidgetPickerDataProvider = new WidgetPickerDataProvider();
+        mPopupDataProvider = new PopupDataProvider(this);
+        mWidgetPickerDataProvider = new WidgetPickerDataProvider(this);
         PillColorProvider.getInstance(mWorkspace.getContext()).registerObserver();
 
         boolean internalStateHandled = ACTIVITY_TRACKER.handleCreate(this);
@@ -579,7 +577,6 @@
         setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
 
         setContentView(getRootView());
-        ComposeInitializer.initCompose(this);
 
         if (mOnInitialBindListener != null) {
             getRootView().getViewTreeObserver().addOnPreDrawListener(mOnInitialBindListener);
@@ -1485,7 +1482,7 @@
 
     @Override
     public @Nullable FolderIcon findFolderIcon(final int folderIconId) {
-        return (FolderIcon) mWorkspace.getHomescreenIconByItemId(folderIconId);
+        return (FolderIcon) mWorkspace.getViewByItemId(folderIconId);
     }
 
     /**
@@ -1598,11 +1595,6 @@
 
     private final ScreenOnListener mScreenOnListener = this::onScreenOnChanged;
 
-    private void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
-        mWorkspace.updateNotificationDots(updatedDots);
-        mAppsView.getAppsStore().updateNotificationDots(updatedDots);
-    }
-
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
@@ -1674,7 +1666,7 @@
         } else if (Intent.ACTION_ALL_APPS.equals(intent.getAction())) {
             showAllAppsFromIntent(alreadyOnHome);
         } else if (INTENT_ACTION_ALL_APPS_TOGGLE.equals(intent.getAction())) {
-            toggleAllAppsSearch(alreadyOnHome);
+            toggleAllApps(alreadyOnHome, true);
         } else if (Intent.ACTION_SHOW_WORK_APPS.equals(intent.getAction())) {
             showAllAppsWithSelectedTabFromIntent(alreadyOnHome,
                     ActivityAllAppsContainerView.AdapterHolder.WORK);
@@ -1688,12 +1680,15 @@
         // Overridden
     }
 
-    /** Toggles Launcher All Apps with keyboard ready for search. */
-    public void toggleAllAppsSearch() {
-        toggleAllAppsSearch(/* alreadyOnHome= */ true);
+    /**
+     * Toggles Launcher All Apps.
+     * @param focusSearch Indicates whether to make All Apps keyboard ready for search.
+     */
+    public void toggleAllApps(boolean focusSearch) {
+        toggleAllApps(/* alreadyOnHome= */ true, focusSearch);
     }
 
-    protected void toggleAllAppsSearch(boolean alreadyOnHome) {
+    private void toggleAllApps(boolean alreadyOnHome, boolean focusSearch) {
         if (getStateManager().isInStableState(ALL_APPS)) {
             getStateManager().goToState(NORMAL, alreadyOnHome);
         } else {
@@ -1705,7 +1700,8 @@
                     new AnimationSuccessListener() {
                         @Override
                         public void onAnimationSuccess(Animator animator) {
-                            if (mAppsView.getSearchUiManager().getEditText() != null) {
+                            if (focusSearch
+                                    && mAppsView.getSearchUiManager().getEditText() != null) {
                                 mAppsView.getSearchUiManager().getEditText().requestFocus();
                             }
                         }
@@ -1804,6 +1800,7 @@
 
         mAppWidgetHolder.stopListening();
         mAppWidgetHolder.destroy();
+        mWidgetPickerDataProvider.destroy();
 
         TextKeyListener.getInstance().release();
         mModelCallbacks.clearPendingBinds();
@@ -1884,7 +1881,8 @@
             if (dropView != null && dropView.containsAppWidgetHostView()) {
                 // Extracting Bitmap from dropView instead of its content view produces the correct
                 // bitmap.
-                widgetPreviewBitmap = getBitmapFromView(dropView);
+                widgetPreviewBitmap = createHardwareBitmap(
+                        dropView.getWidth(), dropView.getHeight(), dropView::draw);
             }
         }
 
@@ -2054,7 +2052,7 @@
     public boolean removeItem(View v, final ItemInfo itemInfo, boolean deleteFromDb,
             @Nullable final String reason) {
         if (itemInfo instanceof WorkspaceItemInfo) {
-            View collectionIcon = mWorkspace.getHomescreenIconByItemId(itemInfo.container);
+            View collectionIcon = mWorkspace.getViewByItemId(itemInfo.container);
             if (collectionIcon instanceof FolderIcon) {
                 // Remove the shortcut from the folder before removing it from launcher
                 ((FolderInfo) collectionIcon.getTag()).remove((WorkspaceItemInfo) itemInfo, true);
@@ -2433,52 +2431,25 @@
     }
 
     /**
-     * Similar to {@link #getFirstMatch} but optimized to finding a suitable view for the app close
-     * animation.
+     * Finds the first view on homescreen matching the provided parameters, optimized to finding a
+     * suitable view for the app close animation.
      *
      * @param svi The StableViewInfo of the preferred item to match to if it exists or null
      * @param packageName The package name of the app to match.
      * @param user The user of the app to match.
-     * @param supportsAllAppsState If true and we are in All Apps state, looks for view in All Apps.
-     *                             Else we only looks on the workspace.
      */
-    public @Nullable View getFirstMatchForAppClose(
-            @Nullable StableViewInfo svi, String packageName,
-            UserHandle user, boolean supportsAllAppsState) {
+    public @Nullable View getFirstHomeElementForAppClose(
+            @Nullable StableViewInfo svi, String packageName, UserHandle user) {
         final Predicate<ItemInfo> preferredItem = svi == null ? i -> false : svi::matches;
-        final Predicate<ItemInfo> packageAndUserAndApp = info ->
-                info != null
-                        && info.itemType == ITEM_TYPE_APPLICATION
-                        && info.user.equals(user)
-                        && info.getTargetComponent() != null
-                        && TextUtils.equals(info.getTargetComponent().getPackageName(),
-                        packageName);
-
-        if (supportsAllAppsState && isInState(LauncherState.ALL_APPS)) {
-            AllAppsRecyclerView activeRecyclerView = mAppsView.getActiveRecyclerView();
-            View v = getFirstMatch(Collections.singletonList(activeRecyclerView),
-                    preferredItem, packageAndUserAndApp);
-
-            if (v != null && activeRecyclerView.computeVerticalScrollOffset() > 0) {
-                RectF locationBounds = new RectF();
-                FloatingIconView.getLocationBoundsForView(this, v, false, locationBounds,
-                        new Rect());
-                if (locationBounds.top < mAppsView.getHeaderBottom()) {
-                    // Icon is covered by scrim, return null to play fallback animation.
-                    return null;
-                }
-            }
-
-            return v;
-        }
+        final Predicate<ItemInfo> packageAndUserAndApp = info -> info != null
+                && info.itemType == ITEM_TYPE_APPLICATION
+                && info.user.equals(user)
+                && TextUtils.equals(info.getTargetPackage(), packageName);
 
         // Look for the item inside the folder at the current page
         Folder folder = Folder.getOpen(this);
         if (folder != null) {
-            View v = getFirstMatch(Collections.singletonList(
-                    folder.getContent().getCurrentCellLayout().getShortcutsAndWidgets()),
-                    preferredItem,
-                    packageAndUserAndApp);
+            View v = folder.getFirstMatch(preferredItem, packageAndUserAndApp);
             if (v == null) {
                 folder.close(isStarted() && !isForceInvisible());
             } else {
@@ -2486,72 +2457,19 @@
             }
         }
 
-        List<ViewGroup> containers = new ArrayList<>(mWorkspace.getPanelCount() + 1);
-        containers.add(mWorkspace.getHotseat().getShortcutsAndWidgets());
-        mWorkspace.forEachVisiblePage(page
-                -> containers.add(((CellLayout) page).getShortcutsAndWidgets()));
+        List<CellLayout> containers = new ArrayList<>(mWorkspace.getPanelCount() + 1);
+        containers.add(mWorkspace.getHotseat());
+        mWorkspace.forEachVisiblePage(page -> containers.add((CellLayout) page));
+        CellLayout[] containerArray = containers.toArray(new CellLayout[0]);
+        LauncherBindableItemsContainer visibleContainer =
+                op -> mapOverCellLayouts(containerArray, op);
 
         // Order: Preferred item by itself or in folder, then by matching package/user
-        return getFirstMatch(containers, preferredItem, forFolderMatch(preferredItem),
+        return visibleContainer.getFirstMatch(
+                preferredItem, forFolderMatch(preferredItem),
                 packageAndUserAndApp, forFolderMatch(packageAndUserAndApp));
     }
 
-    /**
-     * Finds the first view matching the ordered operators across the given viewgroups in order.
-     * @param containers List of ViewGroups to scan, in order of preference.
-     * @param operators List of operators, in order starting from best matching operator.
-     */
-    @Nullable
-    private static View getFirstMatch(Iterable<ViewGroup> containers,
-            final Predicate<ItemInfo>... operators) {
-        for (Predicate<ItemInfo> operator : operators) {
-            for (ViewGroup container : containers) {
-                View match = mapOverViewGroup(container, operator);
-                if (match != null) {
-                    return match;
-                }
-            }
-        }
-        return null;
-    }
-
-    /** Convert a {@link View} to {@link Bitmap}. */
-    private static Bitmap getBitmapFromView(@Nullable View view) {
-        if (view == null) {
-            return null;
-        }
-        Bitmap returnedBitmap =
-                Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(returnedBitmap);
-        view.draw(canvas);
-        return returnedBitmap;
-    }
-
-    /**
-     * Returns the first view matching the operator in the given ViewGroups, or null if none.
-     * Forward iteration matters.
-     */
-    @Nullable
-    private static View mapOverViewGroup(ViewGroup container, Predicate<ItemInfo> op) {
-        final int itemCount = container.getChildCount();
-        for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
-            View item = container.getChildAt(itemIdx);
-            if (item.getVisibility() != View.VISIBLE) {
-                continue;
-            }
-            if (item instanceof ViewGroup viewGroup) {
-                View view = mapOverViewGroup(viewGroup, op);
-                if (view != null) {
-                    return view;
-                }
-            }
-            if (item.getTag() instanceof ItemInfo itemInfo && op.test(itemInfo)) {
-                return item;
-            }
-        }
-        return null;
-    }
-
     private ValueAnimator createNewAppBounceAnimation(View v, int i) {
         ValueAnimator bounceAnim = new PropertyListBuilder().alpha(1).scale(1).build(v)
                 .setDuration(ItemInstallQueue.NEW_SHORTCUT_BOUNCE_DURATION);
@@ -2560,10 +2478,6 @@
         return bounceAnim;
     }
 
-    private void announceForAccessibility(@StringRes int stringResId) {
-        getDragLayer().announceForAccessibility(getString(stringResId));
-    }
-
     /**
      * Informs us that the overlay (-1 screen, typically), has either become visible or invisible.
      */
@@ -2630,9 +2544,8 @@
      * See {@code LauncherBindingDelegate}
      */
     @Override
-    public void bindAllWidgets(@NonNull final List<WidgetsListBaseEntry> allWidgets,
-            @NonNull final List<WidgetsListBaseEntry> defaultWidgets) {
-        mModelCallbacks.bindAllWidgets(allWidgets, defaultWidgets);
+    public void bindAllWidgets(@NonNull final List<WidgetsListBaseEntry> allWidgets) {
+        mModelCallbacks.bindAllWidgets(allWidgets);
     }
 
     @Override
@@ -2948,11 +2861,6 @@
         return mModelCallbacks.getWorkspaceLoading();
     }
 
-    @Override
-    public boolean isBindingItems() {
-        return isWorkspaceLoading();
-    }
-
     /**
      * Returns true if a touch interaction is in progress
      */
@@ -3027,11 +2935,6 @@
         return mWidgetPickerDataProvider;
     }
 
-    @Override
-    public DotInfo getDotInfoForItem(ItemInfo info) {
-        return mPopupDataProvider.getDotInfoForItem(info);
-    }
-
     @NonNull
     public LauncherOverlayManager getOverlayManager() {
         return mOverlayManager;
@@ -3046,6 +2949,12 @@
         return mDragLayer;
     }
 
+    @NonNull
+    @Override
+    public LauncherBindableItemsContainer getContent() {
+        return mWorkspace;
+    }
+
     @Override
     public ActivityAllAppsContainerView<Launcher> getAppsView() {
         return mAppsView;
diff --git a/src/com/android/launcher3/LauncherModel.kt b/src/com/android/launcher3/LauncherModel.kt
index 892a218..add0ad8 100644
--- a/src/com/android/launcher3/LauncherModel.kt
+++ b/src/com/android/launcher3/LauncherModel.kt
@@ -43,7 +43,6 @@
 import com.android.launcher3.model.ReloadStringCacheTask
 import com.android.launcher3.model.ShortcutsChangedTask
 import com.android.launcher3.model.UserLockStateChangedTask
-import com.android.launcher3.model.WidgetsFilterDataProvider
 import com.android.launcher3.model.data.ItemInfo
 import com.android.launcher3.model.data.WorkspaceItemInfo
 import com.android.launcher3.pm.UserCache
@@ -82,8 +81,6 @@
     val modelDelegate: ModelDelegate,
 ) {
 
-    private val widgetsFilterDataProvider = WidgetsFilterDataProvider.newInstance(context)
-
     private val mCallbacksList = ArrayList<BgDataModel.Callbacks>(1)
 
     // < only access in worker thread >
@@ -151,11 +148,6 @@
         owner: BgDataModel.Callbacks?,
     ) = ModelWriter(context, this, mBgDataModel, verifyChanges, cellPosMapper, owner)
 
-    /** Returns the [WidgetsFilterDataProvider] that manages widget filters. */
-    fun getWidgetsFilterDataProvider(): WidgetsFilterDataProvider {
-        return widgetsFilterDataProvider
-    }
-
     /** Called when the icon for an app changes, outside of package event */
     @WorkerThread
     fun onAppIconChanged(packageName: String, user: UserHandle) {
@@ -176,10 +168,7 @@
     /** Called when the model is destroyed */
     fun destroy() {
         mModelDestroyed = true
-        MODEL_EXECUTOR.execute {
-            modelDelegate.destroy()
-            widgetsFilterDataProvider.destroy()
-        }
+        MODEL_EXECUTOR.execute { modelDelegate.destroy() }
     }
 
     fun reloadStringCache() {
@@ -335,7 +324,6 @@
                             mBgDataModel,
                             this.modelDelegate,
                             launcherBinder,
-                            widgetsFilterDataProvider,
                         )
                     mLoaderTask = task
 
@@ -442,14 +430,6 @@
         }
     }
 
-    /** Called when the widget filters are refreshed and available to bind to the model. */
-    fun onWidgetFiltersLoaded() {
-        enqueueModelUpdateTask { taskController, dataModel, _ ->
-            dataModel.widgetsModel.updateWidgetFilters(widgetsFilterDataProvider)
-            taskController.bindUpdatedWidgets(dataModel)
-        }
-    }
-
     fun enqueueModelUpdateTask(task: ModelUpdateTask) {
         if (mModelDestroyed) {
             return
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index 2a5cd63..7a04b0f 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -75,18 +75,18 @@
     @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
     private fun <T> getInner(item: Item, default: T): T {
         val sp = getSharedPrefs(item)
-
-        return when (item.type) {
-            String::class.java -> sp.getString(item.sharedPrefKey, default as? String)
-            Boolean::class.java,
-            java.lang.Boolean::class.java -> sp.getBoolean(item.sharedPrefKey, default as Boolean)
-            Int::class.java,
-            java.lang.Integer::class.java -> sp.getInt(item.sharedPrefKey, default as Int)
-            Float::class.java,
-            java.lang.Float::class.java -> sp.getFloat(item.sharedPrefKey, default as Float)
-            Long::class.java,
-            java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, default as Long)
-            Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as? Set<String>)
+        return when {
+            item.type == String::class.java -> sp.getString(item.sharedPrefKey, default as? String)
+            item.type == Boolean::class.java || item.type == java.lang.Boolean::class.java ->
+                sp.getBoolean(item.sharedPrefKey, default as Boolean)
+            item.type == Int::class.java || item.type == java.lang.Integer::class.java ->
+                sp.getInt(item.sharedPrefKey, default as Int)
+            item.type == Float::class.java || item.type == java.lang.Float::class.java ->
+                sp.getFloat(item.sharedPrefKey, default as Float)
+            item.type == Long::class.java || item.type == java.lang.Long::class.java ->
+                sp.getLong(item.sharedPrefKey, default as Long)
+            Set::class.java.isAssignableFrom(item.type) ->
+                sp.getStringSet(item.sharedPrefKey, default as? Set<String>)
             else ->
                 throw IllegalArgumentException(
                     "item type: ${item.type}" + " is not compatible with sharedPref methods"
@@ -147,17 +147,18 @@
         item: Item,
         value: Any?,
     ): SharedPreferences.Editor =
-        when (item.type) {
-            String::class.java -> putString(item.sharedPrefKey, value as? String)
-            Boolean::class.java,
-            java.lang.Boolean::class.java -> putBoolean(item.sharedPrefKey, value as Boolean)
-            Int::class.java,
-            java.lang.Integer::class.java -> putInt(item.sharedPrefKey, value as Int)
-            Float::class.java,
-            java.lang.Float::class.java -> putFloat(item.sharedPrefKey, value as Float)
-            Long::class.java,
-            java.lang.Long::class.java -> putLong(item.sharedPrefKey, value as Long)
-            Set::class.java -> putStringSet(item.sharedPrefKey, value as? Set<String>)
+        when {
+            item.type == String::class.java -> putString(item.sharedPrefKey, value as? String)
+            item.type == Boolean::class.java || item.type == java.lang.Boolean::class.java ->
+                putBoolean(item.sharedPrefKey, value as Boolean)
+            item.type == Int::class.java || item.type == java.lang.Integer::class.java ->
+                putInt(item.sharedPrefKey, value as Int)
+            item.type == Float::class.java || item.type == java.lang.Float::class.java ->
+                putFloat(item.sharedPrefKey, value as Float)
+            item.type == Long::class.java || item.type == java.lang.Long::class.java ->
+                putLong(item.sharedPrefKey, value as Long)
+            Set::class.java.isAssignableFrom(item.type) ->
+                putStringSet(item.sharedPrefKey, value as? Set<String>)
             else ->
                 throw IllegalArgumentException(
                     "item type: ${item.type} is not compatible with sharedPref methods"
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 8c6555e..78ad04b 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -70,6 +70,7 @@
     public static final int WORKSPACE_PAGE_INDICATOR = 1 << 5;
     public static final int SPLIT_PLACHOLDER_VIEW = 1 << 6;
     public static final int FLOATING_SEARCH_BAR = 1 << 7;
+    public static final int ADD_DESK_BUTTON = 1 << 8;
 
     // Flag indicating workspace has multiple pages visible.
     public static final int FLAG_MULTI_PAGE = BaseState.getFlag(0);
diff --git a/src/com/android/launcher3/ModelCallbacks.kt b/src/com/android/launcher3/ModelCallbacks.kt
index d01f35d..32b47d0 100644
--- a/src/com/android/launcher3/ModelCallbacks.kt
+++ b/src/com/android/launcher3/ModelCallbacks.kt
@@ -234,11 +234,8 @@
         PopupContainerWithArrow.dismissInvalidPopup(launcher)
     }
 
-    override fun bindAllWidgets(
-        allWidgets: List<WidgetsListBaseEntry>,
-        defaultWidgets: List<WidgetsListBaseEntry>,
-    ) {
-        launcher.widgetPickerDataProvider.setWidgets(allWidgets, defaultWidgets)
+    override fun bindAllWidgets(allWidgets: List<WidgetsListBaseEntry>) {
+        launcher.widgetPickerDataProvider.setWidgets(allWidgets)
     }
 
     /** Returns the ids of the workspaces to bind. */
diff --git a/src/com/android/launcher3/PillColorPorovider.kt b/src/com/android/launcher3/PillColorPorovider.kt
index 347c5d6..e7f37bd 100644
--- a/src/com/android/launcher3/PillColorPorovider.kt
+++ b/src/com/android/launcher3/PillColorPorovider.kt
@@ -58,13 +58,8 @@
     }
 
     fun setup() {
-        appTitlePillPaint.color =
-            context.resources.getColor(
-                R.color.material_color_surface_container_lowest,
-                context.theme,
-            )
-        appTitleTextPaint.color =
-            context.resources.getColor(R.color.material_color_on_surface, context.theme)
+        appTitlePillPaint.color = context.getColor(R.color.materialColorSurfaceContainer)
+        appTitleTextPaint.color = context.getColor(R.color.materialColorOnSurface)
         isMatchaEnabledInternal = Settings.Secure.getInt(context.contentResolver, MATCHA_SETTING, 0)
         isMatchaEnabled = isMatchaEnabledInternal != 0
     }
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index cb3a0bc..9a9bc1d 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -18,6 +18,7 @@
 
 import static com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN;
 import static com.android.launcher3.Flags.enableSmartspaceAsAWidget;
+import static com.android.launcher3.graphics.ShapeDelegate.DEFAULT_PATH_SIZE;
 import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
@@ -39,6 +40,7 @@
 import android.graphics.LightingColorFilter;
 import android.graphics.Matrix;
 import android.graphics.Paint;
+import android.graphics.Path;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -632,7 +634,7 @@
 
         Drawable badge = null;
         if ((info instanceof ItemInfoWithIcon iiwi) && !iiwi.getMatchingLookupFlag().useLowRes()) {
-            badge = iiwi.bitmap.getBadgeDrawable(context, useTheme);
+            badge = iiwi.bitmap.getBadgeDrawable(context, useTheme, getIconShapeOrNull(context));
         }
 
         if (info instanceof PendingAddShortcutInfo) {
@@ -659,8 +661,11 @@
                 // Only fetch badge if the icon is on workspace
                 if (info.id != ItemInfo.NO_ID && badge == null) {
                     badge = appState.getIconCache().getShortcutInfoBadge(si).newIcon(
-                            context, ThemeManager.INSTANCE.get(context).isIconThemeEnabled()
-                                    ? FLAG_THEMED : 0);
+                            context,
+                            ThemeManager.INSTANCE.get(context).isIconThemeEnabled()
+                                    ? FLAG_THEMED : 0,
+                            getIconShapeOrNull(context)
+                    );
                 }
             }
         } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
@@ -706,10 +711,11 @@
 
         if (badge == null) {
             badge = BitmapInfo.LOW_RES_INFO.withFlags(
-                            UserCache.INSTANCE.get(context)
-                                    .getUserInfo(info.user)
-                                    .applyBitmapInfoFlags(FlagOp.NO_OP))
-                    .getBadgeDrawable(context, useTheme);
+                    UserCache.INSTANCE.get(context)
+                            .getUserInfo(info.user)
+                            .applyBitmapInfoFlags(FlagOp.NO_OP)
+                    )
+                    .getBadgeDrawable(context, useTheme, getIconShapeOrNull(context));
             if (badge == null) {
                 badge = new ColorDrawable(Color.TRANSPARENT);
             }
@@ -939,4 +945,18 @@
         }
         return null;
     }
+
+    /**
+     * Returns current icon shape to use for badges if flag is on, otherwise null.
+     */
+    @Nullable
+    public static Path getIconShapeOrNull(Context context) {
+        if (Flags.enableLauncherIconShapes()) {
+            return ThemeManager.INSTANCE.get(context)
+                    .getIconShape()
+                    .getPath(DEFAULT_PATH_SIZE);
+        } else {
+            return null;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 94ff441..6ed183a 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -67,6 +67,7 @@
 import android.widget.FrameLayout;
 import android.widget.Toast;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.core.view.ViewCompat;
@@ -83,7 +84,6 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.debug.TestEventEmitter;
 import com.android.launcher3.debug.TestEventEmitter.TestEvent;
-import com.android.launcher3.dot.FolderDotInfo;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
@@ -119,7 +119,6 @@
 import com.android.launcher3.util.LauncherBindableItemsContainer;
 import com.android.launcher3.util.MSDLPlayerWrapper;
 import com.android.launcher3.util.OverlayEdgeEffect;
-import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.WallpaperOffsetInterpolator;
@@ -893,6 +892,9 @@
                 mScreenOrder.removeValue(extraEmptyPageId);
             });
 
+            // Since we removed some screens, before moving to next page, update the state
+            // description with correct page numbers.
+            updateAccessibilityViewPageDescription();
             setCurrentPage(getNextPage());
 
             // Update the page indicator to reflect the removed page.
@@ -1118,6 +1120,9 @@
         if (pageShift >= 0) {
             setCurrentPage(currentPage - pageShift);
         }
+
+        // Now that we have removed some pages, ensure state description is up to date.
+        updateAccessibilityViewPageDescription();
     }
 
     /**
@@ -3159,7 +3164,7 @@
                         + "Workspace#onDropCompleted. Please file a bug. ");
             }
         }
-        View cell = getHomescreenIconByItemId(d.originalDragInfo.id);
+        View cell = getViewByItemId(d.originalDragInfo.id);
         if (d.cancelled && cell != null) {
             cell.setVisibility(VISIBLE);
         }
@@ -3308,29 +3313,9 @@
         return layouts;
     }
 
-    public View getHomescreenIconByItemId(final int id) {
-        return getFirstMatch((info, v) -> info != null && info.id == id);
-    }
-
     public LauncherAppWidgetHostView getWidgetForAppWidgetId(final int appWidgetId) {
-        return (LauncherAppWidgetHostView) getFirstMatch((info, v) ->
-                (info instanceof LauncherAppWidgetInfo) &&
-                        ((LauncherAppWidgetInfo) info).appWidgetId == appWidgetId);
-    }
-
-    public View getFirstMatch(final ItemOperator operator) {
-        final View[] value = new View[1];
-        mapOverItems(new ItemOperator() {
-            @Override
-            public boolean evaluate(ItemInfo info, View v) {
-                if (operator.evaluate(info, v)) {
-                    value[0] = v;
-                    return true;
-                }
-                return false;
-            }
-        });
-        return value[0];
+        return (LauncherAppWidgetHostView) mapOverItems((info, v) ->
+                (info instanceof LauncherAppWidgetInfo lawi) && lawi.appWidgetId == appWidgetId);
     }
 
     void clearDropTargets() {
@@ -3389,68 +3374,31 @@
     }
 
     @Override
-    public void mapOverItems(ItemOperator op) {
-        for (CellLayout layout : getWorkspaceAndHotseatCellLayouts()) {
-            if (mapOverCellLayout(layout, op) != null) {
-                return;
-            }
-        }
+    public View mapOverItems(@NonNull ItemOperator op) {
+        return mapOverCellLayouts(getWorkspaceAndHotseatCellLayouts(), op);
     }
 
     /**
-     * Perform {param operator} over all the items in a given {param layout}.
-     *
-     * @return The first item that satisfies the operator or null.
+     * Perform {param op} over all the items in the provided {param layouts} until a match is found
      */
-    public View mapOverCellLayout(CellLayout layout, ItemOperator operator) {
-        // TODO(b/128460496) Potential race condition where layout is not yet loaded
-        if (layout == null) {
-            return null;
-        }
-        ShortcutAndWidgetContainer container = layout.getShortcutsAndWidgets();
-        // map over all the shortcuts on the workspace
-        final int itemCount = container.getChildCount();
-        for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
-            View item = container.getChildAt(itemIdx);
-            if (operator.evaluate((ItemInfo) item.getTag(), item)) {
-                return item;
+    public static View mapOverCellLayouts(CellLayout[] layouts, ItemOperator op) {
+        for (CellLayout layout : layouts) {
+            // TODO(b/128460496) Potential race condition where layout is not yet loaded
+            if (layout == null) continue;
+
+            ShortcutAndWidgetContainer container = layout.getShortcutsAndWidgets();
+            // map over all the shortcuts on the layout
+            final int itemCount = container.getChildCount();
+            for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
+                View item = container.getChildAt(itemIdx);
+                if (op.evaluate((ItemInfo) item.getTag(), item)) {
+                    return item;
+                }
             }
         }
         return null;
     }
 
-    public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
-        final PackageUserKey packageUserKey = new PackageUserKey(null, null);
-        Predicate<ItemInfo> matcher = info -> !packageUserKey.updateFromItemInfo(info)
-                || updatedDots.test(packageUserKey);
-
-        ItemOperator op = (info, v) -> {
-            if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView) {
-                if (matcher.test(info)) {
-                    ((BubbleTextView) v).applyDotState(info, true /* animate */);
-                }
-            } else if (info instanceof FolderInfo && v instanceof FolderIcon) {
-                FolderInfo fi = (FolderInfo) info;
-                if (fi.anyMatch(matcher)) {
-                    FolderDotInfo folderDotInfo = new FolderDotInfo();
-                    for (ItemInfo si : fi.getContents()) {
-                        folderDotInfo.addDotInfo(mLauncher.getDotInfoForItem(si));
-                    }
-                    ((FolderIcon) v).setDotInfo(folderDotInfo);
-                }
-            }
-
-            // process all the shortcuts
-            return false;
-        };
-
-        mapOverItems(op);
-        Folder folder = Folder.getOpen(mLauncher);
-        if (folder != null) {
-            folder.iterateOverItems(op);
-        }
-    }
-
     /**
      * Remove workspace icons & widget information related to items in matcher.
      *
@@ -3513,6 +3461,18 @@
     protected void announcePageForAccessibility() {
         // Talkback focuses on AccessibilityActionView by default, so we need to modify the state
         // description there in order for the change in page scroll to be announced.
+        updateAccessibilityViewPageDescription();
+    }
+
+    /**
+     * Updates the state description that is set on the accessibility actions view for the
+     * workspace.
+     * <p>The updated value is called out when talkback focuses on the view and is not disruptive.
+     * </p>
+     */
+    protected void updateAccessibilityViewPageDescription() {
+        // Set the state description on accessibility action view so that when it is focused,
+        // talkback describes the correct state of home screen pages.
         ViewCompat.setStateDescription(mLauncher.getAccessibilityActionView(),
                 getCurrentPageDescription());
     }
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 709b52a..870c891 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -15,10 +15,14 @@
  */
 package com.android.launcher3.allapps;
 
+import static android.multiuser.Flags.enableMovingContentIntoPrivateSpace;
+
 import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_BOTTOM_VIEW_TO_SCROLL_TO;
+import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_MASK_PRIVATE_SPACE_HEADER;
 import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_LEFT;
 import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_RIGHT;
 import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_NOTHING;
+import static com.android.launcher3.icons.BitmapInfo.FLAG_NO_BADGE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_PREINSTALLED_APPS_COUNT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_USER_INSTALLED_APPS_COUNT;
 
@@ -60,6 +64,7 @@
         AllAppsStore.OnUpdateListener {
 
     public static final String TAG = "AlphabeticalAppsList";
+    public static final String PRIVATE_SPACE_PACKAGE = "com.android.privatespace";
 
     private final WorkProfileManager mWorkProviderManager;
 
@@ -382,7 +387,7 @@
 
     private int addPrivateSpaceApps(int position) {
         // Add Install Apps Button first.
-        if (Flags.privateSpaceAppInstallerButton()) {
+        if (Flags.privateSpaceAppInstallerButton() && !enableMovingContentIntoPrivateSpace()) {
             mPrivateProviderManager.addPrivateSpaceInstallAppButton(mAdapterItems);
             position++;
         }
@@ -419,6 +424,28 @@
         // Add system apps.
         position = addAppsWithSections(split.get(false), position);
 
+        if (enableMovingContentIntoPrivateSpace()) {
+            // Look for the private space app via package and move it after header.
+            int headerIndex = -1;
+            int privateSpaceAppIndex = -1;
+            for (int i = 0; i < mAdapterItems.size(); i++) {
+                BaseAllAppsAdapter.AdapterItem currentItem = mAdapterItems.get(i);
+                if (currentItem.viewType == VIEW_TYPE_MASK_PRIVATE_SPACE_HEADER) {
+                    headerIndex = i;
+                }
+                if (currentItem.itemInfo != null && Objects.equals(
+                        currentItem.itemInfo.getTargetPackage(), PRIVATE_SPACE_PACKAGE)) {
+                    currentItem.itemInfo.bitmap.creationFlags |= FLAG_NO_BADGE;
+                    privateSpaceAppIndex = i;
+                }
+            }
+            if (headerIndex != -1 && privateSpaceAppIndex != -1) {
+                BaseAllAppsAdapter.AdapterItem movedItem =
+                        mAdapterItems.remove(privateSpaceAppIndex);
+                // Move the icon after the header.
+                mAdapterItems.add(headerIndex + 1, movedItem);
+            }
+        }
         return position;
     }
 
diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
index 150761f..06643d3 100644
--- a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
+++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
@@ -28,6 +28,7 @@
 import com.android.launcher3.graphics.ThemeManager;
 import com.android.launcher3.icons.LauncherIcons.IconPool;
 import com.android.launcher3.model.ItemInstallQueue;
+import com.android.launcher3.model.WidgetsFilterDataProvider;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.util.ApiWrapper;
@@ -84,6 +85,7 @@
     RemoveAnimationSettingsTracker getRemoveAnimationSettingsTracker();
     LauncherAppState getLauncherAppState();
     GridCustomizationsProxy getGridCustomizationsProxy();
+    WidgetsFilterDataProvider getWidgetsFilterDataProvider();
 
     /** Builder for LauncherBaseAppComponent. */
     interface Builder {
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index c50c008..613b430 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -558,7 +558,7 @@
 
             target.getHitRectRelativeToDragLayer(r);
             if (r.contains(x, y)) {
-                mActivity.getDragLayer().mapCoordInSelfToDescendant((View) target,
+                mActivity.getDragLayer().mapCoordInSelfToDescendant(target.getDropView(),
                         mCoordinatesTemp);
                 mDragObject.x = mCoordinatesTemp[0];
                 mDragObject.y = mCoordinatesTemp[1];
diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
index cf5150a..fedc118 100644
--- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -53,6 +53,20 @@
         } else if (index >= MAX_NUM_ITEMS_IN_PREVIEW) {
             // Items beyond those displayed in the preview are animated to the center
             mTmpPoint[0] = mTmpPoint[1] = mAvailableSpace / 2 - (mIconSize * totalScale) / 2;
+        } else if (Flags.enableLauncherIconShapes()) {
+            if (index == 0) {
+                // top left
+                getGridPosition(0, 0, mTmpPoint);
+            } else if (index == 1) {
+                // top right
+                getGridPosition(0, 1, mTmpPoint);
+            } else if (index == 2) {
+                // bottom left
+                getGridPosition(1, 0, mTmpPoint);
+            } else if (index == 3) {
+                // bottom right
+                getGridPosition(1, 1, mTmpPoint);
+            }
         } else {
             getPosition(index, curNumItems, mTmpPoint);
         }
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index fb48a4d..2803256 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -65,6 +65,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.core.content.res.ResourcesCompat;
@@ -100,6 +101,7 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pageindicators.PageIndicatorDots;
 import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.LauncherBindableItemsContainer;
 import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.views.ActivityContext;
@@ -122,7 +124,8 @@
  */
 public class Folder extends AbstractFloatingView implements ClipPathView, DragSource,
         View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener,
-        View.OnFocusChangeListener, DragListener, ExtendedEditText.OnBackKeyListener {
+        View.OnFocusChangeListener, DragListener, ExtendedEditText.OnBackKeyListener,
+        LauncherBindableItemsContainer {
     private static final String TAG = "Launcher.Folder";
     private static final boolean DEBUG = false;
 
@@ -261,7 +264,7 @@
     @Nullable
     private KeyboardInsetAnimationCallback mKeyboardInsetAnimationCallback;
 
-    private GradientDrawable mBackground;
+    private final @NonNull GradientDrawable mBackground;
 
     /**
      * Used to inflate the Workspace from XML.
@@ -283,6 +286,10 @@
         // click).
         setFocusableInTouchMode(true);
 
+        mBackground = (GradientDrawable) Objects.requireNonNull(
+                ResourcesCompat.getDrawable(getResources(),
+                        R.drawable.round_rect_folder, getContext().getTheme()));
+        mBackground.setCallback(this);
     }
 
     @Override
@@ -296,9 +303,6 @@
         final DeviceProfile dp = mActivityContext.getDeviceProfile();
         final int paddingLeftRight = dp.folderContentPaddingLeftRight;
 
-        mBackground = (GradientDrawable) ResourcesCompat.getDrawable(getResources(),
-                R.drawable.round_rect_folder, getContext().getTheme());
-
         mContent = findViewById(R.id.folder_content);
         mContent.setPadding(paddingLeftRight, dp.folderContentPaddingTop, paddingLeftRight, 0);
         mContent.setFolder(this);
@@ -345,6 +349,11 @@
         return true;
     }
 
+    @Override
+    protected boolean verifyDrawable(@NonNull Drawable who) {
+        return super.verifyDrawable(who) || (who == mBackground);
+    }
+
     void callBeginDragShared(View v, DragOptions options) {
         mLauncherDelegate.beginDragShared(v, this, options);
     }
@@ -1506,8 +1515,10 @@
     /**
      * Utility methods to iterate over items of the view
      */
-    public void iterateOverItems(ItemOperator op) {
-        mContent.iterateOverItems(op);
+    @Override
+    @Nullable
+    public View mapOverItems(@NonNull ItemOperator op) {
+        return mContent.iterateOverItems(op);
     }
 
     /**
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProxy.java b/src/com/android/launcher3/graphics/GridCustomizationsProxy.java
index 40863c4..70b9f46 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProxy.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProxy.java
@@ -55,6 +55,7 @@
 import com.android.systemui.shared.Flags;
 
 import java.lang.ref.WeakReference;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
@@ -130,8 +131,6 @@
     private static final int MESSAGE_ID_UPDATE_GRID = 7414;
     private static final int MESSAGE_ID_UPDATE_COLOR = 856;
 
-    private static final String DEFAULT_SHAPE_KEY = "circle";
-
     // Set of all active previews used to track duplicate memory allocations
     private final Set<PreviewLifecycleObserver> mActivePreviews =
             Collections.newSetFromMap(new ConcurrentHashMap<>());
@@ -170,18 +169,18 @@
                     MatrixCursor cursor = new MatrixCursor(new String[]{
                             KEY_SHAPE_KEY, KEY_SHAPE_TITLE, KEY_PATH, KEY_IS_DEFAULT});
                     String currentShapePath = mThemeManager.getIconState().getIconMask();
-                    Optional<IconShapeModel> selectedShape = ShapesProvider.INSTANCE.getIconShapes()
-                            .values()
-                            .stream()
-                            .filter(shape -> shape.getPathString().equals(currentShapePath))
-                            .findFirst();
+                    Optional<IconShapeModel> selectedShape = Arrays.stream(
+                            ShapesProvider.INSTANCE.getIconShapes()).filter(
+                                    shape -> shape.getPathString().equals(currentShapePath)
+                    ).findFirst();
                     // Handle default for when current shape doesn't match new shapes.
                     if (selectedShape.isEmpty()) {
-                        selectedShape = Optional.ofNullable(ShapesProvider.INSTANCE.getIconShapes()
-                                .get(DEFAULT_SHAPE_KEY));
+                        selectedShape = Optional.of(Arrays.stream(
+                                ShapesProvider.INSTANCE.getIconShapes()
+                        ).findFirst().get());
                     }
 
-                    for (IconShapeModel shape : ShapesProvider.INSTANCE.getIconShapes().values()) {
+                    for (IconShapeModel shape : ShapesProvider.INSTANCE.getIconShapes()) {
                         cursor.newRow()
                                 .add(KEY_SHAPE_KEY, shape.getKey())
                                 .add(KEY_SHAPE_TITLE, shape.getTitle())
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 740b87b..b80238c 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -38,7 +38,6 @@
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.graphics.PointF;
@@ -64,7 +63,6 @@
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.InvariantDeviceProfile;
@@ -90,11 +88,13 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.util.BaseContext;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
-import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
+import com.android.launcher3.util.SandboxContext;
+import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.WindowBounds;
 import com.android.launcher3.util.window.WindowManagerProxy;
 import com.android.launcher3.views.ActivityContext;
@@ -109,7 +109,6 @@
 import dagger.BindsInstance;
 import dagger.Component;
 
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -127,7 +126,7 @@
  *   3) Place appropriate elements like icons and first-page qsb
  *   4) Measure and draw the view on a canvas
  */
-public class LauncherPreviewRenderer extends ContextWrapper
+public class LauncherPreviewRenderer extends BaseContext
         implements ActivityContext, WorkspaceLayoutManager, LayoutInflater.Factory2 {
 
     /**
@@ -156,7 +155,6 @@
         }
     }
 
-    private final List<OnDeviceProfileChangeListener> mDpChangeListeners = new ArrayList<>();
     private final Handler mUiHandler;
     private final Context mContext;
     private final InvariantDeviceProfile mIdp;
@@ -184,7 +182,7 @@
             WallpaperColors wallpaperColorsOverride,
             @Nullable final SparseArray<Size> launcherWidgetSpanInfo) {
 
-        super(context);
+        super(context, Themes.getActivityThemeRes(context));
         mUiHandler = new Handler(Looper.getMainLooper());
         mContext = context;
         mIdp = idp;
@@ -263,6 +261,13 @@
                     : null;
         }
         mAppWidgetHost = new LauncherPreviewAppWidgetHost(context);
+
+        onViewCreated();
+    }
+
+    @Override
+    public InsettableFrameLayout getRootView() {
+        return mRootView;
     }
 
     /**
@@ -349,11 +354,6 @@
     }
 
     @Override
-    public List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners() {
-        return mDpChangeListeners;
-    }
-
-    @Override
     public Hotseat getHotseat() {
         return mHotseat;
     }
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 8af18f5..d425f03 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -88,8 +88,9 @@
     private static final String KEY_COLOR_RESOURCE_IDS = "color_resource_ids";
     private static final String KEY_COLOR_VALUES = "color_values";
     private static final String KEY_DARK_MODE = "use_dark_mode";
+    public static final String KEY_SKIP_ANIMATIONS = "skip_animations";
 
-    private Context mContext;
+    private final Context mContext;
     private SparseIntArray mPreviewColorOverride;
     private String mGridName;
     private String mShapeKey;
@@ -102,6 +103,7 @@
     private final IBinder mHostToken;
     private final int mWidth;
     private final int mHeight;
+    private final boolean mSkipAnimations;
     private final int mDisplayId;
     private final Display mDisplay;
     private final WallpaperColors mWallpaperColors;
@@ -128,6 +130,7 @@
         mHostToken = bundle.getBinder(KEY_HOST_TOKEN);
         mWidth = bundle.getInt(KEY_VIEW_WIDTH);
         mHeight = bundle.getInt(KEY_VIEW_HEIGHT);
+        mSkipAnimations = bundle.getBoolean(KEY_SKIP_ANIMATIONS, false);
         mDisplayId = bundle.getInt(KEY_DISPLAY_ID);
         mDisplay = context.getSystemService(DisplayManager.class)
                 .getDisplay(mDisplayId);
@@ -343,9 +346,7 @@
                     bgModel,
                     LauncherAppState.getInstance(previewContext).getModel().getModelDelegate(),
                     new BaseLauncherBinder(LauncherAppState.getInstance(previewContext), bgModel,
-                            /* bgAllAppsList= */ null, new Callbacks[0]),
-                    LauncherAppState.getInstance(
-                            previewContext).getModel().getWidgetsFilterDataProvider()) {
+                            /* bgAllAppsList= */ null, new Callbacks[0])) {
 
                 @Override
                 public void run() {
@@ -421,7 +422,7 @@
 
 
         if (!Flags.newCustomizationPickerUi()) {
-            view.setAlpha(0);
+            view.setAlpha(mSkipAnimations ? 1 : 0);
             view.animate().alpha(1)
                     .setInterpolator(new AccelerateDecelerateInterpolator())
                     .setDuration(FADE_IN_ANIMATION_DURATION)
@@ -442,7 +443,7 @@
             );
             mViewRoot.setLayoutParams(layoutParams);
             mViewRoot.addView(view);
-            mViewRoot.setAlpha(0);
+            mViewRoot.setAlpha(mSkipAnimations ? 1 : 0);
             mViewRoot.animate().alpha(1)
                     .setInterpolator(new AccelerateDecelerateInterpolator())
                     .setDuration(FADE_IN_ANIMATION_DURATION)
diff --git a/src/com/android/launcher3/graphics/ShapeDelegate.kt b/src/com/android/launcher3/graphics/ShapeDelegate.kt
index 9033eac..7c04292 100644
--- a/src/com/android/launcher3/graphics/ShapeDelegate.kt
+++ b/src/com/android/launcher3/graphics/ShapeDelegate.kt
@@ -203,7 +203,11 @@
                     start =
                         poly.transformed(
                             Matrix().apply {
-                                setRectToRect(RectF(0f, 0f, 100f, 100f), RectF(startRect), FILL)
+                                setRectToRect(
+                                    RectF(0f, 0f, DEFAULT_PATH_SIZE, DEFAULT_PATH_SIZE),
+                                    RectF(startRect),
+                                    FILL,
+                                )
                             }
                         ),
                     end =
@@ -281,7 +285,10 @@
                     PathParser.createPathFromPathData(shapeStr).apply {
                         transform(
                             Matrix().apply {
-                                setScale(AREA_CALC_SIZE / 100f, AREA_CALC_SIZE / 100f)
+                                setScale(
+                                    AREA_CALC_SIZE / DEFAULT_PATH_SIZE,
+                                    AREA_CALC_SIZE / DEFAULT_PATH_SIZE,
+                                )
                             }
                         )
                     }
diff --git a/src/com/android/launcher3/graphics/ThemeManager.kt b/src/com/android/launcher3/graphics/ThemeManager.kt
index de85460..ebb7ea0 100644
--- a/src/com/android/launcher3/graphics/ThemeManager.kt
+++ b/src/com/android/launcher3/graphics/ThemeManager.kt
@@ -103,7 +103,7 @@
     private fun parseIconState(oldState: IconState?): IconState {
         val shapeModel =
             prefs.get(PREF_ICON_SHAPE).let { shapeOverride ->
-                ShapesProvider.iconShapes.values.firstOrNull { it.key == shapeOverride }
+                ShapesProvider.iconShapes.firstOrNull { it.key == shapeOverride }
             }
         val iconMask =
             when {
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 2f1af68..74d5098 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -433,20 +433,29 @@
         @UiEvent(doc = "Notification dismissed by swiping right.")
         LAUNCHER_NOTIFICATION_DISMISSED(652),
 
-        @UiEvent(doc = "Current grid size is changed to 6.")
-        LAUNCHER_GRID_SIZE_6(930),
+        @UiEvent(doc = "Current grid size is changed to 2x2")
+        LAUNCHER_GRID_SIZE_2_BY_2(2181),
 
-        @UiEvent(doc = "Current grid size is changed to 5.")
-        LAUNCHER_GRID_SIZE_5(662),
+        @UiEvent(doc = "Current grid size is changed to 3x3")
+        LAUNCHER_GRID_SIZE_3_BY_3(2182),
 
-        @UiEvent(doc = "Current grid size is changed to 4.")
-        LAUNCHER_GRID_SIZE_4(663),
+        @UiEvent(doc = "Current grid size is changed to 4x4")
+        LAUNCHER_GRID_SIZE_4_BY_4(2183),
 
-        @UiEvent(doc = "Current grid size is changed to 3.")
-        LAUNCHER_GRID_SIZE_3(664),
+        @UiEvent(doc = "Current grid size is changed to 4x5")
+        LAUNCHER_GRID_SIZE_4_BY_5(2184),
 
-        @UiEvent(doc = "Current grid size is changed to 2.")
-        LAUNCHER_GRID_SIZE_2(665),
+        @UiEvent(doc = "Current grid size is changed to 4x6")
+        LAUNCHER_GRID_SIZE_4_BY_6(2185),
+
+        @UiEvent(doc = "Current grid size is changed to 5x5")
+        LAUNCHER_GRID_SIZE_5_BY_5(2186),
+
+        @UiEvent(doc = "Current grid size is changed to 5x6")
+        LAUNCHER_GRID_SIZE_5_BY_6(2187),
+
+        @UiEvent(doc = "Current grid size is changed to 6x5")
+        LAUNCHER_GRID_SIZE_6_BY_5(2188),
 
         @UiEvent(doc = "Launcher entered into AllApps state.")
         LAUNCHER_ALLAPPS_ENTRY(692),
diff --git a/src/com/android/launcher3/model/BaseLauncherBinder.java b/src/com/android/launcher3/model/BaseLauncherBinder.java
index a2ca6b6..262bf67 100644
--- a/src/com/android/launcher3/model/BaseLauncherBinder.java
+++ b/src/com/android/launcher3/model/BaseLauncherBinder.java
@@ -26,8 +26,6 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
-import static java.util.Collections.emptyList;
-
 import android.os.Trace;
 import android.util.Log;
 import android.util.Pair;
@@ -45,7 +43,6 @@
 import com.android.launcher3.model.BgDataModel.FixedContainerItems;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
@@ -165,17 +162,10 @@
         if (!WIDGETS_ENABLED) {
             return;
         }
-        Map<PackageItemInfo, List<WidgetItem>>
-                widgetsByPackageItem = mBgDataModel.widgetsModel.getWidgetsByPackageItemForPicker();
         List<WidgetsListBaseEntry> widgets = new WidgetsListBaseEntriesBuilder(mApp.getContext())
-                .build(widgetsByPackageItem);
-        Predicate<WidgetItem> filter = mBgDataModel.widgetsModel.getDefaultWidgetsFilter();
-        List<WidgetsListBaseEntry> defaultWidgets =
-                filter != null ? new WidgetsListBaseEntriesBuilder(
-                        mApp.getContext()).build(widgetsByPackageItem,
-                        mBgDataModel.widgetsModel.getDefaultWidgetsFilter()) : emptyList();
+                .build(mBgDataModel.widgetsModel.getWidgetsByPackageItemForPicker());
 
-        executeCallbacksTask(c -> c.bindAllWidgets(widgets, defaultWidgets), mUiExecutor);
+        executeCallbacksTask(c -> c.bindAllWidgets(widgets), mUiExecutor);
     }
 
     /**
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index eab28b7..d9eccaf 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -431,9 +431,8 @@
         /**
          * Binds the app widgets to the providers that share widgets with the UI.
          */
-        default void bindAllWidgets(@NonNull List<WidgetsListBaseEntry> widgets,
-                @NonNull List<WidgetsListBaseEntry> defaultWidgets) {
-        }
+        default void bindAllWidgets(@NonNull List<WidgetsListBaseEntry> widgets) { }
+
         default void bindSmartspaceWidget() { }
 
         /** Called when workspace has been bound. */
diff --git a/src/com/android/launcher3/model/DeviceGridState.java b/src/com/android/launcher3/model/DeviceGridState.java
index d06f541..96ce4c8 100644
--- a/src/com/android/launcher3/model/DeviceGridState.java
+++ b/src/com/android/launcher3/model/DeviceGridState.java
@@ -21,11 +21,6 @@
 import static com.android.launcher3.LauncherPrefs.DEVICE_TYPE;
 import static com.android.launcher3.LauncherPrefs.HOTSEAT_COUNT;
 import static com.android.launcher3.LauncherPrefs.WORKSPACE_SIZE;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_2;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_3;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_4;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_5;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_6;
 
 import android.content.Context;
 import android.text.TextUtils;
@@ -114,17 +109,23 @@
      */
     public LauncherEvent getWorkspaceSizeEvent() {
         if (!TextUtils.isEmpty(mGridSizeString)) {
-            switch (getColumns()) {
-                case 6:
-                    return LAUNCHER_GRID_SIZE_6;
-                case 5:
-                    return LAUNCHER_GRID_SIZE_5;
-                case 4:
-                    return LAUNCHER_GRID_SIZE_4;
-                case 3:
-                    return LAUNCHER_GRID_SIZE_3;
-                case 2:
-                    return LAUNCHER_GRID_SIZE_2;
+            switch (mGridSizeString) {
+                case "2,2":
+                    return LauncherEvent.LAUNCHER_GRID_SIZE_2_BY_2;
+                case "3,3":
+                    return LauncherEvent.LAUNCHER_GRID_SIZE_3_BY_3;
+                case "4,4":
+                    return LauncherEvent.LAUNCHER_GRID_SIZE_4_BY_4;
+                case "4,5":
+                    return LauncherEvent.LAUNCHER_GRID_SIZE_4_BY_5;
+                case "4,6":
+                    return LauncherEvent.LAUNCHER_GRID_SIZE_4_BY_6;
+                case "5,5":
+                    return LauncherEvent.LAUNCHER_GRID_SIZE_5_BY_5;
+                case "5,6":
+                    return LauncherEvent.LAUNCHER_GRID_SIZE_5_BY_6;
+                case "6,5":
+                    return LauncherEvent.LAUNCHER_GRID_SIZE_6_BY_5;
             }
         }
         return null;
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index d44b289..fb1ebaf 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -20,7 +20,6 @@
 import static com.android.launcher3.Flags.enableLauncherBrMetricsFixed;
 import static com.android.launcher3.Flags.enableSmartspaceAsAWidget;
 import static com.android.launcher3.Flags.enableSmartspaceRemovalToggle;
-import static com.android.launcher3.Flags.enableTieredWidgetsByDefaultInPicker;
 import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE;
 import static com.android.launcher3.LauncherPrefs.SHOULD_SHOW_SMARTSPACE;
 import static com.android.launcher3.LauncherSettings.Favorites.DESKTOP_ICON_FLAG;
@@ -148,7 +147,6 @@
     private final UserManager mUserManager;
     private final UserCache mUserCache;
     private final PackageManagerHelper mPmHelper;
-    private final WidgetsFilterDataProvider mWidgetsFilterDataProvider;
 
     private final InstallSessionHelper mSessionHelper;
     private final IconCache mIconCache;
@@ -167,16 +165,13 @@
     private String mDbName;
 
     public LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel,
-            ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder,
-            @NonNull WidgetsFilterDataProvider widgetsFilterDataProvider) {
-        this(app, bgAllAppsList, bgModel, modelDelegate, launcherBinder, widgetsFilterDataProvider,
-                new UserManagerState());
+            ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder) {
+        this(app, bgAllAppsList, bgModel, modelDelegate, launcherBinder, new UserManagerState());
     }
 
     @VisibleForTesting
     LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel,
             ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder,
-            WidgetsFilterDataProvider widgetsFilterDataProvider,
             UserManagerState userManagerState) {
         mApp = app;
         mBgAllAppsList = bgAllAppsList;
@@ -191,7 +186,6 @@
         mIconCache = mApp.getIconCache();
         mUserManagerState = userManagerState;
         mInstallingPkgsCached = null;
-        mWidgetsFilterDataProvider = widgetsFilterDataProvider;
     }
 
     protected synchronized void waitForIdle() {
@@ -348,13 +342,6 @@
 
             // fourth step
             WidgetsModel widgetsModel = mBgDataModel.widgetsModel;
-            if (enableTieredWidgetsByDefaultInPicker()) {
-                // Begin periodic refresh of filters
-                mWidgetsFilterDataProvider.initPeriodicDataRefresh(
-                        mApp.getModel()::onWidgetFiltersLoaded);
-                // And, update model with currently cached data.
-                widgetsModel.updateWidgetFilters(mWidgetsFilterDataProvider);
-            }
             List<CachedObject> allWidgetsList = widgetsModel.update(mApp, /*packageUser=*/null);
             logASplit("load widgets finished");
 
diff --git a/src/com/android/launcher3/model/ModelTaskController.kt b/src/com/android/launcher3/model/ModelTaskController.kt
index 6e3e35e..5566482 100644
--- a/src/com/android/launcher3/model/ModelTaskController.kt
+++ b/src/com/android/launcher3/model/ModelTaskController.kt
@@ -77,19 +77,10 @@
     }
 
     fun bindUpdatedWidgets(dataModel: BgDataModel) {
-        val widgetsByPackageItem = dataModel.widgetsModel.widgetsByPackageItemForPicker
-        val allWidgets = WidgetsListBaseEntriesBuilder(app.context).build(widgetsByPackageItem)
-
-        val defaultWidgetsFilter = dataModel.widgetsModel.defaultWidgetsFilter
-        val defaultWidgets =
-            if (defaultWidgetsFilter != null) {
-                WidgetsListBaseEntriesBuilder(app.context)
-                    .build(widgetsByPackageItem, defaultWidgetsFilter)
-            } else {
-                emptyList()
-            }
-
-        scheduleCallbackTask { it.bindAllWidgets(allWidgets, defaultWidgets) }
+        val allWidgets =
+            WidgetsListBaseEntriesBuilder(app.context)
+                .build(dataModel.widgetsModel.widgetsByPackageItemForPicker)
+        scheduleCallbackTask { it.bindAllWidgets(allWidgets) }
     }
 
     fun deleteAndBindComponentsRemoved(matcher: Predicate<ItemInfo?>, reason: String?) {
diff --git a/src/com/android/launcher3/model/WidgetsFilterDataProvider.kt b/src/com/android/launcher3/model/WidgetsFilterDataProvider.kt
index 0571de3..90d6fb2 100644
--- a/src/com/android/launcher3/model/WidgetsFilterDataProvider.kt
+++ b/src/com/android/launcher3/model/WidgetsFilterDataProvider.kt
@@ -16,55 +16,38 @@
 
 package com.android.launcher3.model
 
-import android.content.Context
-import androidx.annotation.WorkerThread
-import com.android.launcher3.R
-import com.android.launcher3.util.ResourceBasedOverride
+import com.android.launcher3.dagger.LauncherAppSingleton
 import java.util.function.Predicate
+import javax.inject.Inject
 
 /** Helper for the widgets model to load the filters that can be applied to available widgets. */
-open class WidgetsFilterDataProvider(val context: Context) : ResourceBasedOverride {
+@LauncherAppSingleton
+open class WidgetsFilterDataProvider @Inject constructor() {
+
+    /** Filter that should be applied to the widget predictions */
+    open val predictedWidgetsFilter: Predicate<WidgetItem>? = null
+
     /**
-     * Start regular periodic refresh of widget filtering data starting now (if not started
-     * already).
+     * Filter that should be applied to the widgets list to see which widgets can be shown by
+     * default.
      */
-    @WorkerThread
-    open fun initPeriodicDataRefresh(callback: WidgetsFilterLoadedCallback? = null) {
-        // no-op
+    open val defaultWidgetsFilter: Predicate<WidgetItem>? = null
+
+    protected val listeners = mutableListOf<WidgetsFilterLoadedCallback>()
+
+    /** Adds a callback for listening to filter changes */
+    fun addFilterChangeCallback(callback: WidgetsFilterLoadedCallback) {
+        listeners.add(callback)
     }
 
-    /**
-     * Returns a filter that should be applied to the widget predictions.
-     *
-     * @return null if no filter needs to be applied
-     */
-    @WorkerThread open fun getPredictedWidgetsFilter(): Predicate<WidgetItem>? = null
-
-    /**
-     * Returns a filter that should be applied to the widgets list to see which widgets can be shown
-     * by default.
-     *
-     * @return null if no separate "default" list is supported
-     */
-    @WorkerThread open fun getDefaultWidgetsFilter(): Predicate<WidgetItem>? = null
-
-    /** Called when filter data provider is no longer needed. */
-    open fun destroy() {}
-
-    companion object {
-        /** Returns a new instance of the [WidgetsFilterDataProvider] based on resource override. */
-        fun newInstance(context: Context?): WidgetsFilterDataProvider {
-            return ResourceBasedOverride.Overrides.getObject(
-                WidgetsFilterDataProvider::class.java,
-                context,
-                R.string.widgets_filter_data_provider_class,
-            )
-        }
+    /** Removes a previously added callback */
+    fun removeFilterChangeCallback(callback: WidgetsFilterLoadedCallback) {
+        listeners.remove(callback)
     }
-}
 
-/** Interface for the model callback to be invoked when filters are loaded. */
-interface WidgetsFilterLoadedCallback {
-    /** Method called back when widget filters are loaded */
-    fun onWidgetsFilterLoaded()
+    /** Interface for the model callback to be invoked when filters are loaded. */
+    interface WidgetsFilterLoadedCallback {
+        /** Method called back when widget filters are loaded */
+        fun onWidgetsFilterLoaded()
+    }
 }
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index ab960d8..52b142d 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -19,8 +19,6 @@
 import android.util.Log;
 import android.util.Pair;
 
-import androidx.annotation.AnyThread;
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.collection.ArrayMap;
 
@@ -68,8 +66,6 @@
 
     /* Map of widgets and shortcuts that are tracked per package. */
     private final Map<PackageItemInfo, List<WidgetItem>> mWidgetsByPackageItem = new HashMap<>();
-    @Nullable private Predicate<WidgetItem> mDefaultWidgetsFilter = null;
-    @Nullable private Predicate<WidgetItem> mPredictedWidgetsFilter = null;
     @Nullable private WidgetValidityCheckForPicker mWidgetValidityCheckForPicker = null;
 
     /**
@@ -129,37 +125,6 @@
     }
 
     /**
-     * Returns widget filter that can be applied to {@link WidgetItem}s to check if they can be
-     * shown in the default widgets list.
-     * <p>Returns null if filtering isn't available</p>
-     */
-    @AnyThread
-    public @Nullable Predicate<WidgetItem> getDefaultWidgetsFilter() {
-        return mDefaultWidgetsFilter;
-    }
-
-    /**
-     * Returns widget filter that can be applied to {@link WidgetItem}s to check if they can be
-     * part of widget predictions.
-     * <p>Returns null if filter isn't available</p>
-     */
-    @AnyThread
-    public @Nullable  Predicate<WidgetItem> getPredictedWidgetsFilter() {
-        return mPredictedWidgetsFilter;
-    }
-
-    /**
-     * Updates model with latest filter data in cache.
-     */
-    public void updateWidgetFilters(@NonNull WidgetsFilterDataProvider widgetsFilterDataProvider) {
-        if (!WIDGETS_ENABLED) {
-            return;
-        }
-        mDefaultWidgetsFilter = widgetsFilterDataProvider.getDefaultWidgetsFilter();
-        mPredictedWidgetsFilter = widgetsFilterDataProvider.getPredictedWidgetsFilter();
-    }
-
-    /**
      * @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
      *                    only widgets and shortcuts associated with the package/user are.
      */
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index b60b8cc..f5e5e16 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -26,6 +26,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.Flags;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.graphics.ThemeManager;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.BitmapInfo.DrawableCreationFlags;
@@ -325,10 +326,12 @@
      * Returns a FastBitmapDrawable with the icon and context theme applied
      */
     public FastBitmapDrawable newIcon(Context context, @DrawableCreationFlags int creationFlags) {
-        if (!ThemeManager.INSTANCE.get(context).isIconThemeEnabled()) {
+        ThemeManager themeManager = ThemeManager.INSTANCE.get(context);
+        if (!themeManager.isIconThemeEnabled()) {
             creationFlags &= ~FLAG_THEMED;
         }
-        FastBitmapDrawable drawable = bitmap.newIcon(context, creationFlags);
+        FastBitmapDrawable drawable = bitmap.newIcon(
+                context, creationFlags, Utilities.getIconShapeOrNull(context));
         drawable.setIsDisabled(isDisabled());
         return drawable;
     }
diff --git a/src/com/android/launcher3/model/data/TaskItemInfo.kt b/src/com/android/launcher3/model/data/TaskItemInfo.kt
index fc1cd4d..8b72835 100644
--- a/src/com/android/launcher3/model/data/TaskItemInfo.kt
+++ b/src/com/android/launcher3/model/data/TaskItemInfo.kt
@@ -17,8 +17,7 @@
 package com.android.launcher3.model.data
 
 /**
- * Temporary class holding a Task ID to allow us to reference a Task when clicking a hotseat item.
- *
- * TODO(b/315344726): Remove this class when we have proper Taskbar support for multi-instance apps
+ * A Task info class holding a Task ID to allow us to reference a Task when clicking a hotseat item.
+ * This is also used to help identify the shortcuts shown in the long-press menu.
  */
 class TaskItemInfo(val taskId: Int, itemInfo: WorkspaceItemInfo) : WorkspaceItemInfo(itemInfo)
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index a691e45..a9a26a1 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.pageindicators;
 
+import static com.android.launcher3.Flags.enableLauncherVisualRefresh;
 import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
 
 import android.animation.Animator;
@@ -53,12 +54,15 @@
 /**
  * {@link PageIndicator} which shows dots per page. The active page is shown with the current
  * accent color.
+ * <p>
+ * TODO(b/402258632): Split PageIndicatorDots into 2 different classes: FolderPageIndicator &
+ * WorkspacePageIndicator. A lot of the functionality in this class is only used by one UI purpose.
  */
 public class PageIndicatorDots extends View implements Insettable, PageIndicator {
 
     private static final float SHIFT_PER_ANIMATION = 0.5f;
-    private static final float SHIFT_THRESHOLD = 0.1f;
-    private static final long ANIMATION_DURATION = 150;
+    private static final float SHIFT_THRESHOLD = (enableLauncherVisualRefresh() ? 0.5f : 0.2f);
+    private static final long ANIMATION_DURATION = (enableLauncherVisualRefresh() ? 200 : 150);
     private static final int PAGINATION_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
     private static final int PAGINATION_FADE_IN_DURATION = 83;
     private static final int PAGINATION_FADE_OUT_DURATION = 167;
@@ -78,6 +82,7 @@
     // This value approximately overshoots to 1.5 times the original size.
     private static final float ENTER_ANIMATION_OVERSHOOT_TENSION = 4.9f;
 
+    // This is used to optimize the onDraw method by not constructing a new RectF each draw.
     private static final RectF sTempRect = new RectF();
 
     private static final FloatProperty<PageIndicatorDots> CURRENT_POSITION =
@@ -93,7 +98,7 @@
                     obj.invalidate();
                     obj.invalidateOutline();
                 }
-    };
+            };
 
     private static final IntProperty<PageIndicatorDots> PAGINATION_ALPHA =
             new IntProperty<PageIndicatorDots>("pagination_alpha") {
@@ -111,6 +116,7 @@
 
     private final Handler mDelayedPaginationFadeHandler = new Handler(Looper.getMainLooper());
     private final float mDotRadius;
+    private final float mGapWidth;
     private final float mCircleGap;
     private final boolean mIsRtl;
 
@@ -130,6 +136,7 @@
      * 1.0  => Active dot is at position 1
      */
     private float mCurrentPosition;
+    private int mLastPosition;
     private float mFinalPosition;
     private boolean mIsScrollPaused;
     @VisibleForTesting
@@ -157,7 +164,10 @@
         mPaginationPaint.setStyle(Style.FILL);
         mPaginationPaint.setColor(Themes.getAttrColor(context, R.attr.pageIndicatorDotColor));
         mDotRadius = getResources().getDimension(R.dimen.page_indicator_dot_size) / 2;
-        mCircleGap = DOT_GAP_FACTOR * mDotRadius;
+        mGapWidth = getResources().getDimension(R.dimen.page_indicator_gap_width);
+        mCircleGap = (enableLauncherVisualRefresh())
+                ? mDotRadius * 2 + mGapWidth
+                : DOT_GAP_FACTOR * mDotRadius;
         setOutlineProvider(new MyOutlineProver());
         mIsRtl = Utilities.isRtl(getResources());
     }
@@ -188,29 +198,40 @@
 
         mTotalScroll = totalScroll;
 
-        int scrollPerPage = totalScroll / (mNumPages - 1);
-        int pageToLeft = scrollPerPage == 0 ? 0 : currentScroll / scrollPerPage;
-        int pageToLeftScroll = pageToLeft * scrollPerPage;
-        int pageToRightScroll = pageToLeftScroll + scrollPerPage;
+        if (enableLauncherVisualRefresh()) {
+            float scrollPerPage = (float) totalScroll / (mNumPages - 1);
+            float position = currentScroll / scrollPerPage;
+            animateToPosition(Math.round(position));
 
-        float scrollThreshold = SHIFT_THRESHOLD * scrollPerPage;
-        if (currentScroll < pageToLeftScroll + scrollThreshold) {
-            // scroll is within the left page's threshold
-            animateToPosition(pageToLeft);
-            if (mShouldAutoHide) {
-                hideAfterDelay();
-            }
-        } else if (currentScroll > pageToRightScroll - scrollThreshold) {
-            // scroll is far enough from left page to go to the right page
-            animateToPosition(pageToLeft + 1);
-            if (mShouldAutoHide) {
+            float delta = Math.abs((int) position - position);
+            if (mShouldAutoHide && (delta < 0.1 || delta > 0.9)) {
                 hideAfterDelay();
             }
         } else {
-            // scroll is between left and right page
-            animateToPosition(pageToLeft + SHIFT_PER_ANIMATION);
-            if (mShouldAutoHide) {
-                mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null);
+            int scrollPerPage = totalScroll / (mNumPages - 1);
+            int pageToLeft = scrollPerPage == 0 ? 0 : currentScroll / scrollPerPage;
+            int pageToLeftScroll = pageToLeft * scrollPerPage;
+            int pageToRightScroll = pageToLeftScroll + scrollPerPage;
+
+            float scrollThreshold = SHIFT_THRESHOLD * scrollPerPage;
+            if (currentScroll < pageToLeftScroll + scrollThreshold) {
+                // scroll is within the left page's threshold
+                animateToPosition(pageToLeft);
+                if (mShouldAutoHide) {
+                    hideAfterDelay();
+                }
+            } else if (currentScroll > pageToRightScroll - scrollThreshold) {
+                // scroll is far enough from left page to go to the right page
+                animateToPosition(pageToLeft + 1);
+                if (mShouldAutoHide) {
+                    hideAfterDelay();
+                }
+            } else {
+                // scroll is between left and right page
+                animateToPosition(pageToLeft + SHIFT_PER_ANIMATION);
+                if (mShouldAutoHide) {
+                    mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null);
+                }
             }
         }
     }
@@ -283,15 +304,23 @@
 
     private void animateToPosition(float position) {
         mFinalPosition = position;
-        if (Math.abs(mCurrentPosition - mFinalPosition) < SHIFT_THRESHOLD) {
+        if (!enableLauncherVisualRefresh()
+                && Math.abs(mCurrentPosition - mFinalPosition) < SHIFT_THRESHOLD) {
             mCurrentPosition = mFinalPosition;
         }
-        if (mAnimator == null && Float.compare(mCurrentPosition, mFinalPosition) != 0) {
-            float positionForThisAnim = mCurrentPosition > mFinalPosition ?
-                    mCurrentPosition - SHIFT_PER_ANIMATION : mCurrentPosition + SHIFT_PER_ANIMATION;
+        if (mAnimator == null && Float.compare(mCurrentPosition, position) != 0) {
+            float positionForThisAnim = enableLauncherVisualRefresh()
+                    ? position
+                    : (mCurrentPosition > mFinalPosition
+                            ? mCurrentPosition - SHIFT_PER_ANIMATION
+                            : mCurrentPosition + SHIFT_PER_ANIMATION);
             mAnimator = ObjectAnimator.ofFloat(this, CURRENT_POSITION, positionForThisAnim);
             mAnimator.addListener(new AnimationCycleListener());
             mAnimator.setDuration(ANIMATION_DURATION);
+            if (enableLauncherVisualRefresh()) {
+                mLastPosition = (int) mCurrentPosition;
+                mAnimator.setInterpolator(new OvershootInterpolator());
+            }
             mAnimator.start();
         }
     }
@@ -314,6 +343,7 @@
         invalidate();
     }
 
+    // TODO(b/394355070): Verify Folder Entry Animation works correctly with visual updates
     public void playEntryAnimation() {
         int count = mEntryAnimationRadiusFactors.length;
         if (count == 0) {
@@ -391,6 +421,7 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        // TODO(b/394355070): Verify Folder Entry Animation works correctly with visual updates
         // Add extra spacing of mDotRadius on all sides so than entry animation could be run.
         int width = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY ?
                 MeasureSpec.getSize(widthMeasureSpec) : (int) ((mNumPages * 3 + 2) * mDotRadius);
@@ -410,17 +441,14 @@
             return;
         }
 
-        // Draw all page indicators;
         float circleGap = mCircleGap;
-        float startX = ((float) getWidth() / 2)
-                - (mCircleGap * (((float) mNumPages - 1) / 2))
-                - mDotRadius;
-
-        float x = startX + mDotRadius;
+        float x = ((float) getWidth() / 2) - (mCircleGap * ((float) mNumPages - 1) / 2);
         float y = getHeight() / 2;
 
         if (mEntryAnimationRadiusFactors != null) {
             // During entry animation, only draw the circles
+            // TODO(b/394355070): Verify Folder Entry Animation works correctly - visual updates
+
             if (mIsRtl) {
                 x = getWidth() - x;
                 circleGap = -circleGap;
@@ -432,18 +460,84 @@
                 x += circleGap;
             }
         } else {
+            // Save the current alpha value, so we can reset to it again after drawing the dots
             int alpha = mPaginationPaint.getAlpha();
 
-            // Here we draw the dots
-            mPaginationPaint.setAlpha((int) (alpha * DOT_ALPHA_FRACTION));
-            for (int i = 0; i < mNumPages; i++) {
-                canvas.drawCircle(x, y, mDotRadius, mPaginationPaint);
-                x += circleGap;
+            if (enableLauncherVisualRefresh()) {
+                int nonActiveAlpha = (int) (alpha * DOT_ALPHA_FRACTION);
+
+                float diameter = 2 * mDotRadius;
+                sTempRect.top = y - mDotRadius;
+                sTempRect.bottom = y + mDotRadius;
+                sTempRect.left = x - diameter;
+
+                float posDif = Math.abs(mLastPosition - mCurrentPosition);
+                float boundedPosition = (posDif > 1)
+                        ? Math.round(mCurrentPosition)
+                        : mCurrentPosition;
+                float bounceProgress = (posDif > 1) ? posDif - 1 : 0;
+                float bounceAdjustment = Math.abs(mCurrentPosition - boundedPosition) * diameter;
+
+                // Here we draw the dots, one at a time from the left-most dot to the right-most dot
+                // 1.0 => 000000 000000111111 000000
+                // 1.3 => 000000 0000001111 11000000
+                // 1.6 => 000000 00000011 1111000000
+                // 2.0 => 000000 000000 111111000000
+                for (int i = 0; i < mNumPages; i++) {
+                    mPaginationPaint.setAlpha(nonActiveAlpha);
+                    float delta = Math.abs(boundedPosition - i);
+                    if (delta <= SHIFT_THRESHOLD) {
+                        mPaginationPaint.setAlpha(alpha);
+                    }
+
+                    // If boundedPosition is 3.3, both 3 and 4 should enter this condition.
+                    // If boundedPosition is 3, only 3 should enter this condition.
+                    if (delta < 1) {
+                        sTempRect.right = sTempRect.left + diameter + ((1 - delta) * diameter);
+
+                        // While the animation is shifting the active pagination dots size from
+                        // the previously active one, to the newly active dot, there is no bounce
+                        // adjustment. The bounce happens in the "Overshoot" phase of the animation.
+                        // mLastPosition is used to determine when the currentPosition is just
+                        // leaving the page, or if it is in the overshoot phase.
+                        if (boundedPosition == i && bounceProgress != 0) {
+                            if (mLastPosition < mCurrentPosition) {
+                                sTempRect.left -= bounceAdjustment;
+                            } else {
+                                sTempRect.right += bounceAdjustment;
+                            }
+                        }
+                    } else {
+                        sTempRect.right = sTempRect.left + diameter;
+
+                        if (mLastPosition == i && bounceProgress != 0) {
+                            if (mLastPosition > mCurrentPosition) {
+                                sTempRect.left += bounceAdjustment;
+                            } else {
+                                sTempRect.right -= bounceAdjustment;
+                            }
+                        }
+                    }
+                    canvas.drawRoundRect(sTempRect, mDotRadius, mDotRadius, mPaginationPaint);
+
+                    // TODO(b/394355070) Verify RTL experience works correctly with visual updates
+                    sTempRect.left = sTempRect.right + mGapWidth;
+                }
+            } else {
+                // Here we draw the dots
+                mPaginationPaint.setAlpha((int) (alpha * DOT_ALPHA_FRACTION));
+                for (int i = 0; i < mNumPages; i++) {
+                    canvas.drawCircle(x, y, mDotRadius, mPaginationPaint);
+                    x += circleGap;
+                }
+
+                // Here we draw the current page indicator
+                mPaginationPaint.setAlpha(alpha);
+                canvas.drawRoundRect(getActiveRect(), mDotRadius, mDotRadius, mPaginationPaint);
             }
 
-            // Here we draw the current page indicator
+            // Reset the alpha so it doesn't become progressively more transparent each onDraw call
             mPaginationPaint.setAlpha(alpha);
-            canvas.drawRoundRect(getActiveRect(), mDotRadius, mDotRadius, mPaginationPaint);
         }
     }
 
@@ -499,6 +593,7 @@
         @Override
         public void getOutline(View view, Outline outline) {
             if (mEntryAnimationRadiusFactors == null) {
+                // TODO(b/394355070): Verify Outline works correctly with visual updates
                 RectF activeRect = getActiveRect();
                 outline.setRoundRect(
                         (int) activeRect.left,
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index 20c0ecc..98a3882 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -219,6 +219,6 @@
     public static UserBadgeDrawable getBadgeDrawable(Context context, UserHandle userHandle) {
         return (UserBadgeDrawable) BitmapInfo.LOW_RES_INFO.withFlags(UserCache.getInstance(context)
                         .getUserInfo(userHandle).applyBitmapInfoFlags(FlagOp.NO_OP))
-                .getBadgeDrawable(context, false /* isThemed */);
+                .getBadgeDrawable(context, false /* isThemed */, null);
     }
 }
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index e52ca6d..aad1400 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -234,6 +234,20 @@
      */
     public void populateAndShowRows(final BubbleTextView originalIcon,
             int deepShortcutCount, List<SystemShortcut> systemShortcuts) {
+        populateAndShowRows(originalIcon, (ItemInfo) originalIcon.getTag(), deepShortcutCount,
+                systemShortcuts);
+    }
+
+    /**
+     * Populate and show shortcuts for the Launcher U app shortcut design.
+     * Will inflate the container and shortcut View instances for the popup container.
+     * @param originalIcon App icon that the popup is shown for
+     * @param itemInfo The info that is used to load app shortcuts
+     * @param deepShortcutCount Number of DeepShortcutView instances to add to container
+     * @param systemShortcuts List of SystemShortcuts to add to container
+     */
+    public void populateAndShowRows(final BubbleTextView originalIcon, ItemInfo itemInfo,
+            int deepShortcutCount, List<SystemShortcut> systemShortcuts) {
 
         mOriginalIcon = originalIcon;
         mContainerWidth = getResources().getDimensionPixelSize(R.dimen.bg_popup_item_width);
@@ -246,7 +260,7 @@
                     R.layout.system_shortcut);
         }
         show();
-        loadAppShortcuts((ItemInfo) originalIcon.getTag());
+        loadAppShortcuts(itemInfo);
     }
 
     /**
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index 8a5e388..5c1a755 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -23,20 +23,27 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
 import com.android.launcher3.dot.DotInfo;
+import com.android.launcher3.dot.FolderDotInfo;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.notification.NotificationKeyData;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.ShortcutUtil;
+import com.android.launcher3.views.ActivityContext;
 
 import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 /**
@@ -47,19 +54,49 @@
     private static final boolean LOGD = false;
     private static final String TAG = "PopupDataProvider";
 
-    private final Consumer<Predicate<PackageUserKey>> mNotificationDotsChangeListener;
+    private final ActivityContext mContext;
+
+    /** Maps packages to their DotInfo's . */
+    private final Map<PackageUserKey, DotInfo> mPackageUserToDotInfos = new HashMap<>();
 
     /** Maps launcher activity components to a count of how many shortcuts they have. */
     private HashMap<ComponentKey, Integer> mDeepShortcutMap = new HashMap<>();
-    /** Maps packages to their DotInfo's . */
-    private Map<PackageUserKey, DotInfo> mPackageUserToDotInfos = new HashMap<>();
 
-    public PopupDataProvider(Consumer<Predicate<PackageUserKey>> notificationDotsChangeListener) {
-        mNotificationDotsChangeListener = notificationDotsChangeListener;
+    public PopupDataProvider(ActivityContext context) {
+        mContext = context;
     }
 
     private void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
-        mNotificationDotsChangeListener.accept(updatedDots);
+        final PackageUserKey packageUserKey = new PackageUserKey(null, null);
+        Predicate<ItemInfo> matcher = info -> !packageUserKey.updateFromItemInfo(info)
+                || updatedDots.test(packageUserKey);
+
+        ItemOperator op = (info, v) -> {
+            if (v instanceof BubbleTextView && info != null && matcher.test(info)) {
+                ((BubbleTextView) v).applyDotState(info, true /* animate */);
+            } else if (v instanceof FolderIcon icon
+                    && info instanceof FolderInfo fi && fi.anyMatch(matcher)) {
+                FolderDotInfo folderDotInfo = new FolderDotInfo();
+                for (ItemInfo si : fi.getContents()) {
+                    folderDotInfo.addDotInfo(getDotInfoForItem(si));
+                }
+                icon.setDotInfo(folderDotInfo);
+            }
+
+            // process all the shortcuts
+            return false;
+        };
+
+        mContext.getContent().mapOverItems(op);
+        Folder folder = Folder.getOpen(mContext);
+        if (folder != null) {
+            folder.mapOverItems(op);
+        }
+
+        ActivityAllAppsContainerView<?> appsView = mContext.getAppsView();
+        if (appsView != null) {
+            appsView.getAppsStore().updateNotificationDots(updatedDots);
+        }
     }
 
     @Override
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 7e08c6e..b7efdec 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -210,30 +210,6 @@
         }
     }
 
-    public static final Factory<ActivityContext> PIN_UNPIN_ITEM =
-            (context, itemInfo, originalView) -> {
-                // Predicted items use {@code HotseatPredictionController.PinPrediction} shortcut
-                // to pin.
-                if (itemInfo.isPredictedItem()) {
-                    return null;
-                }
-                return new PinUnpinItem<>(context, itemInfo, originalView);
-            };
-
-    private static class PinUnpinItem<T extends ActivityContext> extends SystemShortcut<T> {
-        PinUnpinItem(T target, ItemInfo itemInfo, @NonNull View originalView) {
-            // TODO(b/375648361): Check the pin state of the item to determine if the pin or the
-            //  unpin option should be used.
-            super(R.drawable.ic_pin, R.string.pin_to_taskbar, target,
-                    itemInfo, originalView);
-        }
-
-        @Override
-        public void onClick(View view) {
-            // TODO(b/375648361): Pin/Unpin the item here.
-        }
-    }
-
     public static final Factory<ActivityContext> PRIVATE_PROFILE_INSTALL =
             (context, itemInfo, originalView) -> {
                 if (originalView == null) {
diff --git a/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt b/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt
index e4c50f0..2d1a5f5 100644
--- a/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt
+++ b/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.util.Log
+import android.view.ContextThemeWrapper
 import android.view.InflateException
 import androidx.annotation.VisibleForTesting
 import androidx.annotation.VisibleForTesting.Companion.PROTECTED
@@ -33,8 +34,6 @@
 import com.android.launcher3.util.Executors.VIEW_PREINFLATION_EXECUTOR
 import com.android.launcher3.util.Themes
 import com.android.launcher3.views.ActivityContext
-import com.android.launcher3.views.ActivityContext.ActivityContextDelegate
-import java.lang.IllegalStateException
 
 const val PREINFLATE_ICONS_ROW_COUNT = 4
 const val EXTRA_ICONS_COUNT = 2
@@ -80,11 +79,9 @@
         // create a separate AssetManager obj internally to avoid lock contention with
         // AssetManager obj that is associated with the launcher context on the main thread.
         val allAppsPreInflationContext =
-            ActivityContextDelegate(
-                context.createConfigurationContext(context.resources.configuration),
-                Themes.getActivityThemeRes(context),
-                context,
-            )
+            ContextThemeWrapper(context, Themes.getActivityThemeRes(context)).apply {
+                applyOverrideConfiguration(context.resources.configuration)
+            }
 
         // Because we perform onCreateViewHolder() on worker thread, we need a separate
         // adapter/inflator object as they are not thread-safe. Note that the adapter
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index c20d655..c4fed71 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.secondarydisplay;
 
 import static com.android.launcher3.util.WallpaperThemeManager.setWallpaperDependentTheme;
+import static com.android.window.flags.Flags.enableTaskbarConnectedDisplays;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -28,6 +29,7 @@
 import android.view.ViewAnimationUtils;
 import android.view.inputmethod.InputMethodManager;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.AbstractFloatingView;
@@ -56,7 +58,6 @@
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.touch.ItemClickHandler.ItemClickProxy;
 import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.Themes;
@@ -82,7 +83,6 @@
     private boolean mAppDrawerShown = false;
 
     private StringCache mStringCache;
-    private boolean mBindingItems = false;
     private SecondaryDisplayPredictions mSecondaryDisplayPredictions;
 
     private final int[] mTempXY = new int[2];
@@ -94,27 +94,6 @@
         mModel = LauncherAppState.getInstance(this).getModel();
         mDragController = new SecondaryDragController(this);
         mSecondaryDisplayPredictions = SecondaryDisplayPredictions.newInstance(this);
-        if (getWindow().getDecorView().isAttachedToWindow()) {
-            initUi();
-        }
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        initUi();
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        this.getDragController().removeDragListener(this);
-    }
-
-    private void initUi() {
-        if (mDragLayer != null) {
-            return;
-        }
 
         mDeviceProfile = InvariantDeviceProfile.INSTANCE.get(this)
                 .createDeviceProfileForSecondaryDisplay(this);
@@ -124,10 +103,13 @@
         mDragLayer = findViewById(R.id.drag_layer);
         mAppsView = findViewById(R.id.apps_view);
         mAppsButton = findViewById(R.id.all_apps_button);
+        // TODO (b/391965805): Replace this flag with DesktopExperiences flag.
+        if (enableTaskbarConnectedDisplays()) {
+            mAppsButton.setVisibility(View.INVISIBLE);
+        }
 
         mDragController.addDragListener(this);
-        mPopupDataProvider = new PopupDataProvider(
-                mAppsView.getAppsStore()::updateNotificationDots);
+        mPopupDataProvider = new PopupDataProvider(this);
 
         mModel.addCallbacksAndLoad(this);
     }
@@ -243,7 +225,9 @@
                 @Override
                 public void onAnimationEnd(Animator animation) {
                     mAppsView.setVisibility(View.INVISIBLE);
-                    mAppsButton.setVisibility(View.VISIBLE);
+                    // TODO (b/391965805): Replace this flag with DesktopExperiences flag.
+                    mAppsButton.setVisibility(
+                            enableTaskbarConnectedDisplays() ? View.INVISIBLE : View.VISIBLE);
                     mAppsView.getSearchUiManager().resetSearch();
                 }
             });
@@ -253,21 +237,10 @@
 
     @Override
     public void startBinding() {
-        mBindingItems = true;
         mDragController.cancelDrag();
     }
 
     @Override
-    public boolean isBindingItems() {
-        return mBindingItems;
-    }
-
-    @Override
-    public void finishBindingItems(IntSet pagesBoundFirst) {
-        mBindingItems = false;
-    }
-
-    @Override
     public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap) {
         mPopupDataProvider.setDeepShortcutMap(deepShortcutMap);
     }
@@ -299,6 +272,8 @@
         mStringCache = cache;
     }
 
+    @Override
+    @NonNull
     public PopupDataProvider getPopupDataProvider() {
         return mPopupDataProvider;
     }
diff --git a/src/com/android/launcher3/shapes/ShapesProvider.kt b/src/com/android/launcher3/shapes/ShapesProvider.kt
index 5427c89..03e30d8 100644
--- a/src/com/android/launcher3/shapes/ShapesProvider.kt
+++ b/src/com/android/launcher3/shapes/ShapesProvider.kt
@@ -16,192 +16,71 @@
 
 package com.android.launcher3.shapes
 
+import androidx.annotation.VisibleForTesting
 import com.android.launcher3.Flags as LauncherFlags
 import com.android.systemui.shared.Flags
 
 object ShapesProvider {
-    val folderShapes =
-        if (LauncherFlags.enableLauncherIconShapes()) {
-            mapOf(
-                "clover" to
-                    "M 39.616 4" +
-                        "C 46.224 6.87 53.727 6.87 60.335 4" +
-                        "L 63.884 2.459" +
-                        "C 85.178 -6.789 106.789 14.822 97.541 36.116" +
-                        "L 96 39.665" +
-                        "C 93.13 46.273 93.13 53.776 96 60.384" +
-                        "L 97.541 63.934" +
-                        "C 106.789 85.227 85.178 106.839 63.884 97.591" +
-                        "L 60.335 96.049" +
-                        "C 53.727 93.179 46.224 93.179 39.616 96.049" +
-                        "L 36.066 97.591" +
-                        "C 14.773 106.839 -6.839 85.227 2.409 63.934" +
-                        "L 3.951 60.384" +
-                        "C 6.821 53.776 6.821 46.273 3.951 39.665" +
-                        "L 2.409 36.116" +
-                        "C -6.839 14.822 14.773 -6.789 36.066 2.459" +
-                        "Z",
-                "complexClover" to
-                    "M 49.85 6.764" +
-                        "L 50.013 6.971" +
-                        "L 50.175 6.764" +
-                        "C 53.422 2.635 58.309 0.207 63.538 0.207" +
-                        "C 65.872 0.207 68.175 0.692 70.381 1.648" +
-                        "L 71.79 2.264" +
-                        "L 71.792 2.265" +
-                        "A 3.46 3.46 0 0 0 74.515 2.265" +
-                        "L 74.517 2.264" +
-                        "L 75.926 1.652" +
-                        "A 17.1 17.1 0 0 1 82.769 0.207" +
-                        "C 88.495 0.207 93.824 3.117 97.022 7.989" +
-                        "C 100.21 12.848 100.697 18.712 98.36 24.087" +
-                        "L 97.749 25.496" +
-                        "V 25.497" +
-                        "A 3.45 3.45 0 0 0 97.749 28.222" +
-                        "V 28.223" +
-                        "L 98.36 29.632" +
-                        "C 100.697 35.007 100.207 40.871 97.022 45.73" +
-                        "A 17.5 17.5 0 0 1 93.264 49.838" +
-                        "L 93.06 50" +
-                        "L 93.264 50.162" +
-                        "A 17.5 17.5 0 0 1 97.022 54.27" +
-                        "C 100.21 59.129 100.697 64.993 98.36 70.368" +
-                        "V 71.778" +
-                        "A 3.45 3.45 0 0 0 97.749 74.503" +
-                        "V 74.504" +
-                        "L 98.36 75.913" +
-                        "C 100.697 81.288 100.207 87.152 97.022 92.011" +
-                        "C 93.824 96.883 88.495 99.793 82.769 99.793" +
-                        "C 80.435 99.793 78.132 99.308 75.926 98.348" +
-                        "L 74.517 97.736" +
-                        "H 74.515" +
-                        "A 3.5 3.5 0 0 0 73.153 97.455" +
-                        "C 72.682 97.455 72.225 97.552 71.792 97.736" +
-                        "H 71.79" +
-                        "L 70.381 98.348" +
-                        "A 17.1 17.1 0 0 1 63.538 99.793" +
-                        "C 58.309 99.793 53.422 97.365 50.175 93.236" +
-                        "L 50.013 93.029" +
-                        "L 49.85 93.236" +
-                        "C 46.603 97.365 41.717 99.793 36.488 99.793" +
-                        "C 34.154 99.793 31.851 99.308 29.645 98.348" +
-                        "L 28.236 97.736" +
-                        "H 28.234" +
-                        "A 3.5 3.5 0 0 0 26.872 97.455" +
-                        "C 26.401 97.455 25.944 97.552 25.511 97.736" +
-                        "H 25.509" +
-                        "L 24.1 98.348" +
-                        "A 17.1 17.1 0 0 1 17.257 99.793" +
-                        "C 11.53 99.793 6.202 96.883 3.004 92.011" +
-                        "C -0.181 87.152 -0.671 81.288 1.661 75.913" +
-                        "L 2.277 74.504" +
-                        "V 74.503" +
-                        "A 3.45 3.45 0 0 0 2.277 71.778" +
-                        "V 71.777" +
-                        "L 1.665 70.368" +
-                        "C -0.671 64.993 -0.181 59.129 3.004 54.274" +
-                        "A 17.5 17.5 0 0 1 6.761 50.162" +
-                        "L 6.965 50" +
-                        "L 6.761 49.838" +
-                        "A 17.5 17.5 0 0 1 3.004 45.73" +
-                        "C -0.181 40.871 -0.671 35.007 1.665 29.632" +
-                        "L 2.277 28.223" +
-                        "V 28.222" +
-                        "A 3.45 3.45 0 0 0 2.277 25.497" +
-                        "V 25.496" +
-                        "L 1.665 24.087" +
-                        "C -0.671 18.712 -0.181 12.848 3.004 7.994" +
-                        "V 7.993" +
-                        "C 6.202 3.117 11.53 0.207 17.257 0.207" +
-                        "C 19.591 0.207 21.894 0.692 24.1 1.652" +
-                        "L 25.509 2.264" +
-                        "L 25.511 2.265" +
-                        "A 3.46 3.46 0 0 0 28.234 2.265" +
-                        "L 28.236 2.264" +
-                        "L 29.645 1.652" +
-                        "A 17.1 17.1 0 0 1 36.488 0.207" +
-                        "C 41.717 0.207 46.603 2.635 49.85 6.764" +
-                        "Z",
-                "arch" to
-                    "M 50 0" +
-                        "L 72.5 0" +
-                        "A 27.5 27.5 0 0 1 100 27.5" +
-                        "L 100 86.67" +
-                        "A 13.33 13.33 0 0 1 86.67 100" +
-                        "L 13.33 100" +
-                        "A 13.33 13.33 0 0 1 0 86.67" +
-                        "L 0 27.5" +
-                        "A 27.5 27.5 0 0 1 27.5 0" +
-                        "Z",
-                "square" to
-                    "M 50 0" +
-                        "L 83.4 0" +
-                        "A 16.6 16.6 0 0 1 100 16.6" +
-                        "L 100 83.4" +
-                        "A 16.6 16.6 0 0 1 83.4 100" +
-                        "L 16.6 100" +
-                        "A 16.6 16.6 0 0 1 0 83.4" +
-                        "L 0 16.6" +
-                        "A 16.6 16.6 0 0 1 16.6 0" +
-                        "Z",
-            )
-        } else {
-            mapOf("circle" to "M50 0A50 50,0,1,1,50 100A50 50,0,1,1,50 0")
-        }
+    private const val FOLDER_CLOVER_PATH =
+        "M 39.616 4 C 46.224 6.87 53.727 6.87 60.335 4 L 63.884 2.459 C 85.178 -6.789 106.789 14.822 97.541 36.116 L 96 39.665 C 93.13 46.273 93.13 53.776 96 60.384 L 97.541 63.934 C 106.789 85.227 85.178 106.839 63.884 97.591 L 60.335 96.049 C 53.727 93.179 46.224 93.179 39.616 96.049 L 36.066 97.591 C 14.773 106.839 -6.839 85.227 2.409 63.934 L 3.951 60.384 C 6.821 53.776 6.821 46.273 3.951 39.665 L 2.409 36.116 C -6.839 14.822 14.773 -6.789 36.066 2.459 Z"
+    private const val FOLDER_COMPLEX_CLOVER_PATH =
+        "M 49.85 6.764 L 50.013 6.971 L 50.175 6.764 C 53.422 2.635 58.309 0.207 63.538 0.207 C 65.872 0.207 68.175 0.692 70.381 1.648 L 71.79 2.264 L 71.792 2.265 A 3.46 3.46 0 0 0 74.515 2.265 L 74.517 2.264 L 75.926 1.652 A 17.1 17.1 0 0 1 82.769 0.207 C 88.495 0.207 93.824 3.117 97.022 7.989 C 100.21 12.848 100.697 18.712 98.36 24.087 L 97.749 25.496 V 25.497 A 3.45 3.45 0 0 0 97.749 28.222 V 28.223 L 98.36 29.632 C 100.697 35.007 100.207 40.871 97.022 45.73 A 17.5 17.5 0 0 1 93.264 49.838 L 93.06 50 L 93.264 50.162 A 17.5 17.5 0 0 1 97.022 54.27 C 100.21 59.129 100.697 64.993 98.36 70.368 V 71.778 A 3.45 3.45 0 0 0 97.749 74.503 V 74.504 L 98.36 75.913 C 100.697 81.288 100.207 87.152 97.022 92.011 C 93.824 96.883 88.495 99.793 82.769 99.793 C 80.435 99.793 78.132 99.308 75.926 98.348 L 74.517 97.736 H 74.515 A 3.5 3.5 0 0 0 73.153 97.455 C 72.682 97.455 72.225 97.552 71.792 97.736 H 71.79 L 70.381 98.348 A 17.1 17.1 0 0 1 63.538 99.793 C 58.309 99.793 53.422 97.365 50.175 93.236 L 50.013 93.029 L 49.85 93.236 C 46.603 97.365 41.717 99.793 36.488 99.793 C 34.154 99.793 31.851 99.308 29.645 98.348 L 28.236 97.736 H 28.234 A 3.5 3.5 0 0 0 26.872 97.455 C 26.401 97.455 25.944 97.552 25.511 97.736 H 25.509 L 24.1 98.348 A 17.1 17.1 0 0 1 17.257 99.793 C 11.53 99.793 6.202 96.883 3.004 92.011 C -0.181 87.152 -0.671 81.288 1.661 75.913 L 2.277 74.504 V 74.503 A 3.45 3.45 0 0 0 2.277 71.778 V 71.777 L 1.665 70.368 C -0.671 64.993 -0.181 59.129 3.004 54.274 A 17.5 17.5 0 0 1 6.761 50.162 L 6.965 50 L 6.761 49.838 A 17.5 17.5 0 0 1 3.004 45.73 C -0.181 40.871 -0.671 35.007 1.665 29.632 L 2.277 28.223 V 28.222 A 3.45 3.45 0 0 0 2.277 25.497 V 25.496 L 1.665 24.087 C -0.671 18.712 -0.181 12.848 3.004 7.994 V 7.993 C 6.202 3.117 11.53 0.207 17.257 0.207 C 19.591 0.207 21.894 0.692 24.1 1.652 L 25.509 2.264 L 25.511 2.265 A 3.46 3.46 0 0 0 28.234 2.265 L 28.236 2.264 L 29.645 1.652 A 17.1 17.1 0 0 1 36.488 0.207 C 41.717 0.207 46.603 2.635 49.85 6.764 Z"
+    private const val FOLDER_ARCH_PATH =
+        "M 50 0 L 72.5 0 A 27.5 27.5 0 0 1 100 27.5 L 100 86.67 A 13.33 13.33 0 0 1 86.67 100 L 13.33 100 A 13.33 13.33 0 0 1 0 86.67 L 0 27.5 A 27.5 27.5 0 0 1 27.5 0 Z"
+    private const val FOLDER_SQUARE_PATH =
+        "M 50 0 L 83.4 0 A 16.6 16.6 0 0 1 100 16.6 L 100 83.4 A 16.6 16.6 0 0 1 83.4 100 L 16.6 100 A 16.6 16.6 0 0 1 0 83.4 L 0 16.6 A 16.6 16.6 0 0 1 16.6 0 Z"
+    private const val CIRCLE_PATH = "M50 0A50 50,0,1,1,50 100A50 50,0,1,1,50 0"
+    private const val SQUARE_PATH =
+        "M53.689 0.82 L53.689 .82 C67.434 .82 74.306 .82 79.758 2.978 87.649 6.103 93.897 12.351 97.022 20.242 99.18 25.694 99.18 32.566 99.18 46.311 V53.689 C99.18 67.434 99.18 74.306 97.022 79.758 93.897 87.649 87.649 93.897 79.758 97.022 74.306 99.18 67.434 99.18 53.689 99.18 H46.311 C32.566 99.18 25.694 99.18 20.242 97.022 12.351 93.897 6.103 87.649 2.978 79.758 .82 74.306 .82 67.434 .82 53.689 L.82 46.311 C.82 32.566 .82 25.694 2.978 20.242 6.103 12.351 12.351 6.103 20.242 2.978 25.694 .82 32.566 .82 46.311 .82Z"
+    private const val FOUR_SIDED_COOKIE_PATH =
+        "M39.888,4.517C46.338 7.319 53.662 7.319 60.112 4.517L63.605 3C84.733 -6.176 106.176 15.268 97 36.395L95.483 39.888C92.681 46.338 92.681 53.662 95.483 60.112L97 63.605C106.176 84.732 84.733 106.176 63.605 97L60.112 95.483C53.662 92.681 46.338 92.681 39.888 95.483L36.395 97C15.267 106.176 -6.176 84.732 3 63.605L4.517 60.112C7.319 53.662 7.319 46.338 4.517 39.888L3 36.395C -6.176 15.268 15.267 -6.176 36.395 3Z"
+    private const val SEVEN_SIDED_COOKIE_PATH =
+        "M35.209 4.878C36.326 3.895 36.884 3.404 37.397 3.006 44.82 -2.742 55.18 -2.742 62.603 3.006 63.116 3.404 63.674 3.895 64.791 4.878 65.164 5.207 65.351 5.371 65.539 5.529 68.167 7.734 71.303 9.248 74.663 9.932 74.902 9.981 75.147 10.025 75.637 10.113 77.1 10.375 77.831 10.506 78.461 10.66 87.573 12.893 94.032 21.011 94.176 30.412 94.186 31.062 94.151 31.805 94.08 33.293 94.057 33.791 94.045 34.04 94.039 34.285 93.958 37.72 94.732 41.121 96.293 44.18 96.404 44.399 96.522 44.618 96.759 45.056 97.467 46.366 97.821 47.021 98.093 47.611 102.032 56.143 99.727 66.266 92.484 72.24 91.983 72.653 91.381 73.089 90.177 73.961 89.774 74.254 89.572 74.4 89.377 74.548 86.647 76.626 84.477 79.353 83.063 82.483 82.962 82.707 82.865 82.936 82.671 83.395 82.091 84.766 81.8 85.451 81.51 86.033 77.31 94.44 67.977 98.945 58.801 96.994 58.166 96.859 57.451 96.659 56.019 96.259 55.54 96.125 55.3 96.058 55.063 95.998 51.74 95.154 48.26 95.154 44.937 95.998 44.699 96.058 44.46 96.125 43.981 96.259 42.549 96.659 41.834 96.859 41.199 96.994 32.023 98.945 22.69 94.44 18.49 86.033 18.2 85.451 17.909 84.766 17.329 83.395 17.135 82.936 17.038 82.707 16.937 82.483 15.523 79.353 13.353 76.626 10.623 74.548 10.428 74.4 10.226 74.254 9.823 73.961 8.619 73.089 8.017 72.653 7.516 72.24 .273 66.266 -2.032 56.143 1.907 47.611 2.179 47.021 2.533 46.366 3.241 45.056 3.478 44.618 3.596 44.399 3.707 44.18 5.268 41.121 6.042 37.72 5.961 34.285 5.955 34.04 5.943 33.791 5.92 33.293 5.849 31.805 5.814 31.062 5.824 30.412 5.968 21.011 12.427 12.893 21.539 10.66 22.169 10.506 22.9 10.375 24.363 10.113 24.853 10.025 25.098 9.981 25.337 9.932 28.697 9.248 31.833 7.734 34.461 5.529 34.649 5.371 34.836 5.207 35.209 4.878Z"
+    private const val ARCH_PATH =
+        "M50 0C77.614 0 100 22.386 100 50C100 85.471 100 86.476 99.9 87.321 99.116 93.916 93.916 99.116 87.321 99.9 86.476 100 85.471 100 83.46 100H16.54C14.529 100 13.524 100 12.679 99.9 6.084 99.116 .884 93.916 .1 87.321 0 86.476 0 85.471 0 83.46L0 50C0 22.386 22.386 0 50 0Z"
+    @VisibleForTesting const val CIRCLE_KEY = "circle"
+    @VisibleForTesting const val SQUARE_KEY = "square"
+    @VisibleForTesting const val FOUR_SIDED_COOKIE_KEY = "four_sided_cookie"
+    @VisibleForTesting const val SEVEN_SIDED_COOKIE_KEY = "seven_sided_cookie"
+    @VisibleForTesting const val ARCH_KEY = "arch"
 
     val iconShapes =
         if (Flags.newCustomizationPickerUi() && LauncherFlags.enableLauncherIconShapes()) {
-            mapOf(
-                "circle" to
-                    IconShapeModel(
-                        key = "circle",
-                        title = "circle",
-                        pathString = "M50 0A50 50,0,1,1,50 100A50 50,0,1,1,50 0",
-                        folderPathString = folderShapes["clover"]!!,
-                    ),
-                "square" to
-                    IconShapeModel(
-                        key = "square",
-                        title = "square",
-                        pathString =
-                            "M53.689 0.82 L53.689 .82 C67.434 .82 74.306 .82 79.758 2.978 87.649 6.103 93.897 12.351 97.022 20.242 99.18 25.694 99.18 32.566 99.18 46.311 V53.689 C99.18 67.434 99.18 74.306 97.022 79.758 93.897 87.649 87.649 93.897 79.758 97.022 74.306 99.18 67.434 99.18 53.689 99.18 H46.311 C32.566 99.18 25.694 99.18 20.242 97.022 12.351 93.897 6.103 87.649 2.978 79.758 .82 74.306 .82 67.434 .82 53.689 L.82 46.311 C.82 32.566 .82 25.694 2.978 20.242 6.103 12.351 12.351 6.103 20.242 2.978 25.694 .82 32.566 .82 46.311 .82Z",
-                        folderShapes["square"]!!,
-                    ),
-                "four_sided_cookie" to
-                    IconShapeModel(
-                        key = "four_sided_cookie",
-                        title = "4 sided cookie",
-                        pathString =
-                            "M39.888,4.517C46.338 7.319 53.662 7.319 60.112 4.517L63.605 3C84.733 -6.176 106.176 15.268 97 36.395L95.483 39.888C92.681 46.338 92.681 53.662 95.483 60.112L97 63.605C106.176 84.732 84.733 106.176 63.605 97L60.112 95.483C53.662 92.681 46.338 92.681 39.888 95.483L36.395 97C15.267 106.176 -6.176 84.732 3 63.605L4.517 60.112C7.319 53.662 7.319 46.338 4.517 39.888L3 36.395C -6.176 15.268 15.267 -6.176 36.395 3Z",
-                        folderPathString = folderShapes["complexClover"]!!,
-                        iconScale = 72f / 83.4f,
-                    ),
-                "seven_sided_cookie" to
-                    IconShapeModel(
-                        key = "seven_sided_cookie",
-                        title = "7 sided cookie",
-                        pathString =
-                            "M35.209 4.878C36.326 3.895 36.884 3.404 37.397 3.006 44.82 -2.742 55.18 -2.742 62.603 3.006 63.116 3.404 63.674 3.895 64.791 4.878 65.164 5.207 65.351 5.371 65.539 5.529 68.167 7.734 71.303 9.248 74.663 9.932 74.902 9.981 75.147 10.025 75.637 10.113 77.1 10.375 77.831 10.506 78.461 10.66 87.573 12.893 94.032 21.011 94.176 30.412 94.186 31.062 94.151 31.805 94.08 33.293 94.057 33.791 94.045 34.04 94.039 34.285 93.958 37.72 94.732 41.121 96.293 44.18 96.404 44.399 96.522 44.618 96.759 45.056 97.467 46.366 97.821 47.021 98.093 47.611 102.032 56.143 99.727 66.266 92.484 72.24 91.983 72.653 91.381 73.089 90.177 73.961 89.774 74.254 89.572 74.4 89.377 74.548 86.647 76.626 84.477 79.353 83.063 82.483 82.962 82.707 82.865 82.936 82.671 83.395 82.091 84.766 81.8 85.451 81.51 86.033 77.31 94.44 67.977 98.945 58.801 96.994 58.166 96.859 57.451 96.659 56.019 96.259 55.54 96.125 55.3 96.058 55.063 95.998 51.74 95.154 48.26 95.154 44.937 95.998 44.699 96.058 44.46 96.125 43.981 96.259 42.549 96.659 41.834 96.859 41.199 96.994 32.023 98.945 22.69 94.44 18.49 86.033 18.2 85.451 17.909 84.766 17.329 83.395 17.135 82.936 17.038 82.707 16.937 82.483 15.523 79.353 13.353 76.626 10.623 74.548 10.428 74.4 10.226 74.254 9.823 73.961 8.619 73.089 8.017 72.653 7.516 72.24 .273 66.266 -2.032 56.143 1.907 47.611 2.179 47.021 2.533 46.366 3.241 45.056 3.478 44.618 3.596 44.399 3.707 44.18 5.268 41.121 6.042 37.72 5.961 34.285 5.955 34.04 5.943 33.791 5.92 33.293 5.849 31.805 5.814 31.062 5.824 30.412 5.968 21.011 12.427 12.893 21.539 10.66 22.169 10.506 22.9 10.375 24.363 10.113 24.853 10.025 25.098 9.981 25.337 9.932 28.697 9.248 31.833 7.734 34.461 5.529 34.649 5.371 34.836 5.207 35.209 4.878Z",
-                        folderPathString = folderShapes["clover"]!!,
-                        iconScale = 72f / 80f,
-                    ),
-                "arch" to
-                    IconShapeModel(
-                        key = "arch",
-                        title = "arch",
-                        pathString =
-                            "M50 0C77.614 0 100 22.386 100 50C100 85.471 100 86.476 99.9 87.321 99.116 93.916 93.916 99.116 87.321 99.9 86.476 100 85.471 100 83.46 100H16.54C14.529 100 13.524 100 12.679 99.9 6.084 99.116 .884 93.916 .1 87.321 0 86.476 0 85.471 0 83.46L0 50C0 22.386 22.386 0 50 0Z",
-                        folderPathString = folderShapes["arch"]!!,
-                    ),
+            arrayOf(
+                IconShapeModel(
+                    key = CIRCLE_KEY,
+                    title = "circle",
+                    pathString = CIRCLE_PATH,
+                    folderPathString = FOLDER_CLOVER_PATH,
+                ),
+                IconShapeModel(
+                    key = SQUARE_KEY,
+                    title = "square",
+                    pathString = SQUARE_PATH,
+                    folderPathString = FOLDER_SQUARE_PATH,
+                ),
+                IconShapeModel(
+                    key = FOUR_SIDED_COOKIE_KEY,
+                    title = "4 sided cookie",
+                    pathString = FOUR_SIDED_COOKIE_PATH,
+                    folderPathString = FOLDER_COMPLEX_CLOVER_PATH,
+                    iconScale = 72f / 83.4f,
+                ),
+                IconShapeModel(
+                    key = SEVEN_SIDED_COOKIE_KEY,
+                    title = "7 sided cookie",
+                    pathString = SEVEN_SIDED_COOKIE_PATH,
+                    folderPathString = FOLDER_CLOVER_PATH,
+                    iconScale = 72f / 80f,
+                ),
+                IconShapeModel(
+                    key = ARCH_KEY,
+                    title = "arch",
+                    pathString = ARCH_PATH,
+                    folderPathString = FOLDER_ARCH_PATH,
+                ),
             )
         } else {
-            mapOf(
-                "circle" to
-                    IconShapeModel(
-                        key = "circle",
-                        title = "circle",
-                        pathString = "M50 0A50 50,0,1,1,50 100A50 50,0,1,1,50 0",
-                    )
-            )
+            arrayOf(IconShapeModel(key = CIRCLE_KEY, title = "circle", pathString = CIRCLE_PATH))
         }
 }
diff --git a/src/com/android/launcher3/statemanager/BaseState.java b/src/com/android/launcher3/statemanager/BaseState.java
index b7dd2bf..ea54159 100644
--- a/src/com/android/launcher3/statemanager/BaseState.java
+++ b/src/com/android/launcher3/statemanager/BaseState.java
@@ -69,6 +69,13 @@
     }
 
     /**
+     * For this state, whether we should show desktop exploded view in Overview.
+     */
+    default boolean showExplodedDesktopView() {
+        return false;
+    }
+
+    /**
      * For this state, whether fullscreen and desktop quickswitch carousel are detached.
      */
     default boolean detachDesktopCarousel() {
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 943a913..e5105cd 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -214,6 +214,10 @@
                         ENABLE_TASKBAR_NAVBAR_UNIFICATION);
                 return response;
 
+            case TestProtocol.REQUEST_TASKBAR_SHOWN_ON_HOME:
+                response.putBoolean(TEST_INFO_RESPONSE_FIELD,
+                        DisplayController.showLockedTaskbarOnHome(mContext));
+                return response;
             case TestProtocol.REQUEST_NUM_ALL_APPS_COLUMNS:
                 response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
                         mDeviceProfile.numShownAllAppsColumns);
diff --git a/src/com/android/launcher3/util/BaseContext.kt b/src/com/android/launcher3/util/BaseContext.kt
new file mode 100644
index 0000000..8aa10f3
--- /dev/null
+++ b/src/com/android/launcher3/util/BaseContext.kt
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2025 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.content.Context
+import android.view.ContextThemeWrapper
+import android.view.View
+import android.view.View.OnAttachStateChangeListener
+import android.view.ViewTreeObserver.OnWindowFocusChangeListener
+import android.view.ViewTreeObserver.OnWindowVisibilityChangeListener
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.Lifecycle.State.CREATED
+import androidx.lifecycle.Lifecycle.State.DESTROYED
+import androidx.lifecycle.Lifecycle.State.RESUMED
+import androidx.lifecycle.Lifecycle.State.STARTED
+import androidx.lifecycle.LifecycleRegistry
+import androidx.lifecycle.setViewTreeLifecycleOwner
+import androidx.savedstate.SavedStateRegistry
+import androidx.savedstate.SavedStateRegistryController
+import androidx.savedstate.setViewTreeSavedStateRegistryOwner
+import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener
+import com.android.launcher3.Utilities
+import com.android.launcher3.views.ActivityContext
+
+/**
+ * A context wrapper with lifecycle tracking based on the window events on the rootView of the
+ * [ActivityContext]
+ */
+abstract class BaseContext
+@JvmOverloads
+constructor(base: Context, themeResId: Int, private val destroyOnDetach: Boolean = true) :
+    ContextThemeWrapper(base, themeResId), ActivityContext {
+
+    private val listeners = mutableListOf<OnDeviceProfileChangeListener>()
+
+    private val savedStateRegistryController = SavedStateRegistryController.create(this)
+    private val lifecycleRegistry = LifecycleRegistry(this)
+    private val cleanupSet = WeakCleanupSet(this)
+
+    override val savedStateRegistry: SavedStateRegistry
+        get() = savedStateRegistryController.savedStateRegistry
+
+    override val lifecycle: Lifecycle
+        get() = lifecycleRegistry
+
+    private val viewCache = ViewCache()
+
+    init {
+        Executors.MAIN_EXECUTOR.execute {
+            savedStateRegistryController.performAttach()
+            savedStateRegistryController.performRestore(null)
+        }
+    }
+
+    override fun getOnDeviceProfileChangeListeners() = listeners
+
+    private val finishActions = RunnableList()
+
+    /** Called when the root view is created for this context */
+    fun onViewCreated() {
+        val view = rootView
+        val attachListener =
+            object : OnAttachStateChangeListener {
+
+                override fun onViewAttachedToWindow(view: View) {
+                    view.rootView.setViewTreeLifecycleOwner(this@BaseContext)
+                    view.rootView.setViewTreeSavedStateRegistryOwner(this@BaseContext)
+                    lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
+
+                    val treeObserver = view.viewTreeObserver
+
+                    val focusListener = OnWindowFocusChangeListener { updateState() }
+                    treeObserver.addOnWindowFocusChangeListener(focusListener)
+                    finishActions.add {
+                        treeObserver.removeOnWindowFocusChangeListener(focusListener)
+                    }
+
+                    if (Utilities.ATLEAST_V) {
+                        val visibilityListener = OnWindowVisibilityChangeListener { updateState() }
+                        treeObserver.addOnWindowVisibilityChangeListener(visibilityListener)
+                        finishActions.add {
+                            treeObserver.removeOnWindowVisibilityChangeListener(visibilityListener)
+                        }
+                    }
+                }
+
+                override fun onViewDetachedFromWindow(view: View) {
+                    if (destroyOnDetach) onViewDestroyed()
+                }
+            }
+        view.addOnAttachStateChangeListener(attachListener)
+        finishActions.add { view.removeOnAttachStateChangeListener(attachListener) }
+
+        if (view.isAttachedToWindow) attachListener.onViewAttachedToWindow(view)
+        updateState()
+    }
+
+    override fun getViewCache() = viewCache
+
+    override fun getOwnerCleanupSet() = cleanupSet
+
+    private fun updateState() {
+        if (lifecycleRegistry.currentState.isAtLeast(CREATED)) {
+            lifecycleRegistry.currentState =
+                if (rootView.windowVisibility != View.VISIBLE) CREATED
+                else (if (!rootView.hasWindowFocus()) STARTED else RESUMED)
+        }
+    }
+
+    fun onViewDestroyed() {
+        if (
+            !lifecycleRegistry.currentState.isAtLeast(CREATED) &&
+                lifecycleRegistry.currentState != DESTROYED
+        ) {
+            lifecycleRegistry.currentState = CREATED
+        }
+        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+        finishActions.executeAllAndDestroy()
+    }
+}
diff --git a/src/com/android/launcher3/util/DaggerSingletonObject.java b/src/com/android/launcher3/util/DaggerSingletonObject.java
index a245761..a4bd30a 100644
--- a/src/com/android/launcher3/util/DaggerSingletonObject.java
+++ b/src/com/android/launcher3/util/DaggerSingletonObject.java
@@ -24,8 +24,7 @@
 import java.util.function.Function;
 
 /**
- * A class to provide DaggerSingleton objects in a traditional way for
- * {@link MainThreadInitializedObject}.
+ * A class to provide DaggerSingleton objects in a traditional way.
  * We should delete this class at the end and use @Inject to get dagger provided singletons.
  */
 
diff --git a/src/com/android/launcher3/util/DaggerSingletonTracker.java b/src/com/android/launcher3/util/DaggerSingletonTracker.java
index b7a88db..34b3760 100644
--- a/src/com/android/launcher3/util/DaggerSingletonTracker.java
+++ b/src/com/android/launcher3/util/DaggerSingletonTracker.java
@@ -45,7 +45,7 @@
      * Adds the SafeCloseable Singletons to the mLauncherAppSingletons list.
      * This helps to track the singletons and close them appropriately.
      * See {@link DaggerSingletonTracker#close()} and
-     * {@link MainThreadInitializedObject.SandboxContext#onDestroy()}
+     * {@link SandboxContext#onDestroy()}
      */
     public void addCloseable(SafeCloseable closeable) {
         MAIN_EXECUTOR.execute(() -> {
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index ceece4d..52f8887 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -18,6 +18,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
+import static com.android.launcher3.Flags.enableOverviewOnConnectedDisplays;
 import static com.android.launcher3.InvariantDeviceProfile.TYPE_MULTI_DISPLAY;
 import static com.android.launcher3.InvariantDeviceProfile.TYPE_PHONE;
 import static com.android.launcher3.InvariantDeviceProfile.TYPE_TABLET;
@@ -42,9 +43,11 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.SparseArray;
 import android.view.Display;
 
 import androidx.annotation.AnyThread;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 import androidx.annotation.VisibleForTesting;
@@ -63,8 +66,10 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -78,8 +83,7 @@
  */
 @SuppressLint("NewApi")
 @LauncherAppSingleton
-public class DisplayController implements ComponentCallbacks,
-        DesktopVisibilityListener {
+public class DisplayController implements DesktopVisibilityListener {
 
     private static final String TAG = "DisplayController";
     private static final boolean DEBUG = false;
@@ -110,19 +114,18 @@
 
     private final WindowManagerProxy mWMProxy;
 
-    // Null for SDK < S
-    private final Context mWindowContext;
+    private final @ApplicationContext Context mAppContext;
 
     // The callback in this listener updates DeviceProfile, which other listeners might depend on
     private DisplayInfoChangeListener mPriorityListener;
-    private final CopyOnWriteArrayList<DisplayInfoChangeListener> mListeners =
-            new CopyOnWriteArrayList<>();
+
+    private final SparseArray<PerDisplayInfo> mPerDisplayInfo =
+            new SparseArray<>();
 
     // We will register broadcast receiver on main thread to ensure not missing changes on
     // TARGET_OVERLAY_PACKAGE and ACTION_OVERLAY_CHANGED.
     private final SimpleBroadcastReceiver mReceiver;
 
-    private Info mInfo;
     private boolean mDestroyed = false;
 
     @Inject
@@ -130,18 +133,20 @@
             WindowManagerProxy wmProxy,
             LauncherPrefs prefs,
             DaggerSingletonTracker lifecycle) {
+        mAppContext = context;
         mWMProxy = wmProxy;
 
         if (enableTaskbarPinning()) {
             LauncherPrefChangeListener prefListener = key -> {
+                Info info = getInfo();
                 boolean isTaskbarPinningChanged = TASKBAR_PINNING_KEY.equals(key)
-                        && mInfo.mIsTaskbarPinned != prefs.get(TASKBAR_PINNING);
+                        && info.mIsTaskbarPinned != prefs.get(TASKBAR_PINNING);
                 boolean isTaskbarPinningDesktopModeChanged =
                         TASKBAR_PINNING_DESKTOP_MODE_KEY.equals(key)
-                                && mInfo.mIsTaskbarPinnedInDesktopMode != prefs.get(
+                                && info.mIsTaskbarPinnedInDesktopMode != prefs.get(
                                 TASKBAR_PINNING_IN_DESKTOP_MODE);
                 if (isTaskbarPinningChanged || isTaskbarPinningDesktopModeChanged) {
-                    notifyConfigChange();
+                    notifyConfigChange(DEFAULT_DISPLAY);
                 }
             };
 
@@ -151,23 +156,49 @@
                         prefListener, TASKBAR_PINNING, TASKBAR_PINNING_IN_DESKTOP_MODE));
         }
 
-        Display display = context.getSystemService(DisplayManager.class)
-                .getDisplay(DEFAULT_DISPLAY);
-        mWindowContext = context.createWindowContext(display, TYPE_APPLICATION, null);
-        mWindowContext.registerComponentCallbacks(this);
+        DisplayManager displayManager = context.getSystemService(DisplayManager.class);
+        Display defaultDisplay = displayManager.getDisplay(DEFAULT_DISPLAY);
+        PerDisplayInfo defaultPerDisplayInfo = getOrCreatePerDisplayInfo(defaultDisplay);
 
         // Initialize navigation mode change listener
         mReceiver = new SimpleBroadcastReceiver(context, MAIN_EXECUTOR, this::onIntent);
         mReceiver.registerPkgActions(TARGET_OVERLAY_PACKAGE, ACTION_OVERLAY_CHANGED);
 
-        mInfo = new Info(mWindowContext, wmProxy,
-                wmProxy.estimateInternalDisplayBounds(mWindowContext));
         wmProxy.registerDesktopVisibilityListener(this);
-        FileLog.i(TAG, "(CTOR) perDisplayBounds: " + mInfo.mPerDisplayBounds);
+        FileLog.i(TAG, "(CTOR) perDisplayBounds: "
+                + defaultPerDisplayInfo.mInfo.mPerDisplayBounds);
+
+        if (enableOverviewOnConnectedDisplays()) {
+            final DisplayManager.DisplayListener displayListener =
+                    new DisplayManager.DisplayListener() {
+                        @Override
+                        public void onDisplayAdded(int displayId) {
+                            getOrCreatePerDisplayInfo(displayManager.getDisplay(displayId));
+                        }
+
+                        @Override
+                        public void onDisplayChanged(int displayId) {
+                        }
+
+                        @Override
+                        public void onDisplayRemoved(int displayId) {
+                            removePerDisplayInfo(displayId);
+                        }
+                    };
+            displayManager.registerDisplayListener(displayListener, MAIN_EXECUTOR.getHandler());
+            lifecycle.addCloseable(() -> {
+                displayManager.unregisterDisplayListener(displayListener);
+            });
+            // Add any PerDisplayInfos for already-connected displays.
+            Arrays.stream(displayManager.getDisplays())
+                    .forEach((it) ->
+                            getOrCreatePerDisplayInfo(
+                                    displayManager.getDisplay(it.getDisplayId())));
+        }
 
         lifecycle.addCloseable(() -> {
             mDestroyed = true;
-            mWindowContext.unregisterComponentCallbacks(this);
+            defaultPerDisplayInfo.cleanup();
             mReceiver.unregisterReceiverSafely();
             wmProxy.unregisterDesktopVisibilityListener(this);
         });
@@ -236,9 +267,7 @@
 
     @Override
     public void onIsInDesktopModeChanged(int displayId, boolean isInDesktopModeAndNotInOverview) {
-        if (DEFAULT_DISPLAY == displayId) {
-            notifyConfigChange();
-        }
+        notifyConfigChange(displayId);
     }
 
     /**
@@ -261,60 +290,88 @@
         }
         if (ACTION_OVERLAY_CHANGED.equals(intent.getAction())) {
             Log.d(TAG, "Overlay changed, notifying listeners");
-            notifyConfigChange();
+            notifyConfigChange(DEFAULT_DISPLAY);
         }
     }
 
+    @VisibleForTesting
+    public void onConfigurationChanged(Configuration config) {
+        onConfigurationChanged(config, DEFAULT_DISPLAY);
+    }
+
     @UiThread
-    @Override
-    public final void onConfigurationChanged(Configuration config) {
+    private void onConfigurationChanged(Configuration config, int displayId) {
         Log.d(TASKBAR_NOT_DESTROYED_TAG, "DisplayController#onConfigurationChanged: " + config);
-        if (config.densityDpi != mInfo.densityDpi
-                || config.fontScale != mInfo.fontScale
-                || !mInfo.mScreenSizeDp.equals(
-                        new PortraitSize(config.screenHeightDp, config.screenWidthDp))
-                || mWindowContext.getDisplay().getRotation() != mInfo.rotation
-                || mWMProxy.showLockedTaskbarOnHome(mWindowContext)
-                        != mInfo.showLockedTaskbarOnHome()
-                || mWMProxy.showDesktopTaskbarForFreeformDisplay(mWindowContext)
-                        != mInfo.showDesktopTaskbarForFreeformDisplay()) {
-            notifyConfigChange();
+        PerDisplayInfo perDisplayInfo = mPerDisplayInfo.get(displayId);
+        Context windowContext = perDisplayInfo.mWindowContext;
+        Info info = perDisplayInfo.mInfo;
+        if (config.densityDpi != info.densityDpi
+                || config.fontScale != info.fontScale
+                || !info.mScreenSizeDp.equals(
+                    new PortraitSize(config.screenHeightDp, config.screenWidthDp))
+                || windowContext.getDisplay().getRotation() != info.rotation
+                || mWMProxy.showLockedTaskbarOnHome(windowContext)
+                != info.showLockedTaskbarOnHome()
+                || mWMProxy.showDesktopTaskbarForFreeformDisplay(windowContext)
+                != info.showDesktopTaskbarForFreeformDisplay()) {
+            notifyConfigChange(displayId);
         }
     }
 
-    @Override
-    public final void onLowMemory() { }
-
     public void setPriorityListener(DisplayInfoChangeListener listener) {
         mPriorityListener = listener;
     }
 
     public void addChangeListener(DisplayInfoChangeListener listener) {
-        mListeners.add(listener);
+        addChangeListenerForDisplay(listener, DEFAULT_DISPLAY);
     }
 
     public void removeChangeListener(DisplayInfoChangeListener listener) {
-        mListeners.remove(listener);
+        removeChangeListenerForDisplay(listener, DEFAULT_DISPLAY);
+    }
+
+    public void addChangeListenerForDisplay(DisplayInfoChangeListener listener, int displayId) {
+        PerDisplayInfo perDisplayInfo = mPerDisplayInfo.get(displayId);
+        if (perDisplayInfo != null) {
+            perDisplayInfo.addListener(listener);
+        }
+    }
+
+    public void removeChangeListenerForDisplay(DisplayInfoChangeListener listener, int displayId) {
+        PerDisplayInfo perDisplayInfo = mPerDisplayInfo.get(displayId);
+        if (perDisplayInfo != null) {
+            perDisplayInfo.removeListener(listener);
+        }
     }
 
     public Info getInfo() {
-        return mInfo;
+        return mPerDisplayInfo.get(DEFAULT_DISPLAY).mInfo;
+    }
+
+    public @Nullable Info getInfoForDisplay(int displayId) {
+        if (enableOverviewOnConnectedDisplays()) {
+            PerDisplayInfo perDisplayInfo = mPerDisplayInfo.get(displayId);
+            if (perDisplayInfo != null) {
+                return perDisplayInfo.mInfo;
+            } else {
+                return null;
+            }
+        } else {
+            return getInfo();
+        }
     }
 
     @AnyThread
     public void notifyConfigChange() {
-        Info oldInfo = mInfo;
+        notifyConfigChange(DEFAULT_DISPLAY);
+    }
 
-        Context displayInfoContext = mWindowContext;
-        Info newInfo = new Info(displayInfoContext, mWMProxy, oldInfo.mPerDisplayBounds);
+    @AnyThread
+    public void notifyConfigChange(int displayId) {
+        notifyConfigChangeForDisplay(displayId);
+    }
 
-        if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale
-                || newInfo.getNavigationMode() != oldInfo.getNavigationMode()) {
-            // Cache may not be valid anymore, recreate without cache
-            newInfo = new Info(displayInfoContext, mWMProxy,
-                    mWMProxy.estimateInternalDisplayBounds(displayInfoContext));
-        }
-
+    private int calculateChange(Info oldInfo, Info newInfo) {
         int change = 0;
         if (!newInfo.normalizedDisplayInfo.equals(oldInfo.normalizedDisplayInfo)) {
             change |= CHANGE_ACTIVE_SCREEN;
@@ -336,7 +393,7 @@
         }
         if ((newInfo.mIsTaskbarPinned != oldInfo.mIsTaskbarPinned)
                 || (newInfo.mIsTaskbarPinnedInDesktopMode
-                    != oldInfo.mIsTaskbarPinnedInDesktopMode)
+                != oldInfo.mIsTaskbarPinnedInDesktopMode)
                 || newInfo.isPinnedTaskbar() != oldInfo.isPinnedTaskbar()) {
             change |= CHANGE_TASKBAR_PINNING;
         }
@@ -350,23 +407,68 @@
         if (DEBUG) {
             Log.d(TAG, "handleInfoChange - change: " + getChangeFlagsString(change));
         }
+        return change;
+    }
 
-        if (change != 0) {
-            mInfo = newInfo;
-            final int flags = change;
-            MAIN_EXECUTOR.execute(() -> notifyChange(displayInfoContext, flags));
+    private Info getNewInfo(Info oldInfo, Context displayInfoContext) {
+        Info newInfo = new Info(displayInfoContext, mWMProxy, oldInfo.mPerDisplayBounds);
+
+        if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale
+                || newInfo.getNavigationMode() != oldInfo.getNavigationMode()) {
+            // Cache may not be valid anymore, recreate without cache
+            newInfo = new Info(displayInfoContext, mWMProxy,
+                    mWMProxy.estimateInternalDisplayBounds(displayInfoContext));
+        }
+        return newInfo;
+    }
+
+    @AnyThread
+    public void notifyConfigChangeForDisplay(int displayId) {
+        PerDisplayInfo perDisplayInfo = mPerDisplayInfo.get(displayId);
+        if (perDisplayInfo == null) return;
+        Info oldInfo = perDisplayInfo.mInfo;
+        final Info newInfo = getNewInfo(oldInfo, perDisplayInfo.mWindowContext);
+        final int flags = calculateChange(oldInfo, newInfo);
+        if (flags != 0) {
+            MAIN_EXECUTOR.execute(() -> {
+                perDisplayInfo.mInfo = newInfo;
+                if (displayId == DEFAULT_DISPLAY && mPriorityListener != null) {
+                    mPriorityListener.onDisplayInfoChanged(perDisplayInfo.mWindowContext, newInfo,
+                            flags);
+                }
+                perDisplayInfo.notifyListeners(newInfo, flags);
+            });
         }
     }
 
-    private void notifyChange(Context context, int flags) {
-        if (mPriorityListener != null) {
-            mPriorityListener.onDisplayInfoChanged(context, mInfo, flags);
+    private PerDisplayInfo getOrCreatePerDisplayInfo(Display display) {
+        int displayId = display.getDisplayId();
+        PerDisplayInfo perDisplayInfo = mPerDisplayInfo.get(displayId);
+        if (perDisplayInfo != null) {
+            return perDisplayInfo;
         }
+        if (DEBUG) {
+            Log.d(TAG,
+                    String.format("getOrCreatePerDisplayInfo - no cached value found for %d",
+                            displayId));
+        }
+        Context windowContext = mAppContext.createWindowContext(display, TYPE_APPLICATION, null);
+        Info info = new Info(windowContext, mWMProxy,
+                mWMProxy.estimateInternalDisplayBounds(windowContext));
+        perDisplayInfo = new PerDisplayInfo(displayId, windowContext, info);
+        mPerDisplayInfo.put(displayId, perDisplayInfo);
+        return perDisplayInfo;
+    }
 
-        int count = mListeners.size();
-        for (int i = 0; i < count; i++) {
-            mListeners.get(i).onDisplayInfoChanged(context, mInfo, flags);
-        }
+    /**
+     * Clean up resources for the given display id.
+     * @param displayId The display id
+     */
+    void removePerDisplayInfo(int displayId) {
+        PerDisplayInfo info = mPerDisplayInfo.get(displayId);
+        if (info == null) return;
+        info.cleanup();
+        mPerDisplayInfo.remove(displayId);
     }
 
     public static class Info {
@@ -601,21 +703,29 @@
      * Dumps the current state information
      */
     public void dump(PrintWriter pw) {
-        Info info = mInfo;
-        pw.println("DisplayController.Info:");
-        pw.println("  normalizedDisplayInfo=" + info.normalizedDisplayInfo);
-        pw.println("  rotation=" + info.rotation);
-        pw.println("  fontScale=" + info.fontScale);
-        pw.println("  densityDpi=" + info.densityDpi);
-        pw.println("  navigationMode=" + info.getNavigationMode().name());
-        pw.println("  isTaskbarPinned=" + info.mIsTaskbarPinned);
-        pw.println("  isTaskbarPinnedInDesktopMode=" + info.mIsTaskbarPinnedInDesktopMode);
-        pw.println("  isInDesktopMode=" + info.mIsInDesktopMode);
-        pw.println("  showLockedTaskbarOnHome=" + info.showLockedTaskbarOnHome());
-        pw.println("  currentSize=" + info.currentSize);
-        info.mPerDisplayBounds.forEach((key, value) -> pw.println(
-                "  perDisplayBounds - " + key + ": " + value));
-        pw.println("  isTransientTaskbar=" + info.isTransientTaskbar());
+        int count = mPerDisplayInfo.size();
+        for (int i = 0; i < count; ++i) {
+            int displayId = mPerDisplayInfo.keyAt(i);
+            Info info = getInfoForDisplay(displayId);
+            if (info == null) {
+                continue;
+            }
+            pw.println(String.format(Locale.ENGLISH, "DisplayController.Info (displayId=%d):",
+                    displayId));
+            pw.println("  normalizedDisplayInfo=" + info.normalizedDisplayInfo);
+            pw.println("  rotation=" + info.rotation);
+            pw.println("  fontScale=" + info.fontScale);
+            pw.println("  densityDpi=" + info.densityDpi);
+            pw.println("  navigationMode=" + info.getNavigationMode().name());
+            pw.println("  isTaskbarPinned=" + info.mIsTaskbarPinned);
+            pw.println("  isTaskbarPinnedInDesktopMode=" + info.mIsTaskbarPinnedInDesktopMode);
+            pw.println("  isInDesktopMode=" + info.mIsInDesktopMode);
+            pw.println("  showLockedTaskbarOnHome=" + info.showLockedTaskbarOnHome());
+            pw.println("  currentSize=" + info.currentSize);
+            info.mPerDisplayBounds.forEach((key, value) -> pw.println(
+                    "  perDisplayBounds - " + key + ": " + value));
+            pw.println("  isTransientTaskbar=" + info.isTransientTaskbar());
+        }
     }
 
     /**
@@ -643,4 +753,47 @@
         }
     }
 
+    private class PerDisplayInfo implements ComponentCallbacks {
+        final int mDisplayId;
+        final CopyOnWriteArrayList<DisplayInfoChangeListener> mListeners =
+                new CopyOnWriteArrayList<>();
+        final Context mWindowContext;
+        Info mInfo;
+
+        PerDisplayInfo(int displayId, Context windowContext, Info info) {
+            this.mDisplayId = displayId;
+            this.mWindowContext = windowContext;
+            this.mInfo = info;
+            windowContext.registerComponentCallbacks(this);
+        }
+
+        void addListener(DisplayInfoChangeListener listener) {
+            mListeners.add(listener);
+        }
+
+        void removeListener(DisplayInfoChangeListener listener) {
+            mListeners.remove(listener);
+        }
+
+        void notifyListeners(Info info, int flags) {
+            int count = mListeners.size();
+            for (int i = 0; i < count; ++i) {
+                mListeners.get(i).onDisplayInfoChanged(mWindowContext, info, flags);
+            }
+        }
+
+        @Override
+        public void onConfigurationChanged(@NonNull Configuration newConfig) {
+            DisplayController.this.onConfigurationChanged(newConfig, mDisplayId);
+        }
+
+        @Override
+        public void onLowMemory() {}
+
+        void cleanup() {
+            mWindowContext.unregisterComponentCallbacks(this);
+            mListeners.clear();
+        }
+    }
+
 }
diff --git a/src/com/android/launcher3/util/KFloatProperty.kt b/src/com/android/launcher3/util/KFloatProperty.kt
new file mode 100644
index 0000000..5579241
--- /dev/null
+++ b/src/com/android/launcher3/util/KFloatProperty.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2025 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.FloatProperty
+import kotlin.reflect.KMutableProperty1
+
+/** Maps any Kotlin mutable property (var) to [FloatProperty]. */
+class KFloatProperty<T>(private val kProperty: KMutableProperty1<T, Float>) :
+    FloatProperty<T>(kProperty.name) {
+    override fun get(target: T) = kProperty.get(target)
+
+    override fun setValue(target: T, value: Float) {
+        kProperty.set(target, value)
+    }
+}
diff --git a/src/com/android/launcher3/util/LauncherBindableItemsContainer.java b/src/com/android/launcher3/util/LauncherBindableItemsContainer.java
deleted file mode 100644
index 20e3eaf..0000000
--- a/src/com/android/launcher3/util/LauncherBindableItemsContainer.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2021 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.view.View;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.apppairs.AppPairIcon;
-import com.android.launcher3.folder.Folder;
-import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.model.data.AppPairInfo;
-import com.android.launcher3.model.data.FolderInfo;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.views.ActivityContext;
-import com.android.launcher3.widget.PendingAppWidgetHostView;
-
-import java.util.Set;
-
-/**
- * Interface representing a container which can bind Launcher items with some utility methods
- */
-public interface LauncherBindableItemsContainer {
-
-    /**
-     * Called to update workspace items as a result of
-     * {@link com.android.launcher3.model.BgDataModel.Callbacks#bindItemsUpdated(Set)}
-     */
-    default void updateContainerItems(Set<ItemInfo> updates, ActivityContext context) {
-        ItemOperator op = (info, v) -> {
-            if (v instanceof BubbleTextView shortcut
-                    && info instanceof WorkspaceItemInfo wii
-                    && updates.contains(info)) {
-                shortcut.applyFromWorkspaceItem(wii);
-            } else if (info instanceof FolderInfo && v instanceof FolderIcon folderIcon) {
-                folderIcon.updatePreviewItems(updates::contains);
-            } else if (info instanceof AppPairInfo && v instanceof AppPairIcon appPairIcon) {
-                appPairIcon.maybeRedrawForWorkspaceUpdate(updates::contains);
-            } else if (v instanceof PendingAppWidgetHostView pendingView
-                    && updates.contains(info)) {
-                pendingView.applyState();
-                pendingView.postProviderAvailabilityCheck();
-            }
-
-            // Iterate all items
-            return false;
-        };
-
-        mapOverItems(op);
-        Folder openFolder = Folder.getOpen(context);
-        if (openFolder != null) {
-            openFolder.iterateOverItems(op);
-        }
-    }
-
-    /**
-     * Map the operator over the shortcuts and widgets.
-     *
-     * @param op the operator to map over the shortcuts
-     */
-    void mapOverItems(ItemOperator op);
-
-    interface ItemOperator {
-        /**
-         * Process the next itemInfo, possibly with side-effect on the next item.
-         *
-         * @param info info for the shortcut
-         * @param view view for the shortcut
-         * @return true if done, false to continue the map
-         */
-        boolean evaluate(ItemInfo info, View view);
-    }
-}
diff --git a/src/com/android/launcher3/util/LauncherBindableItemsContainer.kt b/src/com/android/launcher3/util/LauncherBindableItemsContainer.kt
new file mode 100644
index 0000000..8fdedef
--- /dev/null
+++ b/src/com/android/launcher3/util/LauncherBindableItemsContainer.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 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.view.View
+import com.android.launcher3.BubbleTextView
+import com.android.launcher3.apppairs.AppPairIcon
+import com.android.launcher3.folder.Folder
+import com.android.launcher3.folder.FolderIcon
+import com.android.launcher3.model.data.AppPairInfo
+import com.android.launcher3.model.data.FolderInfo
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator
+import com.android.launcher3.views.ActivityContext
+import com.android.launcher3.widget.PendingAppWidgetHostView
+import java.util.function.Predicate
+
+/** Interface representing a container which can bind Launcher items with some utility methods */
+interface LauncherBindableItemsContainer {
+
+    /**
+     * Called to update workspace items as a result of {@link
+     * com.android.launcher3.model.BgDataModel.Callbacks#bindItemsUpdated(Set)}
+     */
+    fun updateContainerItems(updates: Set<ItemInfo>, context: ActivityContext) {
+        val op = ItemOperator { info, v ->
+            when {
+                v is BubbleTextView && info is WorkspaceItemInfo && updates.contains(info) ->
+                    v.applyFromWorkspaceItem(info)
+                v is FolderIcon && info is FolderInfo -> v.updatePreviewItems(updates::contains)
+                v is AppPairIcon && info is AppPairInfo ->
+                    v.maybeRedrawForWorkspaceUpdate(updates::contains)
+                v is PendingAppWidgetHostView && updates.contains(info) -> {
+                    v.applyState()
+                    v.postProviderAvailabilityCheck()
+                }
+            }
+
+            // Iterate all items
+            false
+        }
+
+        mapOverItems(op)
+        Folder.getOpen(context)?.mapOverItems(op)
+    }
+
+    /** Returns the first view, matching the [op] */
+    @Deprecated("Use mapOverItems instead", ReplaceWith("mapOverItems(op)"))
+    fun getFirstMatch(op: ItemOperator): View? = mapOverItems(op)
+
+    /** Finds the first icon to match one of the given matchers, from highest to lowest priority. */
+    fun getFirstMatch(vararg matchers: Predicate<ItemInfo>): View? =
+        matchers.firstNotNullOfOrNull { mapOverItems { info, _ -> info != null && it.test(info) } }
+
+    fun getViewByItemId(id: Int): View? = mapOverItems { info, _ -> info != null && info.id == id }
+
+    /**
+     * Map the [op] over the shortcuts and widgets. Once we found the first view which matches, we
+     * will stop the iteration and return that view.
+     */
+    fun mapOverItems(op: ItemOperator): View?
+
+    fun interface ItemOperator {
+
+        /**
+         * Process the next itemInfo, possibly with side-effect on the next item.
+         *
+         * @param info info for the shortcut
+         * @param view view for the shortcut
+         * @return true if done, false to continue the map
+         */
+        fun evaluate(info: ItemInfo?, view: View): Boolean
+    }
+}
diff --git a/src/com/android/launcher3/util/LifecycleHelper.kt b/src/com/android/launcher3/util/LifecycleHelper.kt
new file mode 100644
index 0000000..803adae
--- /dev/null
+++ b/src/com/android/launcher3/util/LifecycleHelper.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2025 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.app.Activity
+import android.os.Bundle
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleRegistry
+import androidx.lifecycle.setViewTreeLifecycleOwner
+import androidx.savedstate.SavedStateRegistryController
+import androidx.savedstate.SavedStateRegistryOwner
+import androidx.savedstate.setViewTreeSavedStateRegistryOwner
+
+/** Utility class for triggering various lifecycle events based on activity callbacks */
+class LifecycleHelper(
+    private val owner: SavedStateRegistryOwner,
+    private val savedStateRegistryController: SavedStateRegistryController,
+    private val lifecycleRegistry: LifecycleRegistry,
+) : ActivityLifecycleCallbacksAdapter {
+
+    override fun onActivityPreCreated(activity: Activity, savedInstanceState: Bundle?) {
+        savedStateRegistryController.performRestore(savedInstanceState)
+    }
+
+    override fun onActivityPostCreated(activity: Activity, savedInstanceState: Bundle?) {
+        activity.window.decorView.setViewTreeLifecycleOwner(owner)
+        activity.window.decorView.setViewTreeSavedStateRegistryOwner(owner)
+        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
+    }
+
+    override fun onActivityPostStarted(activity: Activity) {
+        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
+    }
+
+    override fun onActivityPostResumed(activity: Activity) {
+        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
+    }
+
+    override fun onActivityPrePaused(activity: Activity) {
+        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+    }
+
+    override fun onActivityPreStopped(activity: Activity) {
+        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
+    }
+
+    override fun onActivityPreDestroyed(activity: Activity) {
+        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+    }
+
+    override fun onActivitySaveInstanceState(activity: Activity, bundle: Bundle) {
+        lifecycleRegistry.currentState = Lifecycle.State.CREATED
+        savedStateRegistryController.performSave(bundle)
+    }
+}
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
deleted file mode 100644
index 356a551..0000000
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright (C) 2018 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 static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
-import android.content.Context;
-import android.os.Looper;
-import android.util.Log;
-
-import androidx.annotation.UiThread;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.launcher3.LauncherApplication;
-import com.android.launcher3.util.ResourceBasedOverride.Overrides;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.ExecutionException;
-import java.util.function.Consumer;
-
-/**
- * Utility class for defining singletons which are initiated on main thread.
- *
- * TODO(b/361850561): Do not delete MainThreadInitializedObject until we find a way to
- * unregister and understand how singleton objects are destroyed in dagger graph.
- */
-public class MainThreadInitializedObject<T extends SafeCloseable> {
-
-    private final ObjectProvider<T> mProvider;
-    private T mValue;
-
-    public MainThreadInitializedObject(ObjectProvider<T> provider) {
-        mProvider = provider;
-    }
-
-    public T get(Context context) {
-        Context app = context.getApplicationContext();
-        if (app instanceof ObjectSandbox sc) {
-            return sc.getObject(this);
-        }
-
-        if (mValue == null) {
-            if (Looper.myLooper() == Looper.getMainLooper()) {
-                mValue = TraceHelper.allowIpcs("main.thread.object", () -> mProvider.get(app));
-            } else {
-                try {
-                    return MAIN_EXECUTOR.submit(() -> get(context)).get();
-                } catch (InterruptedException|ExecutionException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-        }
-        return mValue;
-    }
-
-    /**
-     * Executes the callback is the value is already created
-     * @return true if the callback was executed, false otherwise
-     */
-    public boolean executeIfCreated(Consumer<T> callback) {
-        T v = mValue;
-        if (v != null) {
-            callback.accept(v);
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    @VisibleForTesting
-    public void initializeForTesting(T value) {
-        mValue = value;
-    }
-
-    /**
-     * Initializes a provider based on resource overrides
-     */
-    public static <T extends ResourceBasedOverride & SafeCloseable> MainThreadInitializedObject<T>
-            forOverride(Class<T> clazz, int resourceId) {
-        return new MainThreadInitializedObject<>(c -> Overrides.getObject(clazz, c, resourceId));
-    }
-
-    public interface ObjectProvider<T> {
-
-        T get(Context context);
-    }
-
-    /** Sandbox for isolating {@link MainThreadInitializedObject} instances from Launcher. */
-    public interface ObjectSandbox {
-
-        /**
-         * Find a cached object from mObjectMap if we have already created one. If not, generate
-         * an object using the provider.
-         */
-        <T extends SafeCloseable> T getObject(MainThreadInitializedObject<T> object);
-
-
-        /**
-         * Put a value into cache, can be used to put mocked MainThreadInitializedObject
-         * instances.
-         */
-        <T extends SafeCloseable> void putObject(MainThreadInitializedObject<T> object, T value);
-
-        /**
-         * Returns whether this sandbox should cleanup all objects when its destroyed or leave it
-         * to the GC.
-         * These objects can have listeners attached to the system server and mey not be able to get
-         * GCed themselves when running on a device.
-         * Some environments like Robolectric tear down the whole system at the end of the test,
-         * so manual cleanup may not be required.
-         */
-        default boolean shouldCleanUpOnDestroy() {
-            return true;
-        }
-
-        @UiThread
-        default <T extends SafeCloseable> T createObject(MainThreadInitializedObject<T> object) {
-            return object.mProvider.get((Context) this);
-        }
-    }
-
-    /**
-     * Abstract Context which allows custom implementations for
-     * {@link MainThreadInitializedObject} providers
-     */
-    public static class SandboxContext extends LauncherApplication implements ObjectSandbox {
-
-        private static final String TAG = "SandboxContext";
-
-        private final Map<MainThreadInitializedObject, Object> mObjectMap = new HashMap<>();
-        private final ArrayList<SafeCloseable> mOrderedObjects = new ArrayList<>();
-
-        private final Object mDestroyLock = new Object();
-        private boolean mDestroyed = false;
-
-        public SandboxContext(Context base) {
-            attachBaseContext(base);
-        }
-
-        @Override
-        public Context getApplicationContext() {
-            return this;
-        }
-
-        @Override
-        public boolean shouldCleanUpOnDestroy() {
-            return (getBaseContext().getApplicationContext() instanceof ObjectSandbox os)
-                    ? os.shouldCleanUpOnDestroy() : true;
-        }
-
-        public void onDestroy() {
-            if (shouldCleanUpOnDestroy()) {
-                cleanUpObjects();
-            }
-        }
-
-        protected void cleanUpObjects() {
-            getAppComponent().getDaggerSingletonTracker().close();
-            synchronized (mDestroyLock) {
-                // Destroy in reverse order
-                for (int i = mOrderedObjects.size() - 1; i >= 0; i--) {
-                    mOrderedObjects.get(i).close();
-                }
-                mDestroyed = true;
-            }
-        }
-
-        @Override
-        public <T extends SafeCloseable> T getObject(MainThreadInitializedObject<T> object) {
-            synchronized (mDestroyLock) {
-                if (mDestroyed) {
-                    Log.e(TAG, "Static object access with a destroyed context");
-                }
-                T t = (T) mObjectMap.get(object);
-                if (t != null) {
-                    return t;
-                }
-                if (Looper.myLooper() == Looper.getMainLooper()) {
-                    t = createObject(object);
-                    mObjectMap.put(object, t);
-                    mOrderedObjects.add(t);
-                    return t;
-                }
-            }
-
-            try {
-                return MAIN_EXECUTOR.submit(() -> getObject(object)).get();
-            } catch (InterruptedException | ExecutionException e) {
-                throw new RuntimeException(e);
-            }
-        }
-
-        @Override
-        public <T extends SafeCloseable> void putObject(
-                MainThreadInitializedObject<T> object, T value) {
-            mObjectMap.put(object, value);
-        }
-    }
-}
diff --git a/src/com/android/launcher3/util/MultiPropertyDelegate.kt b/src/com/android/launcher3/util/MultiPropertyDelegate.kt
new file mode 100644
index 0000000..837a586
--- /dev/null
+++ b/src/com/android/launcher3/util/MultiPropertyDelegate.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2025 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 kotlin.reflect.KProperty
+
+/** Delegate Kotlin mutable property (var) to a property in [MultiPropertyFactory] */
+class MultiPropertyDelegate(private val property: MultiPropertyFactory<*>.MultiProperty) {
+    constructor(factory: MultiPropertyFactory<*>, enum: Enum<*>) : this(factory[enum.ordinal])
+
+    operator fun getValue(thisRef: Any?, kProperty: KProperty<*>): Float = property.value
+
+    operator fun setValue(thisRef: Any?, kProperty: KProperty<*>, value: Float) {
+        property.value = value
+    }
+}
diff --git a/src/com/android/launcher3/util/SandboxContext.kt b/src/com/android/launcher3/util/SandboxContext.kt
new file mode 100644
index 0000000..c6224e2
--- /dev/null
+++ b/src/com/android/launcher3/util/SandboxContext.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2025 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.content.Context
+import com.android.launcher3.LauncherApplication
+
+/** Abstract Context which allows custom implementations for dagger components. */
+open class SandboxContext(base: Context?) : LauncherApplication() {
+    init {
+        base?.let { attachBaseContext(it) }
+    }
+
+    override fun getApplicationContext(): Context {
+        return this
+    }
+
+    /**
+     * Returns whether this sandbox should cleanup all objects when its destroyed or leave it to the
+     * GC. These objects can have listeners attached to the system server and mey not be able to get
+     * GCed themselves when running on a device. Some environments like Robolectric tear down the
+     * whole system at the end of the test, so manual cleanup may not be required.
+     */
+    open fun shouldCleanUpOnDestroy(): Boolean {
+        return (getBaseContext().getApplicationContext() as? SandboxContext)
+            ?.shouldCleanUpOnDestroy() ?: true
+    }
+
+    fun onDestroy() {
+        if (shouldCleanUpOnDestroy()) {
+            cleanUpObjects()
+        }
+    }
+
+    open protected fun cleanUpObjects() {
+        appComponent.daggerSingletonTracker.close()
+    }
+
+    companion object {
+        private const val TAG = "SandboxContext"
+    }
+}
diff --git a/src/com/android/launcher3/util/WeakCleanupSet.kt b/src/com/android/launcher3/util/WeakCleanupSet.kt
new file mode 100644
index 0000000..7bf3289
--- /dev/null
+++ b/src/com/android/launcher3/util/WeakCleanupSet.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2025 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 androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import java.util.Collections
+import java.util.WeakHashMap
+
+/**
+ * Utility class which maintains a list of cleanup callbacks using weak-references. These callbacks
+ * are called when the [owner] is destroyed, but can also be cleared when the caller is GCed
+ */
+class WeakCleanupSet(owner: LifecycleOwner) {
+
+    private val callbacks = Collections.newSetFromMap<OnOwnerDestroyedCallback>(WeakHashMap())
+    private var destroyed = false
+
+    init {
+        MAIN_EXECUTOR.execute {
+            owner.lifecycle.addObserver(
+                object : DefaultLifecycleObserver {
+
+                    override fun onDestroy(owner: LifecycleOwner) {
+                        destroyed = true
+                        callbacks.forEach { it.onOwnerDestroyed() }
+                    }
+                }
+            )
+        }
+    }
+
+    fun addOnOwnerDestroyedCallback(callback: OnOwnerDestroyedCallback) {
+        if (destroyed) callback.onOwnerDestroyed() else callbacks.add(callback)
+    }
+
+    /** Callback when the owner is destroyed */
+    interface OnOwnerDestroyedCallback {
+        fun onOwnerDestroyed()
+    }
+}
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index 11f0bc2..68e0324 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -520,6 +520,33 @@
          */
         default void onCanCreateDesksChanged(boolean canCreateDesks) {
         }
+
+        /**
+         * Called when a new desk is added.
+         *
+         * @param displayId The ID of the display on which the desk was added.
+         * @param deskId The ID of the newly added desk.
+         */
+        default void onDeskAdded(int displayId, int deskId) {}
+
+        /**
+         * Called when an existing desk is removed.
+         *
+         * @param displayId The ID of the display on which the desk was removed.
+         * @param deskId The ID of the desk that was removed.
+         */
+        default void onDeskRemoved(int displayId, int deskId) {}
+
+        /**
+         * Called when the active desk changes.
+         *
+         * @param displayId The ID of the display on which the desk activation change is happening.
+         * @param newActiveDesk The ID of the new active desk or -1 if no desk is active anymore
+         *                      (i.e. exit desktop mode).
+         * @param oldActiveDesk The ID of the desk that was previously active, or -1 if no desk was
+         *                      active before.
+         */
+        default void onActiveDeskChanged(int displayId, int newActiveDesk, int oldActiveDesk) {}
     }
 
 }
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index 81968fc..cbf5341 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -43,7 +43,6 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.util.Log;
-import android.view.ContextThemeWrapper;
 import android.view.Display;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -57,6 +56,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.core.view.WindowInsetsCompat;
+import androidx.savedstate.SavedStateRegistryOwner;
 
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
@@ -81,11 +81,13 @@
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.util.ApplicationInfoWrapper;
+import com.android.launcher3.util.LauncherBindableItemsContainer;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.ViewCache;
+import com.android.launcher3.util.WeakCleanupSet;
 import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider;
 
 import java.util.List;
@@ -94,7 +96,7 @@
  * An interface to be used along with a context for various activities in Launcher. This allows a
  * generic class to depend on Context subclass instead of an Activity.
  */
-public interface ActivityContext {
+public interface ActivityContext extends SavedStateRegistryOwner {
 
     String TAG = "ActivityContext";
 
@@ -102,10 +104,6 @@
         return false;
     }
 
-    default DotInfo getDotInfoForItem(ItemInfo info) {
-        return null;
-    }
-
     default AccessibilityDelegate getAccessibilityDelegate() {
         return null;
     }
@@ -195,6 +193,14 @@
     }
 
     /**
+     * Returns the primary content of this context
+     */
+    @NonNull
+    default LauncherBindableItemsContainer getContent() {
+        return op -> null;
+    }
+
+    /**
      * The all apps container, if it exists in this context.
      */
     default ActivityAllAppsContainerView<?> getAppsView() {
@@ -225,9 +231,7 @@
         getOnDeviceProfileChangeListeners().remove(listener);
     }
 
-    default ViewCache getViewCache() {
-        return new ViewCache();
-    }
+    ViewCache getViewCache();
 
     /**
      * Controller for supporting item drag-and-drop
@@ -271,11 +275,6 @@
      */
     default void applyOverwritesToLogItem(LauncherAtom.ItemInfo.Builder itemInfoBuilder) { }
 
-    /** Returns {@code true} if items are currently being bound within this context. */
-    default boolean isBindingItems() {
-        return false;
-    }
-
     default View.OnClickListener getItemOnClickListener() {
         return v -> {
             // No op.
@@ -287,9 +286,13 @@
         return v -> false;
     }
 
-    @Nullable
+    @NonNull
     default PopupDataProvider getPopupDataProvider() {
-        return null;
+        return new PopupDataProvider(this);
+    }
+
+    default DotInfo getDotInfoForItem(ItemInfo info) {
+        return getPopupDataProvider().getDotInfoForItem(info);
     }
 
     /**
@@ -518,6 +521,9 @@
         return new CellPosMapper(dp.isVerticalBarLayout(), dp.numShownHotseatIcons);
     }
 
+    /** Set to manage objects that can be cleaned up along with the context */
+    WeakCleanupSet getOwnerCleanupSet();
+
     /** Whether bubbles are enabled. */
     default boolean isBubbleBarEnabled() {
         return false;
@@ -552,21 +558,10 @@
     static <T extends Context & ActivityContext> T lookupContextNoThrow(Context context) {
         if (context instanceof ActivityContext) {
             return (T) context;
-        } else if (context instanceof ActivityContextDelegate acd) {
-            return (T) acd.mDelegate;
-        } else if (context instanceof ContextWrapper) {
-            return lookupContextNoThrow(((ContextWrapper) context).getBaseContext());
+        } else if (context instanceof ContextWrapper cw) {
+            return lookupContextNoThrow(cw.getBaseContext());
         } else {
             return null;
         }
     }
-
-    class ActivityContextDelegate extends ContextThemeWrapper {
-        public final ActivityContext mDelegate;
-
-        public ActivityContextDelegate(Context base, int themeResId, ActivityContext delegate) {
-            super(base, themeResId);
-            mDelegate = delegate;
-        }
-    }
 }
diff --git a/src/com/android/launcher3/views/ComposeInitializer.java b/src/com/android/launcher3/views/ComposeInitializer.java
deleted file mode 100644
index 0929885..0000000
--- a/src/com/android/launcher3/views/ComposeInitializer.java
+++ /dev/null
@@ -1,229 +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.launcher3.views;
-
-import android.os.Build;
-import android.view.View;
-import android.view.ViewParent;
-import android.view.ViewTreeObserver;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RequiresApi;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
-import androidx.lifecycle.ViewTreeLifecycleOwner;
-import androidx.savedstate.SavedStateRegistry;
-import androidx.savedstate.SavedStateRegistryController;
-import androidx.savedstate.SavedStateRegistryOwner;
-import androidx.savedstate.ViewTreeSavedStateRegistryOwner;
-
-import com.android.launcher3.Utilities;
-
-/**
- * An initializer to use Compose for classes implementing {@code ActivityContext}. This allows
- * adding ComposeView to ViewTree outside a {@link androidx.activity.ComponentActivity}.
- */
-public final class ComposeInitializer {
-    /**
-     * Performs the initialization to use Compose in the ViewTree of {@code target}.
-     */
-    public static void initCompose(ActivityContext target) {
-        getContentChild(target).addOnAttachStateChangeListener(
-                new View.OnAttachStateChangeListener() {
-
-                    @Override
-                    public void onViewAttachedToWindow(View v) {
-                        ComposeInitializer.onAttachedToWindow(v);
-                    }
-
-                    @Override
-                    public void onViewDetachedFromWindow(View v) {
-                        ComposeInitializer.onDetachedFromWindow(v);
-                    }
-                });
-    }
-
-    /**
-     * Find the "content child" for {@code target}.
-     *
-     * @see "WindowRecomposer.android.kt: [View.contentChild]"
-     */
-    private static View getContentChild(ActivityContext target) {
-        View self = target.getDragLayer();
-        ViewParent parent = self.getParent();
-        while (parent instanceof View parentView) {
-            if (parentView.getId() == android.R.id.content) return self;
-            self = parentView;
-            parent = self.getParent();
-        }
-        return self;
-    }
-
-    /**
-     * Function to be called on your window root view's [View.onAttachedToWindow] function.
-     */
-    private static void onAttachedToWindow(View root) {
-        if (ViewTreeLifecycleOwner.get(root) != null) {
-            throw new IllegalStateException(
-                    "View " + root + " already has a LifecycleOwner");
-        }
-
-        ViewParent parent = root.getParent();
-        if (parent instanceof View && ((View) parent).getId() != android.R.id.content) {
-            throw new IllegalStateException(
-                    "ComposeInitializer.onContentChildAttachedToWindow(View) must be called on "
-                            + "the content child. Outside of activities and dialogs, this is "
-                            + "usually the top-most View of a window.");
-        }
-
-        // The lifecycle owner, which is STARTED when [root] is visible and RESUMED when [root]
-        // is both visible and focused.
-        ViewLifecycleOwner lifecycleOwner = new ViewLifecycleOwner(root);
-
-        // We must call [ViewLifecycleOwner.onCreate] after creating the
-        // [SavedStateRegistryOwner] because `onCreate` might move the lifecycle state to STARTED
-        // which will make [SavedStateRegistryController.performRestore] throw.
-        lifecycleOwner.onCreate();
-
-        // Set the owners on the root. They will be reused by any ComposeView inside the root
-        // hierarchy.
-        ViewTreeLifecycleOwner.set(root, lifecycleOwner);
-        ViewTreeSavedStateRegistryOwner.set(root, lifecycleOwner);
-    }
-
-    /**
-     * Function to be called on your window root view's [View.onDetachedFromWindow] function.
-     */
-    private static void onDetachedFromWindow(View root) {
-        final LifecycleOwner lifecycleOwner = ViewTreeLifecycleOwner.get(root);
-        if (lifecycleOwner != null) {
-            ((ViewLifecycleOwner) lifecycleOwner).onDestroy();
-        }
-        ViewTreeLifecycleOwner.set(root, null);
-        ViewTreeSavedStateRegistryOwner.set(root, null);
-    }
-
-    /**
-     * A [LifecycleOwner] for a [View] that updates lifecycle state based on window state.
-     *
-     * Also a trivial implementation of [SavedStateRegistryOwner] that does not do any save or
-     * restore. This works for processes similar to the SystemUI process, which is always running
-     * and top-level windows using this initialization are created once, when the process is
-     * started.
-     *
-     * The implementation requires the caller to call [onCreate] and [onDestroy] when the view is
-     * attached to or detached from a view hierarchy. After [onCreate] and before [onDestroy] is
-     * called, the implementation monitors window state in the following way
-     * * If the window is not visible, we are in the [Lifecycle.State.CREATED] state
-     * * If the window is visible but not focused, we are in the [Lifecycle.State.STARTED] state
-     * * If the window is visible and focused, we are in the [Lifecycle.State.RESUMED] state
-     *
-     * Or in table format:
-     * ```
-     * ┌───────────────┬───────────────────┬──────────────┬─────────────────┐
-     * │ View attached │ Window Visibility │ Window Focus │ Lifecycle State │
-     * ├───────────────┼───────────────────┴──────────────┼─────────────────┤
-     * │ Not attached  │                 Any              │       N/A       │
-     * ├───────────────┼───────────────────┬──────────────┼─────────────────┤
-     * │               │    Not visible    │     Any      │     CREATED     │
-     * │               ├───────────────────┼──────────────┼─────────────────┤
-     * │   Attached    │                   │   No focus   │     STARTED     │
-     * │               │      Visible      ├──────────────┼─────────────────┤
-     * │               │                   │  Has focus   │     RESUMED     │
-     * └───────────────┴───────────────────┴──────────────┴─────────────────┘
-     * ```
-     */
-    private static class ViewLifecycleOwner implements SavedStateRegistryOwner {
-        private final ViewTreeObserver.OnWindowFocusChangeListener mWindowFocusListener =
-                hasFocus -> updateState();
-        private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
-
-        private final SavedStateRegistryController mSavedStateRegistryController =
-                SavedStateRegistryController.create(this);
-
-        private final View mView;
-        private final Api34Impl mApi34Impl;
-
-        ViewLifecycleOwner(View view) {
-            mView = view;
-            if (Utilities.ATLEAST_U) {
-                mApi34Impl = new Api34Impl();
-            } else {
-                mApi34Impl = null;
-            }
-
-            mSavedStateRegistryController.performRestore(null);
-        }
-
-        @NonNull
-        @Override
-        public Lifecycle getLifecycle() {
-            return mLifecycleRegistry;
-        }
-
-        @NonNull
-        @Override
-        public SavedStateRegistry getSavedStateRegistry() {
-            return mSavedStateRegistryController.getSavedStateRegistry();
-        }
-
-        void onCreate() {
-            mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED);
-            if (Utilities.ATLEAST_U) {
-                mApi34Impl.addOnWindowVisibilityChangeListener();
-            }
-            mView.getViewTreeObserver().addOnWindowFocusChangeListener(
-                    mWindowFocusListener);
-            updateState();
-        }
-
-        void onDestroy() {
-            if (Utilities.ATLEAST_U) {
-                mApi34Impl.removeOnWindowVisibilityChangeListener();
-            }
-            mView.getViewTreeObserver().removeOnWindowFocusChangeListener(
-                    mWindowFocusListener);
-            mLifecycleRegistry.setCurrentState(Lifecycle.State.DESTROYED);
-        }
-
-        private void updateState() {
-            Lifecycle.State state =
-                    mView.getWindowVisibility() != View.VISIBLE ? Lifecycle.State.CREATED
-                            : (!mView.hasWindowFocus() ? Lifecycle.State.STARTED
-                                    : Lifecycle.State.RESUMED);
-            mLifecycleRegistry.setCurrentState(state);
-        }
-
-        @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
-        private class Api34Impl {
-            private final ViewTreeObserver.OnWindowVisibilityChangeListener
-                    mWindowVisibilityListener =
-                    visibility -> updateState();
-
-            void addOnWindowVisibilityChangeListener() {
-                mView.getViewTreeObserver().addOnWindowVisibilityChangeListener(
-                        mWindowVisibilityListener);
-            }
-
-            void removeOnWindowVisibilityChangeListener() {
-                mView.getViewTreeObserver().removeOnWindowVisibilityChangeListener(
-                        mWindowVisibilityListener);
-            }
-        }
-    }
-}
diff --git a/src/com/android/launcher3/views/FloatingSurfaceView.java b/src/com/android/launcher3/views/FloatingSurfaceView.java
index 5f8e2c0..a4055b6 100644
--- a/src/com/android/launcher3/views/FloatingSurfaceView.java
+++ b/src/com/android/launcher3/views/FloatingSurfaceView.java
@@ -159,9 +159,8 @@
         if (mContract == null) {
             return;
         }
-        View icon = mLauncher.getFirstMatchForAppClose(null /* StableViewInfo */,
-                mContract.componentName.getPackageName(), mContract.user,
-                false /* supportsAllAppsState */);
+        View icon = mLauncher.getFirstHomeElementForAppClose(null /* StableViewInfo */,
+                mContract.componentName.getPackageName(), mContract.user);
 
         boolean iconChanged = mIcon != icon;
         if (iconChanged) {
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index fda5175..af31276 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -16,7 +16,6 @@
 package com.android.launcher3.widget;
 
 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;
@@ -150,40 +149,36 @@
             return;
         }
 
-        if (enableWidgetTapToAdd()) {
-            scrollToWidgetCell(wc);
+        scrollToWidgetCell(wc);
 
-            if (mWidgetCellWithAddButton != null) {
-                if (mWidgetCellWithAddButton.isShowingAddButton()) {
-                    // If there is a add button currently showing, hide it.
-                    mWidgetCellWithAddButton.hideAddButton(/* animate= */ true);
-                } else {
-                    // The last recorded widget cell to show an add button is no longer showing it,
-                    // likely because the widget cell has been recycled or lost focus. If this is
-                    // the cell that has been clicked, we will show it below.
-                    mWidgetCellWithAddButton = null;
-                }
-            }
-
-            if (mWidgetCellWithAddButton != wc) {
-                // If click is on a cell not showing an add button, show it now.
-                final PendingAddItemInfo info = (PendingAddItemInfo) wc.getTag();
-                if (mActivityContext instanceof Launcher) {
-                    wc.showAddButton((view) -> addWidget(info));
-                } else {
-                    wc.showAddButton((view) -> mActivityContext.getItemOnClickListener()
-                            .onClick(wc));
-                }
-            }
-
-            mWidgetCellWithAddButton = mWidgetCellWithAddButton != wc ? wc : null;
-            if (mWidgetCellWithAddButton != null) {
-                mLastSelectedWidgetItem = mWidgetCellWithAddButton.getWidgetItem();
+        if (mWidgetCellWithAddButton != null) {
+            if (mWidgetCellWithAddButton.isShowingAddButton()) {
+                // If there is a add button currently showing, hide it.
+                mWidgetCellWithAddButton.hideAddButton(/* animate= */ true);
             } else {
-                mLastSelectedWidgetItem = null;
+                // The last recorded widget cell to show an add button is no longer showing it,
+                // likely because the widget cell has been recycled or lost focus. If this is
+                // the cell that has been clicked, we will show it below.
+                mWidgetCellWithAddButton = null;
             }
+        }
+
+        if (mWidgetCellWithAddButton != wc) {
+            // If click is on a cell not showing an add button, show it now.
+            final PendingAddItemInfo info = (PendingAddItemInfo) wc.getTag();
+            if (mActivityContext instanceof Launcher) {
+                wc.showAddButton((view) -> addWidget(info));
+            } else {
+                wc.showAddButton((view) -> mActivityContext.getItemOnClickListener()
+                        .onClick(wc));
+            }
+        }
+
+        mWidgetCellWithAddButton = mWidgetCellWithAddButton != wc ? wc : null;
+        if (mWidgetCellWithAddButton != null) {
+            mLastSelectedWidgetItem = mWidgetCellWithAddButton.getWidgetItem();
         } else {
-            mActivityContext.getItemOnClickListener().onClick(wc);
+            mLastSelectedWidgetItem = null;
         }
     }
 
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index b07d807..7a44c6a 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -79,7 +79,8 @@
     private Runnable mAutoAdvanceRunnable;
 
     private long mDeferUpdatesUntilMillis = 0;
-    RemoteViews mLastRemoteViews;
+    private RemoteViews mLastRemoteViews;
+    private boolean mReapplyOnResumeUpdates = false;
 
     private boolean mTrackingWidgetUpdate = false;
 
@@ -138,11 +139,11 @@
                     TRACE_METHOD_NAME + getAppWidgetInfo().provider, getAppWidgetId());
             mTrackingWidgetUpdate = false;
         }
-        if (isDeferringUpdates()) {
-            mLastRemoteViews = remoteViews;
+        mLastRemoteViews = remoteViews;
+        mReapplyOnResumeUpdates = isDeferringUpdates();
+        if (mReapplyOnResumeUpdates) {
             return;
         }
-        mLastRemoteViews = null;
 
         super.updateAppWidget(remoteViews);
 
@@ -150,6 +151,18 @@
         checkIfAutoAdvance();
     }
 
+    @Override
+    public void onViewAdded(View child) {
+        super.onViewAdded(child);
+        mReapplyOnResumeUpdates |= isDeferringUpdates();
+    }
+
+    @Override
+    public void onViewRemoved(View child) {
+        super.onViewRemoved(child);
+        mReapplyOnResumeUpdates |= isDeferringUpdates();
+    }
+
     private boolean checkScrollableRecursively(ViewGroup viewGroup) {
         if (viewGroup instanceof AdapterView) {
             return true;
@@ -204,18 +217,16 @@
      * {@link #updateAppWidget} and apply any deferred updates.
      */
     public void endDeferringUpdates() {
-        RemoteViews remoteViews;
         mDeferUpdatesUntilMillis = 0;
-        remoteViews = mLastRemoteViews;
-
-        if (remoteViews != null) {
-            updateAppWidget(remoteViews);
+        if (mReapplyOnResumeUpdates) {
+            updateAppWidget(mLastRemoteViews);
         }
     }
 
+    @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            BaseDragLayer dragLayer = mActivityContext.getDragLayer();
+            BaseDragLayer<?> dragLayer = mActivityContext.getDragLayer();
             if (mIsScrollable) {
                 dragLayer.requestDisallowInterceptTouchEvent(true);
             }
@@ -225,6 +236,7 @@
         return mLongPressHelper.hasPerformedLongPress();
     }
 
+    @Override
     public boolean onTouchEvent(MotionEvent ev) {
         mLongPressHelper.onTouchEvent(ev);
         // We want to keep receiving though events to be able to cancel long press on ACTION_UP
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 7a27bf4..130843b 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -18,7 +18,6 @@
 
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
 
-import static com.android.launcher3.Flags.enableWidgetTapToAdd;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
 import static com.android.launcher3.widget.util.WidgetSizes.getWidgetItemSizePx;
 
@@ -152,24 +151,22 @@
         mWidgetTextContainer = findViewById(R.id.widget_text_container);
         mWidgetAddButton = findViewById(R.id.widget_add_button);
 
-        if (enableWidgetTapToAdd()) {
-            setAccessibilityDelegate(new AccessibilityDelegate() {
-                @Override
-                public void onInitializeAccessibilityNodeInfo(View host,
-                        AccessibilityNodeInfo info) {
-                    super.onInitializeAccessibilityNodeInfo(host, info);
-                    if (hasOnClickListeners()) {
-                        String accessibilityLabel = getResources().getString(
-                                mWidgetAddButton.isShown()
-                                        ? R.string.widget_cell_tap_to_hide_add_button_label
-                                        : R.string.widget_cell_tap_to_show_add_button_label);
-                        info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK,
-                                accessibilityLabel));
-                    }
+        setAccessibilityDelegate(new AccessibilityDelegate() {
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host,
+                    AccessibilityNodeInfo info) {
+                super.onInitializeAccessibilityNodeInfo(host, info);
+                if (hasOnClickListeners()) {
+                    String accessibilityLabel = getResources().getString(
+                            mWidgetAddButton.isShown()
+                                    ? R.string.widget_cell_tap_to_hide_add_button_label
+                                    : R.string.widget_cell_tap_to_show_add_button_label);
+                    info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK,
+                            accessibilityLabel));
                 }
-            });
-            mWidgetAddButton.setVisibility(INVISIBLE);
-        }
+            }
+        });
+        mWidgetAddButton.setVisibility(INVISIBLE);
     }
 
     public void setRemoteViewsPreview(RemoteViews view) {
@@ -210,9 +207,7 @@
         showDescription(true);
         showDimensions(true);
 
-        if (enableWidgetTapToAdd()) {
-            hideAddButton(/* animate= */ false);
-        }
+        hideAddButton(/* animate= */ false);
 
         if (mActiveRequest != null) {
             mActiveRequest.cancel();
diff --git a/src/com/android/launcher3/widget/model/WidgetListSpaceEntry.java b/src/com/android/launcher3/widget/model/WidgetListSpaceEntry.java
index 5b1da5b..a761ecd 100644
--- a/src/com/android/launcher3/widget/model/WidgetListSpaceEntry.java
+++ b/src/com/android/launcher3/widget/model/WidgetListSpaceEntry.java
@@ -33,4 +33,9 @@
                 Collections.EMPTY_LIST);
         mPkgItem.title = "";
     }
+
+    @Override
+    public WidgetsListBaseEntry copy() {
+        return new WidgetListSpaceEntry();
+    }
 }
diff --git a/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java b/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java
index 0003b76..9246e45 100644
--- a/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java
+++ b/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java
@@ -43,4 +43,7 @@
         this.mWidgets =
                 items.stream().sorted(new WidgetItemComparator()).collect(Collectors.toList());
     }
+
+
+    public abstract WidgetsListBaseEntry copy();
 }
diff --git a/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java b/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java
index d709196..cc1739f 100644
--- a/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java
+++ b/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java
@@ -58,6 +58,11 @@
     }
 
     @Override
+    public WidgetsListBaseEntry copy() {
+        return new WidgetsListContentEntry(mPkgItem, mTitleSectionName, mWidgets, mMaxSpanSize);
+    }
+
+    @Override
     public String toString() {
         return "Content:" + mPkgItem.packageName + ":" + mWidgets.size() + " maxSpanSize: "
                 + mMaxSpanSize;
diff --git a/src/com/android/launcher3/widget/model/WidgetsListExpandActionEntry.java b/src/com/android/launcher3/widget/model/WidgetsListExpandActionEntry.java
index 8c84030..7519bb7 100644
--- a/src/com/android/launcher3/widget/model/WidgetsListExpandActionEntry.java
+++ b/src/com/android/launcher3/widget/model/WidgetsListExpandActionEntry.java
@@ -35,4 +35,9 @@
                 /*items=*/ Collections.EMPTY_LIST);
         mPkgItem.title = "";
     }
+
+    @Override
+    public WidgetsListBaseEntry copy() {
+        return new WidgetsListExpandActionEntry();
+    }
 }
diff --git a/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java b/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java
index 0d775c3..e2ea068 100644
--- a/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java
+++ b/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java
@@ -83,6 +83,12 @@
         mIsWidgetListShown = isWidgetListShown;
     }
 
+    @Override
+    public WidgetsListBaseEntry copy() {
+        return new WidgetsListHeaderEntry(mPkgItem, mTitleSectionName, mWidgets,
+                mVisibleWidgetsCount, mIsSearchEntry, mIsWidgetListShown);
+    }
+
     /** Returns {@code true} if the widgets list associated with this header is shown. */
     public boolean isWidgetListShown() {
         return mIsWidgetListShown;
diff --git a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
index 4ccf16b..ec91622 100644
--- a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.widget.picker;
 
+import static com.android.launcher3.widget.picker.WidgetRecommendationCategory.DEFAULT_WIDGET_RECOMMENDATION_CATEGORY;
 import static com.android.launcher3.widget.util.WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering;
 
 import android.content.ComponentName;
@@ -55,6 +56,7 @@
 public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots> {
     private @Px float mAvailableHeight = Float.MAX_VALUE;
     private @Px float mAvailableWidth = 0;
+    private int mLastUiMode = -1;
     private static final String INITIALLY_DISPLAYED_WIDGETS_STATE_KEY =
             "widgetRecommendationsView:mDisplayedWidgets";
     private static final int MAX_CATEGORIES = 3;
@@ -151,41 +153,7 @@
         mPageSwitchListeners.add(pageChangeListener);
     }
 
-    /**
-     * Displays all the provided recommendations in a single table if they fit.
-     *
-     * @param recommendedWidgets list of widgets to be displayed in recommendation section.
-     * @param deviceProfile      the current {@link DeviceProfile}
-     * @param availableHeight    height in px that can be used to display the recommendations;
-     *                           recommendations that don't fit in this height won't be shown
-     * @param availableWidth     width in px that the recommendations should display in
-     * @param cellPadding        padding in px that should be applied to each widget in the
-     *                           recommendations
-     * @return number of recommendations that could fit in the available space.
-     */
-    public int setRecommendations(
-            List<WidgetItem> recommendedWidgets, DeviceProfile deviceProfile,
-            final @Px float availableHeight, final @Px int availableWidth,
-            final @Px int cellPadding) {
-        this.mAvailableHeight = availableHeight;
-        this.mAvailableWidth = availableWidth;
-        clear();
-
-        Set<ComponentName> displayedWidgets = maybeDisplayInTable(recommendedWidgets,
-                deviceProfile,
-                availableWidth, cellPadding);
-
-        if (mDisplayedWidgets.isEmpty()) {
-            // Save the widgets shown for the first time user opened the picker; so that, they can
-            // be maintained across orientation changes.
-            mDisplayedWidgets = displayedWidgets;
-        }
-
-        updateTitleAndIndicator(/* requestedPage= */ 0);
-        return displayedWidgets.size();
-    }
-
-    private boolean shouldShowFullPageView(
+    private boolean shouldShowSinglePageView(
             Map<WidgetRecommendationCategory, List<WidgetItem>> recommendations) {
         if (mShowFullPageViewIfLowDensity) {
             boolean hasLessCategories = recommendations.size() < MAX_CATEGORIES;
@@ -213,63 +181,82 @@
      * @param cellPadding     padding in px that should be applied to each widget in the
      *                        recommendations
      * @param requestedPage   page number to display initially.
+     * @param forceUpdate     whether to re-render even if available space didn't change
      * @return number of recommendations that could fit in the available space.
      */
     public int setRecommendations(
             Map<WidgetRecommendationCategory, List<WidgetItem>> recommendations,
             DeviceProfile deviceProfile, final @Px float availableHeight,
-            final @Px int availableWidth, final @Px int cellPadding, final int requestedPage) {
-        if (shouldShowFullPageView(recommendations)) {
-            // Show all widgets in single page with unlimited available height.
-            return setRecommendations(
-                    recommendations.values().stream().flatMap(Collection::stream)
-                            .collect(Collectors.toList()),
-                    deviceProfile, /*availableHeight=*/ Float.MAX_VALUE, availableWidth,
-                    cellPadding);
+            final @Px int availableWidth, final @Px int cellPadding, final int requestedPage,
+            final boolean forceUpdate) {
+        if (forceUpdate || shouldUpdate(availableWidth, availableHeight)) {
+            Context context = getContext();
+            this.mAvailableHeight = availableHeight;
+            this.mAvailableWidth = availableWidth;
+            this.mLastUiMode = context.getResources().getConfiguration().uiMode;
 
-        }
-        this.mAvailableHeight = availableHeight;
-        this.mAvailableWidth = availableWidth;
-        Context context = getContext();
-        // For purpose of recommendations section, we don't want paging dots to be halved in two
-        // pane display, so, we always provide isTwoPanels = "false".
-        mPageIndicator.setPauseScroll(/*pause=*/true, /*isTwoPanels=*/ false);
-        clear();
-
-        int displayedCategories = 0;
-        Set<ComponentName> allDisplayedWidgets = new HashSet<>();
-
-        // Render top MAX_CATEGORIES in separate tables. Each table becomes a page.
-        for (Map.Entry<WidgetRecommendationCategory, List<WidgetItem>> entry :
-                new TreeMap<>(recommendations).entrySet()) {
-            // If none of the recommendations for the category could fit in the mAvailableHeight, we
-            // don't want to add that category; and we look for the next one.
-            Set<ComponentName> displayedWidgetsForCategory = maybeDisplayInTable(entry.getValue(),
-                    deviceProfile,
-                    availableWidth, cellPadding);
-            if (!displayedWidgetsForCategory.isEmpty()) {
-                mCategoryTitles.add(
-                        context.getResources().getString(entry.getKey().categoryTitleRes));
-                displayedCategories++;
-                allDisplayedWidgets.addAll(displayedWidgetsForCategory);
+            final Map<WidgetRecommendationCategory, List<WidgetItem>> mappedRecommendations;
+            if (shouldShowSinglePageView(recommendations)) { // map to single category.
+                mappedRecommendations = Map.of(DEFAULT_WIDGET_RECOMMENDATION_CATEGORY,
+                        recommendations.values().stream().flatMap(
+                                Collection::stream).toList());
+            } else {
+                mappedRecommendations = recommendations;
             }
 
-            if (displayedCategories == MAX_CATEGORIES) {
-                break;
+            // For purpose of recommendations section, we don't want paging dots to be halved in two
+            // pane display, so, we always provide isTwoPanels = "false".
+            mPageIndicator.setPauseScroll(/*pause=*/true, /*isTwoPanels=*/ false);
+            clear();
+
+            int displayedCategories = 0;
+            Set<ComponentName> allDisplayedWidgets = new HashSet<>();
+
+            // Render top MAX_CATEGORIES in separate tables. Each table becomes a page.
+            for (Map.Entry<WidgetRecommendationCategory, List<WidgetItem>> entry :
+                    new TreeMap<>(mappedRecommendations).entrySet()) {
+                // If none of the recommendations for the category could fit in the
+                // mAvailableHeight, we don't want to add that category; and we look for the next
+                // one.
+                Set<ComponentName> displayedWidgetsForCategory = maybeDisplayInTable(
+                        entry.getValue(),
+                        deviceProfile,
+                        availableWidth, cellPadding);
+                if (!displayedWidgetsForCategory.isEmpty()) {
+                    mCategoryTitles.add(
+                            context.getResources().getString(entry.getKey().categoryTitleRes));
+                    displayedCategories++;
+                    allDisplayedWidgets.addAll(displayedWidgetsForCategory);
+                }
+
+                if (displayedCategories == MAX_CATEGORIES) {
+                    break;
+                }
             }
-        }
 
-        if (mDisplayedWidgets.isEmpty()) {
-            // Save the widgets shown for the first time user opened the picker; so that, they can
-            // be maintained across orientation changes.
-            mDisplayedWidgets = allDisplayedWidgets;
-        }
+            if (mDisplayedWidgets.isEmpty()) {
+                // Save the widgets shown for the first time user opened the picker; so that,
+                // they can
+                // be maintained across orientation changes.
+                mDisplayedWidgets = allDisplayedWidgets;
+            }
 
-        updateTitleAndIndicator(requestedPage);
-        // For purpose of recommendations section, we don't want paging dots to be halved in two
-        // pane display, so, we always provide isTwoPanels = "false".
-        mPageIndicator.setPauseScroll(/*pause=*/false, /*isTwoPanels=*/false);
-        return allDisplayedWidgets.size();
+            updateTitleAndIndicator(requestedPage);
+            // For purpose of recommendations section, we don't want paging dots to be halved in two
+            // pane display, so, we always provide isTwoPanels = "false".
+            mPageIndicator.setPauseScroll(/*pause=*/false, /*isTwoPanels=*/false);
+            return allDisplayedWidgets.size();
+        } else {
+            return mDisplayedWidgets.size();
+        }
+    }
+
+    /**
+     * Returns if we should re-render the views.
+     */
+    private boolean shouldUpdate(int availableWidth, float availableHeight) {
+        return this.mAvailableWidth != availableWidth || this.mAvailableHeight != availableHeight
+                || getContext().getResources().getConfiguration().uiMode != this.mLastUiMode;
     }
 
     private void clear() {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 7a218ae..44c0ebd 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -15,9 +15,7 @@
  */
 package com.android.launcher3.widget.picker;
 
-import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
 import static com.android.launcher3.Flags.enableTieredWidgetsByDefaultInPicker;
-import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
 import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_EXPAND_PRESS;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
@@ -82,12 +80,10 @@
 import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Predicate;
-import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
 /**
@@ -632,35 +628,22 @@
         if (mIsInSearchMode) {
             return;
         }
-        if (enableCategorizedWidgetSuggestions()) {
-            // We avoid applying new recommendations when some are already displayed.
-            if (mRecommendedWidgetsMap.isEmpty()) {
-                mRecommendedWidgetsMap =
-                        mActivityContext.getWidgetPickerDataProvider().get().getRecommendations();
-            }
-            mRecommendedWidgetsCount = mWidgetRecommendationsView.setRecommendations(
-                    mRecommendedWidgetsMap,
-                    mDeviceProfile,
-                    /* availableHeight= */ getMaxAvailableHeightForRecommendations(),
-                    /* availableWidth= */ mMaxSpanPerRow,
-                    /* cellPadding= */ mWidgetCellHorizontalPadding,
-                    /* requestedPage= */ mRecommendationsCurrentPage
-            );
-        } else {
-            if (mRecommendedWidgets.isEmpty()) {
-                mRecommendedWidgets = mActivityContext.getWidgetPickerDataProvider().get()
-                        .getRecommendations()
-                        .values().stream()
-                        .flatMap(Collection::stream).collect(Collectors.toList());
-                mRecommendedWidgetsCount = mWidgetRecommendationsView.setRecommendations(
-                        mRecommendedWidgets,
-                        mDeviceProfile,
-                        /* availableHeight= */ getMaxAvailableHeightForRecommendations(),
-                        /* availableWidth= */ mMaxSpanPerRow,
-                        /* cellPadding= */ mWidgetCellHorizontalPadding
-                );
-            }
+        boolean forceUpdate = false;
+        // We avoid applying new recommendations when some are already displayed.
+        if (mRecommendedWidgetsMap.isEmpty()) {
+            mRecommendedWidgetsMap =
+                    mActivityContext.getWidgetPickerDataProvider().get().getRecommendations();
+            forceUpdate = true;
         }
+        mRecommendedWidgetsCount = mWidgetRecommendationsView.setRecommendations(
+                mRecommendedWidgetsMap,
+                mDeviceProfile,
+                /* availableHeight= */ getMaxAvailableHeightForRecommendations(),
+                /* availableWidth= */ mMaxSpanPerRow,
+                /* cellPadding= */ mWidgetCellHorizontalPadding,
+                /* requestedPage= */ mRecommendationsCurrentPage,
+                /* forceUpdate= */ forceUpdate
+        );
 
         mWidgetRecommendationsContainer.setVisibility(
                 mRecommendedWidgetsCount > 0 ? VISIBLE : GONE);
@@ -792,13 +775,7 @@
     }
 
     private static int getWidgetSheetId(BaseActivity activity) {
-        boolean isTwoPane = (activity.getDeviceProfile().isTablet
-                // Enables two pane picker for tablets in all orientations when the
-                // enableCategorizedWidgetSuggestions flag is on.
-                && (activity.getDeviceProfile().isLandscape || enableCategorizedWidgetSuggestions())
-                && !activity.getDeviceProfile().isTwoPanels)
-                // Enables two pane picker for unfolded foldables if the flag is on.
-                || (activity.getDeviceProfile().isTwoPanels && enableUnfoldedTwoPanePicker());
+        boolean isTwoPane = activity.getDeviceProfile().isTablet;
 
         return isTwoPane ? R.layout.widgets_two_pane_sheet : R.layout.widgets_full_sheet;
     }
@@ -945,16 +922,7 @@
     private static boolean shouldRecreateLayout(DeviceProfile oldDp, DeviceProfile newDp) {
         // When folding/unfolding the foldables, we need to switch between the regular widget picker
         // and the two pane picker, so we rebuild the picker with the correct layout.
-        boolean isFoldUnFold =
-                oldDp.isTwoPanels != newDp.isTwoPanels && enableUnfoldedTwoPanePicker();
-        // In tablets, on orientation change we switch between single and two pane picker unless the
-        // categorized suggestions flag was on. With the categorized suggestions feature, we use a
-        // two pane picker across all orientations.
-        boolean useDifferentLayoutOnOrientationChange =
-                (!enableCategorizedWidgetSuggestions() && (newDp.isTablet && !newDp.isTwoPanels
-                        && oldDp.isLandscape != newDp.isLandscape));
-
-        return isFoldUnFold || useDifferentLayoutOnOrientationChange;
+        return oldDp.isTwoPanels != newDp.isTwoPanels;
     }
 
     /**
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index 679b0f5..fc99fcc 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -112,21 +112,48 @@
         // Bind the widget items.
         for (int i = 0; i < widgetItemsTable.size(); i++) {
             List<WidgetItem> widgetItemsPerRow = widgetItemsTable.get(i);
-            for (int j = 0; j < widgetItemsPerRow.size(); j++) {
-                WidgetTableRow row = (WidgetTableRow) table.getChildAt(i);
-                row.setVisibility(View.VISIBLE);
-                WidgetCell widget = (WidgetCell) row.getChildAt(j);
-                widget.clear();
-                widget.addPreviewReadyListener(row);
-                WidgetItem widgetItem = widgetItemsPerRow.get(j);
-                widget.setVisibility(View.VISIBLE);
+            WidgetTableRow row = (WidgetTableRow) table.getChildAt(i);
 
-                widget.applyFromCellItem(widgetItem);
-                widget.requestLayout();
+            if (areRowItemsUnchanged(row, widgetItemsPerRow)) {  // Just show widgets in row as is
+                row.setVisibility(View.VISIBLE);
+                for (int j = 0; j < widgetItemsPerRow.size(); j++) {
+                    WidgetCell widget = (WidgetCell) row.getChildAt(j);
+                    widget.setVisibility(View.VISIBLE);
+                }
+            } else {
+                for (int j = 0; j < widgetItemsPerRow.size(); j++) {
+                    row.setVisibility(View.VISIBLE);
+                    WidgetCell widget = (WidgetCell) row.getChildAt(j);
+                    widget.clear();
+                    WidgetItem widgetItem = widgetItemsPerRow.get(j);
+                    widget.addPreviewReadyListener(row);
+                    widget.setVisibility(View.VISIBLE);
+
+                    widget.applyFromCellItem(widgetItem);
+                    widget.requestLayout();
+                }
             }
         }
     }
 
+    private boolean areRowItemsUnchanged(WidgetTableRow row, List<WidgetItem> widgetItemsPerRow) {
+        // NOTE: on rotation or fold / unfold, we bind different view holders
+        // so, we don't any special handling for that case.
+        if (row.getChildCount() != widgetItemsPerRow.size()) { // Items not equal
+            return false;
+        }
+
+        for (int j = 0; j < widgetItemsPerRow.size(); j++) {
+            WidgetCell widgetCell = (WidgetCell) row.getChildAt(j);
+            WidgetItem widgetItem = widgetItemsPerRow.get(j);
+            if (widgetCell.getWidgetItem() == null
+                    || !widgetCell.getWidgetItem().equals(widgetItem)) {
+                return false; // Items at given position in row aren't same.
+            }
+        }
+        return true;
+    }
+
     /**
      * Adds and hides table rows and columns from {@code table} to ensure there is sufficient room
      * to display {@code widgetItemsTable}.
@@ -151,26 +178,31 @@
                 tableRow.setGravity(Gravity.TOP);
                 table.addView(tableRow);
             }
-            // Pass resize delay to let the "move" and "change" animations run before resizing the
-            // row.
-            tableRow.setupRow(widgetItems.size(),
-                    /*resizeDelayMs=*/ WIDGET_LIST_ITEM_APPEARANCE_START_DELAY);
-            if (tableRow.getChildCount() > widgetItems.size()) {
-                for (int j = widgetItems.size(); j < tableRow.getChildCount(); j++) {
-                    tableRow.getChildAt(j).setVisibility(View.GONE);
-                }
-            } else {
-                for (int j = tableRow.getChildCount(); j < widgetItems.size(); j++) {
-                    WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
-                            R.layout.widget_cell, tableRow, false);
-                    // set up touch.
-                    widget.setOnClickListener(mIconClickListener);
-                    widget.addPreviewReadyListener(tableRow);
-                    View preview = widget.findViewById(R.id.widget_preview_container);
-                    preview.setOnClickListener(mIconClickListener);
-                    preview.setOnLongClickListener(mIconLongClickListener);
-                    widget.setAnimatePreview(false);
-                    tableRow.addView(widget);
+
+            // If the row items are unchanged, we don't need to re-setup the row or the items;
+            // we can just show the row as is.
+            if (!areRowItemsUnchanged(tableRow, widgetItems)) {
+                // Pass resize delay to let the "move" and "change" animations run before resizing
+                // the row.
+                tableRow.setupRow(widgetItems.size(),
+                        /*resizeDelayMs=*/ WIDGET_LIST_ITEM_APPEARANCE_START_DELAY);
+                if (tableRow.getChildCount() > widgetItems.size()) {
+                    for (int j = widgetItems.size(); j < tableRow.getChildCount(); j++) {
+                        tableRow.getChildAt(j).setVisibility(View.GONE);
+                    }
+                } else {
+                    for (int j = tableRow.getChildCount(); j < widgetItems.size(); j++) {
+                        WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
+                                R.layout.widget_cell, tableRow, false);
+                        // set up touch.
+                        widget.setOnClickListener(mIconClickListener);
+                        widget.addPreviewReadyListener(tableRow);
+                        View preview = widget.findViewById(R.id.widget_preview_container);
+                        preview.setOnClickListener(mIconClickListener);
+                        preview.setOnLongClickListener(mIconLongClickListener);
+                        widget.setAnimatePreview(false);
+                        tableRow.addView(widget);
+                    }
                 }
             }
         }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
index 216f4d4..9ee9150 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.widget.picker;
 
-import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
 import static com.android.launcher3.widget.util.WidgetSizes.getWidgetSizePx;
 import static com.android.launcher3.widget.util.WidgetsTableUtils.WIDGETS_TABLE_ROW_COUNT_COMPARATOR;
@@ -112,10 +111,8 @@
                 WidgetCell widgetCell = addItemCell(tableRow);
                 widgetCell.applyFromCellItem(widgetItem);
                 widgetCell.showAppIconInWidgetTitle(true);
-                if (enableCategorizedWidgetSuggestions()) {
-                    widgetCell.showDescription(false);
-                    widgetCell.showDimensions(false);
-                }
+                widgetCell.showDescription(false);
+                widgetCell.showDimensions(false);
             }
             addView(tableRow);
         }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index df76400..9ffaf51 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -15,9 +15,7 @@
  */
 package com.android.launcher3.widget.picker;
 
-import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
 import static com.android.launcher3.Flags.enableTieredWidgetsByDefaultInPicker;
-import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
 import static com.android.launcher3.UtilitiesKt.CLIP_CHILDREN_FALSE_MODIFIER;
 import static com.android.launcher3.UtilitiesKt.CLIP_TO_PADDING_FALSE_MODIFIER;
 import static com.android.launcher3.UtilitiesKt.modifyAttributesOnViewTree;
@@ -215,22 +213,9 @@
 
     @Override
     protected int getTabletHorizontalMargin(DeviceProfile deviceProfile) {
-        if (enableCategorizedWidgetSuggestions()) {
-            // two pane picker is full width for fold as well as tablet.
-            return getResources().getDimensionPixelSize(
-                    R.dimen.widget_picker_two_panels_left_right_margin);
-        }
-        if (deviceProfile.isTwoPanels && enableUnfoldedTwoPanePicker()) {
-            // enableUnfoldedTwoPanePicker made two pane picker full-width for fold only.
-            return getResources().getDimensionPixelSize(
-                    R.dimen.widget_picker_two_panels_left_right_margin);
-        }
-        if (deviceProfile.isLandscape && !deviceProfile.isTwoPanels) {
-            // non-fold tablet landscape margins (ag/22163531)
-            return getResources().getDimensionPixelSize(
-                    R.dimen.widget_picker_landscape_tablet_left_right_margin);
-        }
-        return deviceProfile.allAppsLeftRightMargin;
+        // two pane picker is full width for fold as well as tablet.
+        return getResources().getDimensionPixelSize(
+                R.dimen.widget_picker_two_panels_left_right_margin);
     }
 
     @Override
@@ -257,7 +242,7 @@
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         super.onLayout(changed, l, t, r, b);
-        if (changed && mDeviceProfile.isTwoPanels && enableUnfoldedTwoPanePicker()) {
+        if (changed && mDeviceProfile.isTwoPanels) {
             LinearLayout layout = mContent.findViewById(R.id.linear_layout_container);
             FrameLayout leftPane = layout.findViewById(R.id.recycler_view_container);
             LinearLayout.LayoutParams layoutParams = (LayoutParams) leftPane.getLayoutParams();
@@ -427,7 +412,7 @@
     protected int getAvailableWidthForSuggestions(int pickerAvailableWidth) {
         int rightPaneWidth = (int) Math.ceil(0.67 * pickerAvailableWidth);
 
-        if (mDeviceProfile.isTwoPanels && enableUnfoldedTwoPanePicker()) {
+        if (mDeviceProfile.isTwoPanels) {
             // See onLayout
             int leftPaneWidth = (int) (0.33 * pickerAvailableWidth);
             @Px int minLeftPaneWidthPx = Utilities.dpToPx(MINIMUM_WIDTH_LEFT_PANE_FOLDABLE_DP);
@@ -552,13 +537,9 @@
                 }
 
                 WidgetsListContentEntry contentEntryToBind;
-                if (enableCategorizedWidgetSuggestions()) {
-                    // Setting max span size enables row to understand how to fit more than one item
-                    // in a row.
-                    contentEntryToBind = contentEntry.withMaxSpanSize(mMaxSpanPerRow);
-                } else {
-                    contentEntryToBind = contentEntry;
-                }
+                // Setting max span size enables row to understand how to fit more than one item
+                // in a row.
+                contentEntryToBind = contentEntry.withMaxSpanSize(mMaxSpanPerRow);
 
                 WidgetsRowViewHolder widgetsRowViewHolder =
                         mWidgetsListTableViewHolderBinder.newViewHolder(mRightPane);
diff --git a/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProvider.kt b/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProvider.kt
index 46d3e7a..5b97a49 100644
--- a/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProvider.kt
+++ b/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProvider.kt
@@ -16,43 +16,79 @@
 
 package com.android.launcher3.widget.picker.model
 
+import android.content.Context
+import com.android.launcher3.dagger.LauncherComponentProvider.appComponent
+import com.android.launcher3.model.WidgetItem
+import com.android.launcher3.model.WidgetsFilterDataProvider
+import com.android.launcher3.model.WidgetsFilterDataProvider.WidgetsFilterLoadedCallback
 import com.android.launcher3.model.data.ItemInfo
 import com.android.launcher3.widget.model.WidgetsListBaseEntry
 import com.android.launcher3.widget.picker.model.data.WidgetPickerData
 import com.android.launcher3.widget.picker.model.data.WidgetPickerDataUtils.withRecommendedWidgets
 import com.android.launcher3.widget.picker.model.data.WidgetPickerDataUtils.withWidgets
 import java.io.PrintWriter
+import java.util.function.Predicate
 
 /**
  * Provides [WidgetPickerData] to various views such as widget picker, app-specific widget picker,
  * widgets shortcut.
  */
-class WidgetPickerDataProvider {
+class WidgetPickerDataProvider(private val filterProvider: WidgetsFilterDataProvider) :
+    WidgetsFilterLoadedCallback {
+
+    constructor(context: Context) : this(context.appComponent.widgetsFilterDataProvider)
+
     /** All the widgets data provided for the views */
     private var mWidgetPickerData: WidgetPickerData = WidgetPickerData()
 
     private var changeListener: WidgetPickerDataChangeListener? = null
 
+    var hostSpecifiedDefaultWidgetsFilter: Predicate<WidgetItem>? = null
+
+    private var allWidgets: List<WidgetsListBaseEntry> = emptyList()
+
     /** Sets a listener to be called back when widget data is updated. */
     fun setChangeListener(changeListener: WidgetPickerDataChangeListener?) {
         this.changeListener = changeListener
     }
 
+    init {
+        filterProvider.addFilterChangeCallback(this)
+    }
+
     /** Returns the current snapshot of [WidgetPickerData]. */
     fun get(): WidgetPickerData {
         return mWidgetPickerData
     }
 
+    override fun onWidgetsFilterLoaded() {
+        setWidgets(allWidgets)
+    }
+
     /**
      * Updates the widgets available to the widget picker.
      *
      * Generally called when the widgets model has new data.
      */
-    @JvmOverloads
-    fun setWidgets(
-        allWidgets: List<WidgetsListBaseEntry>,
-        defaultWidgets: List<WidgetsListBaseEntry> = listOf()
-    ) {
+    fun setWidgets(allWidgets: List<WidgetsListBaseEntry>) {
+        this.allWidgets = allWidgets
+
+        val currentFilter = filterProvider.defaultWidgetsFilter
+        val finalFilter =
+            when {
+                currentFilter != null && hostSpecifiedDefaultWidgetsFilter != null ->
+                    currentFilter.and(hostSpecifiedDefaultWidgetsFilter)
+                hostSpecifiedDefaultWidgetsFilter != null -> hostSpecifiedDefaultWidgetsFilter
+                else -> currentFilter
+            }
+
+        val defaultWidgets =
+            if (finalFilter != null)
+                allWidgets
+                    .map { it.copy().apply { mWidgets.removeIf(finalFilter) } }
+                    .filter { it.mWidgets.isNotEmpty() }
+            else emptyList()
+
         mWidgetPickerData =
             mWidgetPickerData.withWidgets(allWidgets = allWidgets, defaultWidgets = defaultWidgets)
         changeListener?.onWidgetsBound()
@@ -74,6 +110,10 @@
         writer.println("$prefix\twidgetPickerData:$mWidgetPickerData")
     }
 
+    fun destroy() {
+        filterProvider.removeFilterChangeCallback(this)
+    }
+
     interface WidgetPickerDataChangeListener {
         /** A callback to get notified when widgets are bound. */
         fun onWidgetsBound()
diff --git a/tests/Launcher3Tests.xml b/tests/Launcher3Tests.xml
index 56dd6a4..da86357 100644
--- a/tests/Launcher3Tests.xml
+++ b/tests/Launcher3Tests.xml
@@ -62,6 +62,7 @@
 
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
         <option name="directory-keys" value="/data/user/0/com.android.launcher3/files" />
+        <option name="directory-keys" value="/data/user/10/com.android.launcher3/files" />
         <option name="collect-on-run-ended-only" value="true" />
     </metrics_collector>
 
diff --git a/tests/multivalentTests/shared/com/android/launcher3/testing/OWNERS b/tests/multivalentTests/shared/com/android/launcher3/testing/OWNERS
new file mode 100644
index 0000000..02e8ebc
--- /dev/null
+++ b/tests/multivalentTests/shared/com/android/launcher3/testing/OWNERS
@@ -0,0 +1,4 @@
+vadimt@google.com
+sunnygoyal@google.com
+winsonc@google.com
+hyunyoungs@google.com
diff --git a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index 3658989..a30261e 100644
--- a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -35,10 +35,11 @@
 import com.android.launcher3.util.AllModulesMinusWMProxy
 import com.android.launcher3.util.DisplayController
 import com.android.launcher3.util.FakePrefsModule
-import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext
 import com.android.launcher3.util.NavigationMode
+import com.android.launcher3.util.SandboxContext
 import com.android.launcher3.util.WindowBounds
 import com.android.launcher3.util.rule.TestStabilityRule
+import com.android.launcher3.util.rule.ZipFilesRule
 import com.android.launcher3.util.rule.setFlags
 import com.android.launcher3.util.window.CachedDisplayInfo
 import com.android.launcher3.util.window.WindowManagerProxy
@@ -51,6 +52,7 @@
 import java.io.StringWriter
 import kotlin.math.max
 import kotlin.math.min
+import org.junit.ClassRule
 import org.junit.Rule
 import org.mockito.kotlin.any
 import org.mockito.kotlin.doReturn
@@ -80,6 +82,13 @@
 
     @Rule @JvmField val limitDevicesRule = LimitDevicesRule()
 
+    companion object {
+        @ClassRule
+        @JvmField
+        val resultZipRule =
+            ZipFilesRule(InstrumentationRegistry.getInstrumentation().targetContext, "DumpTest")
+    }
+
     class DeviceSpec(
         val naturalSize: Pair<Int, Int>,
         var densityDpi: Int,
@@ -364,13 +373,9 @@
         context.assets.open("dumpTests/$fileName").bufferedReader().use(BufferedReader::readText)
 
     private fun writeToDevice(context: Context, fileName: String, content: String) {
-        val dir =
-            File(context.filesDir, "dumpTests").also {
-                if (!it.exists()) {
-                    it.mkdirs()
-                }
-            }
-        File(dir, fileName).writeText(content)
+        val file = File(context.getDir("dumpTests", Context.MODE_PRIVATE), fileName)
+        file.writeText(content)
+        resultZipRule.write(file)
     }
 
     protected fun Float.dpToPx(): Float {
diff --git a/tests/multivalentTests/src/com/android/launcher3/AppWidgetsRestoredReceiverTest.kt b/tests/multivalentTests/src/com/android/launcher3/AppWidgetsRestoredReceiverTest.kt
index 0e06051..6483bd5 100644
--- a/tests/multivalentTests/src/com/android/launcher3/AppWidgetsRestoredReceiverTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/AppWidgetsRestoredReceiverTest.kt
@@ -6,7 +6,7 @@
 import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS
 import android.appwidget.AppWidgetManager.EXTRA_HOST_ID
 import android.content.Intent
-import android.platform.uiautomator_helpers.DeviceHelpers
+import android.platform.uiautomatorhelpers.DeviceHelpers
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.launcher3.LauncherPrefs.Companion.APP_WIDGET_IDS
diff --git a/tests/multivalentTests/src/com/android/launcher3/LauncherPrefsTest.kt b/tests/multivalentTests/src/com/android/launcher3/LauncherPrefsTest.kt
index 4aeef2e..da9cc86 100644
--- a/tests/multivalentTests/src/com/android/launcher3/LauncherPrefsTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/LauncherPrefsTest.kt
@@ -24,12 +24,18 @@
 import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
+import org.junit.Assert.assertThrows
 import org.junit.Test
 import org.junit.runner.RunWith
 
 private val TEST_BOOLEAN_ITEM = LauncherPrefs.nonRestorableItem("1", false)
 private val TEST_STRING_ITEM = LauncherPrefs.nonRestorableItem("2", "( ͡❛ ͜ʖ ͡❛)")
 private val TEST_INT_ITEM = LauncherPrefs.nonRestorableItem("3", -1)
+private val TEST_FLOAT_ITEM = LauncherPrefs.nonRestorableItem("4", -1f)
+private val TEST_LONG_ITEM = LauncherPrefs.nonRestorableItem("5", -1L)
+private val TEST_SET_ITEM = LauncherPrefs.nonRestorableItem("6", setOf<String>())
+private val TEST_HASHSET_ITEM = LauncherPrefs.nonRestorableItem("7", hashSetOf<String>())
+
 private val TEST_CONTEXTUAL_ITEM =
     ContextualItem("4", true, { true }, EncryptionType.ENCRYPTED, Boolean::class.java)
 
@@ -144,15 +150,49 @@
     }
 
     @Test
+    fun whenItemType_isInvalid_thenThrowException() {
+        val badItem = LauncherPrefs.nonRestorableItem("8", mapOf<String, String>())
+        with(launcherPrefs) {
+            assertThrows(IllegalArgumentException::class.java) {
+                putSync(badItem.to(badItem.defaultValue))
+            }
+            assertThrows(IllegalArgumentException::class.java) { get(badItem) }
+        }
+    }
+
+    @Test
     fun put_storesListOfItemsInLauncherPrefs_successfully() {
         with(launcherPrefs) {
             putSync(
                 TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue),
                 TEST_INT_ITEM.to(TEST_INT_ITEM.defaultValue),
                 TEST_BOOLEAN_ITEM.to(TEST_BOOLEAN_ITEM.defaultValue),
+                TEST_FLOAT_ITEM.to(TEST_FLOAT_ITEM.defaultValue),
+                TEST_LONG_ITEM.to(TEST_LONG_ITEM.defaultValue),
+                TEST_SET_ITEM.to(TEST_SET_ITEM.defaultValue),
+                TEST_HASHSET_ITEM.to(TEST_HASHSET_ITEM.defaultValue),
             )
-            assertThat(has(TEST_BOOLEAN_ITEM, TEST_INT_ITEM, TEST_STRING_ITEM)).isTrue()
-            remove(TEST_STRING_ITEM, TEST_INT_ITEM, TEST_BOOLEAN_ITEM)
+            assertThat(
+                    has(
+                        TEST_STRING_ITEM,
+                        TEST_INT_ITEM,
+                        TEST_BOOLEAN_ITEM,
+                        TEST_FLOAT_ITEM,
+                        TEST_LONG_ITEM,
+                        TEST_SET_ITEM,
+                        TEST_HASHSET_ITEM,
+                    )
+                )
+                .isTrue()
+            remove(
+                TEST_STRING_ITEM,
+                TEST_INT_ITEM,
+                TEST_BOOLEAN_ITEM,
+                TEST_FLOAT_ITEM,
+                TEST_LONG_ITEM,
+                TEST_SET_ITEM,
+                TEST_HASHSET_ITEM,
+            )
         }
     }
 
diff --git a/tests/multivalentTests/src/com/android/launcher3/icons/UserBadgeDrawableTest.kt b/tests/multivalentTests/src/com/android/launcher3/icons/UserBadgeDrawableTest.kt
index d611ae8..91ba628 100644
--- a/tests/multivalentTests/src/com/android/launcher3/icons/UserBadgeDrawableTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/icons/UserBadgeDrawableTest.kt
@@ -34,19 +34,20 @@
     private val context = InstrumentationRegistry.getInstrumentation().targetContext
     private val canvas = mock<Canvas>()
     private val systemUnderTest =
-        UserBadgeDrawable(context, R.drawable.ic_work_app_badge, R.color.badge_tint_work, false)
+        UserBadgeDrawable(
+            context,
+            R.drawable.ic_work_app_badge,
+            R.color.badge_tint_work,
+            false /* isThemed */,
+            null, /* shape */
+        )
 
     @Test
     fun draw_opaque() {
         val colorList = mutableListOf<Int>()
-        whenever(
-            canvas.drawCircle(
-                any(),
-                any(),
-                any(),
-                any()
-            )
-        ).then { colorList.add(it.getArgument<Paint>(3).color) }
+        whenever(canvas.drawCircle(any(), any(), any(), any())).then {
+            colorList.add(it.getArgument<Paint>(3).color)
+        }
 
         systemUnderTest.alpha = 255
         systemUnderTest.draw(canvas)
@@ -57,14 +58,9 @@
     @Test
     fun draw_transparent() {
         val colorList = mutableListOf<Int>()
-        whenever(
-            canvas.drawCircle(
-                any(),
-                any(),
-                any(),
-                any()
-            )
-        ).then { colorList.add(it.getArgument<Paint>(3).color) }
+        whenever(canvas.drawCircle(any(), any(), any(), any())).then {
+            colorList.add(it.getArgument<Paint>(3).color)
+        }
 
         systemUnderTest.alpha = 0
         systemUnderTest.draw(canvas)
diff --git a/tests/multivalentTests/src/com/android/launcher3/icons/mono/MonoIconThemeControllerTest.kt b/tests/multivalentTests/src/com/android/launcher3/icons/mono/MonoIconThemeControllerTest.kt
index 12c14fb..2c9cb2f 100644
--- a/tests/multivalentTests/src/com/android/launcher3/icons/mono/MonoIconThemeControllerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/icons/mono/MonoIconThemeControllerTest.kt
@@ -38,6 +38,7 @@
 import org.junit.Assert.assertNotNull
 import org.junit.Assert.assertNull
 import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -77,6 +78,8 @@
     @EnableFlags(Flags.FLAG_FORCE_MONOCHROME_APP_ICONS)
     fun `createThemedBitmap when mono generation is enabled`() {
         ensureBitmapSerializationSupported()
+        // Make sure forced theme icon is enabled in BaseIconFactory
+        assumeTrue(iconFactory.shouldForceThemeIcon())
         val icon = AdaptiveIconDrawable(ColorDrawable(Color.BLACK), null, null)
         assertNotNull(
             MonoIconThemeController().createThemedBitmap(icon, BitmapInfo.LOW_RES_INFO, iconFactory)
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/WidgetsModelTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/WidgetsModelTest.kt
index d704195..777d81b 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/WidgetsModelTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/WidgetsModelTest.kt
@@ -43,7 +43,6 @@
 import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
-import java.util.function.Predicate
 import org.junit.Assert.fail
 import org.junit.Before
 import org.junit.Rule
@@ -65,7 +64,6 @@
     @Mock private lateinit var appWidgetManager: AppWidgetManager
     @Mock private lateinit var app: LauncherAppState
     @Mock private lateinit var iconCacheMock: IconCache
-    @Mock private lateinit var widgetsFilterDataProvider: WidgetsFilterDataProvider
 
     private lateinit var context: Context
     private lateinit var idp: InvariantDeviceProfile
@@ -252,27 +250,6 @@
         // No exception
     }
 
-    @Test
-    fun updateWidgetFilters_setsFiltersCorrectly() {
-        val testDefaultWidgetFilter = Predicate<WidgetItem> { w -> w.widgetInfo != null }
-        whenever(widgetsFilterDataProvider.getDefaultWidgetsFilter())
-            .thenReturn(testDefaultWidgetFilter)
-        val testPredicatedWidgetFilter = Predicate<WidgetItem> { w -> w.widgetInfo != null }
-        whenever(widgetsFilterDataProvider.getPredictedWidgetsFilter())
-            .thenReturn(testPredicatedWidgetFilter)
-
-        underTest.updateWidgetFilters(widgetsFilterDataProvider)
-
-        assertThat(underTest.defaultWidgetsFilter).isEqualTo(testDefaultWidgetFilter)
-        assertThat(underTest.predictedWidgetsFilter).isEqualTo(testPredicatedWidgetFilter)
-    }
-
-    @Test
-    fun widgetFilters_nullInitially() {
-        assertThat(underTest.defaultWidgetsFilter).isNull()
-        assertThat(underTest.predictedWidgetsFilter).isNull()
-    }
-
     private fun loadWidgets() {
         val latch = CountDownLatch(1)
         Executors.MODEL_EXECUTOR.execute {
diff --git a/tests/multivalentTests/src/com/android/launcher3/shapes/ShapesProviderTest.kt b/tests/multivalentTests/src/com/android/launcher3/shapes/ShapesProviderTest.kt
index 49d305b..66b8be0 100644
--- a/tests/multivalentTests/src/com/android/launcher3/shapes/ShapesProviderTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/shapes/ShapesProviderTest.kt
@@ -23,6 +23,11 @@
 import androidx.test.filters.SmallTest
 import com.android.launcher3.Flags.FLAG_ENABLE_LAUNCHER_ICON_SHAPES
 import com.android.launcher3.graphics.ShapeDelegate.GenericPathShape
+import com.android.launcher3.shapes.ShapesProvider.ARCH_KEY
+import com.android.launcher3.shapes.ShapesProvider.CIRCLE_KEY
+import com.android.launcher3.shapes.ShapesProvider.FOUR_SIDED_COOKIE_KEY
+import com.android.launcher3.shapes.ShapesProvider.SEVEN_SIDED_COOKIE_KEY
+import com.android.launcher3.shapes.ShapesProvider.SQUARE_KEY
 import com.android.systemui.shared.Flags.FLAG_NEW_CUSTOMIZATION_PICKER_UI
 import org.junit.Rule
 import org.junit.Test
@@ -37,81 +42,99 @@
     @Test
     @EnableFlags(FLAG_ENABLE_LAUNCHER_ICON_SHAPES, FLAG_NEW_CUSTOMIZATION_PICKER_UI)
     fun `verify valid path arch`() {
-        ShapesProvider.iconShapes["arch"]?.apply {
-            GenericPathShape(pathString)
-            PathParser.createPathFromPathData(pathString)
-        }
+        ShapesProvider.iconShapes
+            .find { it.key == ARCH_KEY }!!
+            .run {
+                GenericPathShape(pathString)
+                PathParser.createPathFromPathData(pathString)
+            }
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_LAUNCHER_ICON_SHAPES, FLAG_NEW_CUSTOMIZATION_PICKER_UI)
     fun `verify valid path 4_sided_cookie`() {
-        ShapesProvider.iconShapes["4_sided_cookie"]?.apply {
-            GenericPathShape(pathString)
-            PathParser.createPathFromPathData(pathString)
-        }
+        ShapesProvider.iconShapes
+            .find { it.key == FOUR_SIDED_COOKIE_KEY }!!
+            .run {
+                GenericPathShape(pathString)
+                PathParser.createPathFromPathData(pathString)
+            }
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_LAUNCHER_ICON_SHAPES, FLAG_NEW_CUSTOMIZATION_PICKER_UI)
     fun `verify valid path seven_sided_cookie`() {
-        ShapesProvider.iconShapes["seven_sided_cookie"]?.apply {
-            GenericPathShape(pathString)
-            PathParser.createPathFromPathData(pathString)
-        }
+        ShapesProvider.iconShapes
+            .find { it.key == SEVEN_SIDED_COOKIE_KEY }!!
+            .run {
+                GenericPathShape(pathString)
+                PathParser.createPathFromPathData(pathString)
+            }
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_LAUNCHER_ICON_SHAPES, FLAG_NEW_CUSTOMIZATION_PICKER_UI)
     fun `verify valid path circle`() {
-        ShapesProvider.iconShapes["circle"]?.apply {
-            GenericPathShape(pathString)
-            PathParser.createPathFromPathData(pathString)
-        }
+        ShapesProvider.iconShapes
+            .find { it.key == CIRCLE_KEY }!!
+            .run {
+                GenericPathShape(pathString)
+                PathParser.createPathFromPathData(pathString)
+            }
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_LAUNCHER_ICON_SHAPES, FLAG_NEW_CUSTOMIZATION_PICKER_UI)
     fun `verify valid path square`() {
-        ShapesProvider.iconShapes["square"]?.apply {
-            GenericPathShape(pathString)
-            PathParser.createPathFromPathData(pathString)
-        }
+        ShapesProvider.iconShapes
+            .find { it.key == ARCH_KEY }!!
+            .run {
+                GenericPathShape(pathString)
+                PathParser.createPathFromPathData(pathString)
+            }
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_LAUNCHER_ICON_SHAPES, FLAG_NEW_CUSTOMIZATION_PICKER_UI)
     fun `verify valid folder path clover`() {
-        ShapesProvider.folderShapes["clover"]?.let { pathString ->
-            GenericPathShape(pathString)
-            PathParser.createPathFromPathData(pathString)
-        }
+        ShapesProvider.iconShapes
+            .find { it.key == CIRCLE_KEY }!!
+            .run {
+                GenericPathShape(folderPathString)
+                PathParser.createPathFromPathData(folderPathString)
+            }
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_LAUNCHER_ICON_SHAPES, FLAG_NEW_CUSTOMIZATION_PICKER_UI)
     fun `verify valid folder path complexClover`() {
-        ShapesProvider.folderShapes["complexClover"]?.let { pathString ->
-            GenericPathShape(pathString)
-            PathParser.createPathFromPathData(pathString)
-        }
+        ShapesProvider.iconShapes
+            .find { it.key == FOUR_SIDED_COOKIE_KEY }!!
+            .run {
+                GenericPathShape(folderPathString)
+                PathParser.createPathFromPathData(folderPathString)
+            }
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_LAUNCHER_ICON_SHAPES, FLAG_NEW_CUSTOMIZATION_PICKER_UI)
     fun `verify valid folder path arch`() {
-        ShapesProvider.folderShapes["arch"]?.let { pathString ->
-            GenericPathShape(pathString)
-            PathParser.createPathFromPathData(pathString)
-        }
+        ShapesProvider.iconShapes
+            .find { it.key == ARCH_KEY }!!
+            .run {
+                GenericPathShape(folderPathString)
+                PathParser.createPathFromPathData(folderPathString)
+            }
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_LAUNCHER_ICON_SHAPES, FLAG_NEW_CUSTOMIZATION_PICKER_UI)
     fun `verify valid folder path square`() {
-        ShapesProvider.folderShapes["square"]?.let { pathString ->
-            GenericPathShape(pathString)
-            PathParser.createPathFromPathData(pathString)
-        }
+        ShapesProvider.iconShapes
+            .find { it.key == SQUARE_KEY }!!
+            .run {
+                GenericPathShape(folderPathString)
+                PathParser.createPathFromPathData(folderPathString)
+            }
     }
 }
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/ActivityContextWrapper.java b/tests/multivalentTests/src/com/android/launcher3/util/ActivityContextWrapper.java
index 4217d22..f4dc88d 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/ActivityContextWrapper.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/ActivityContextWrapper.java
@@ -18,23 +18,15 @@
 import android.R;
 import android.content.Context;
 import android.content.ContextWrapper;
-import android.view.ContextThemeWrapper;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.BaseDragLayer;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * {@link ContextWrapper} with internal Launcher interface for testing
  */
-public class ActivityContextWrapper extends ContextThemeWrapper implements ActivityContext {
-
-    private final List<OnDeviceProfileChangeListener> mDpChangeListeners = new ArrayList<>();
+public class ActivityContextWrapper extends BaseContext {
 
     private final DeviceProfile mProfile;
     private final MyDragLayer mMyDragLayer;
@@ -47,20 +39,15 @@
         super(base, theme);
         mProfile = InvariantDeviceProfile.INSTANCE.get(base).getDeviceProfile(base).copy(base);
         mMyDragLayer = new MyDragLayer(this);
+        Executors.MAIN_EXECUTOR.execute(this::onViewCreated);
     }
 
-
     @Override
     public BaseDragLayer getDragLayer() {
         return mMyDragLayer;
     }
 
     @Override
-    public List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners() {
-        return mDpChangeListeners;
-    }
-
-    @Override
     public DeviceProfile getDeviceProfile() {
         return mProfile;
     }
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/LauncherBindableItemsContainerTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/LauncherBindableItemsContainerTest.kt
index 93be5f5..054c90b 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/LauncherBindableItemsContainerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/LauncherBindableItemsContainerTest.kt
@@ -122,9 +122,10 @@
 
         val items = mutableMapOf<ItemInfo, View>()
 
-        override fun mapOverItems(op: ItemOperator) {
-            items.forEach { (item, view) -> if (op.evaluate(item, view)) return@forEach }
-        }
+        override fun mapOverItems(op: ItemOperator): View? =
+            items.firstNotNullOfOrNull { (item, view) ->
+                if (op.evaluate(item, view)) view else null
+            }
 
         fun addIcon(info: WorkspaceItemInfo) {
             val btv = BubbleTextView(this)
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java
index cee5559..4458e8f 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -56,7 +56,6 @@
 import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.ModelDbController;
 import com.android.launcher3.testing.TestInformationProvider;
-import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/ModelTestExtensions.kt b/tests/multivalentTests/src/com/android/launcher3/util/ModelTestExtensions.kt
index 547d05e..ceefb0d 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/ModelTestExtensions.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/ModelTestExtensions.kt
@@ -1,6 +1,7 @@
 package com.android.launcher3.util
 
 import android.content.ContentValues
+import android.os.Process
 import com.android.launcher3.Flags
 import com.android.launcher3.LauncherModel
 import com.android.launcher3.LauncherSettings.Favorites
@@ -66,7 +67,7 @@
         spanX: Int = 1,
         spanY: Int = 1,
         id: Int = 0,
-        profileId: Int = 0,
+        profileId: Int = Process.myUserHandle().identifier,
         tableName: String = Favorites.TABLE_NAME,
         appWidgetId: Int = -1,
         appWidgetSource: Int = -1,
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplication.kt b/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplication.kt
index 0da8891..2fa4cad 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplication.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplication.kt
@@ -27,7 +27,6 @@
 import android.view.Display
 import androidx.test.core.app.ApplicationProvider
 import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext
-import com.android.launcher3.util.MainThreadInitializedObject.ObjectSandbox
 import org.junit.Rule
 import org.junit.rules.ExternalResource
 import org.junit.rules.TestRule
@@ -69,7 +68,7 @@
         // Defer to the true application to decide whether to clean up. For instance, we do not want
         // to cleanup under Robolectric.
         val app = ApplicationProvider.getApplicationContext<Context>()
-        return if (app is ObjectSandbox) app.shouldCleanUpOnDestroy() else true
+        return (app as? SandboxContext)?.shouldCleanUpOnDestroy() ?: true
     }
 
     override fun apply(statement: Statement, description: Description): Statement {
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplicationTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplicationTest.kt
index d87a406..8a21cff 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplicationTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplicationTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.launcher3.util
 
-import android.content.Context
 import android.hardware.display.DisplayManager
 import android.view.Display
 import android.view.Display.DEFAULT_DISPLAY
@@ -63,16 +62,4 @@
             onDestroy()
         }
     }
-
-    @Test
-    fun testGetObject_objectCreatesDisplayContext_isSandboxed() {
-        class TestSingleton(context: Context) : SafeCloseable {
-            override fun close() = Unit
-
-            val displayContext = context.createDisplayContext(display)
-        }
-
-        val displayContext = MainThreadInitializedObject { TestSingleton(it) }[app].displayContext
-        assertThat(displayContext.applicationContext).isEqualTo(app)
-    }
 }
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/TestSandboxModelContextWrapper.java b/tests/multivalentTests/src/com/android/launcher3/util/TestSandboxModelContextWrapper.java
index 393282f..acd17d1 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/TestSandboxModelContextWrapper.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/TestSandboxModelContextWrapper.java
@@ -18,10 +18,9 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
-import static com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
-
 import android.content.ContextWrapper;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.test.platform.app.InstrumentationRegistry;
 
@@ -29,6 +28,7 @@
 import com.android.launcher3.allapps.AllAppsStore;
 import com.android.launcher3.allapps.AlphabeticalAppsList;
 import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.WidgetsFilterDataProvider;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.popup.PopupDataProvider;
@@ -44,7 +44,7 @@
  * There are 2 constructors in this class. The base context can be {@link SandboxContext} or
  * Instrumentation target context.
  * Using {@link SandboxContext} as base context allows custom implementations for
- * MainThreadInitializedObject providers.
+ * providing objects in Dagger components.
  */
 
 public class TestSandboxModelContextWrapper extends ActivityContextWrapper implements
@@ -57,9 +57,9 @@
 
     protected ActivityAllAppsContainerView<ActivityContextWrapper> mAppsView;
 
-    private final PopupDataProvider mPopupDataProvider = new PopupDataProvider(i -> {});
+    private final PopupDataProvider mPopupDataProvider = new PopupDataProvider(this);
     private final WidgetPickerDataProvider mWidgetPickerDataProvider =
-            new WidgetPickerDataProvider();
+            new WidgetPickerDataProvider(new WidgetsFilterDataProvider());
     protected final UserCache mUserCache;
 
     public TestSandboxModelContextWrapper(SandboxContext base) {
@@ -80,7 +80,7 @@
         mAllAppsStore = mAppsView.getAppsStore();
     }
 
-    @Nullable
+    @NonNull
     @Override
     public PopupDataProvider getPopupDataProvider() {
         return mPopupDataProvider;
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/rule/ZipFilesRule.kt b/tests/multivalentTests/src/com/android/launcher3/util/rule/ZipFilesRule.kt
new file mode 100644
index 0000000..d12de76
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/util/rule/ZipFilesRule.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2025 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.rule
+
+import android.content.Context
+import android.os.FileUtils
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileOutputStream
+import java.util.zip.ZipEntry
+import java.util.zip.ZipOutputStream
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+class ZipFilesRule(val context: Context, val name: String) : TestRule {
+
+    var resultsZip: ZipOutputStream? = null
+
+    override fun apply(base: Statement, description: Description): Statement {
+        return object : Statement() {
+            @Throws(Throwable::class)
+            override fun evaluate() {
+                resultsZip =
+                    ZipOutputStream(
+                        FileOutputStream(
+                            File(context.filesDir, "${description.testClass.simpleName}-$name.zip")
+                        )
+                    )
+                try {
+                    base.evaluate() // This will run the test.
+                } finally {
+                    resultsZip?.close()
+                }
+            }
+        }
+    }
+
+    fun write(file: File) {
+        if (resultsZip !is ZipOutputStream) {
+            throw RuntimeException(
+                "Cannot save files before the test rule starts! We need the rule to start to get the name of the test"
+            )
+        }
+        resultsZip!!.let {
+            it.putNextEntry(ZipEntry(file.name))
+            FileUtils.copy(FileInputStream(file), it)
+            it.closeEntry()
+        }
+    }
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/picker/OWNERS b/tests/multivalentTests/src/com/android/launcher3/widget/picker/OWNERS
index 775b0c7..716ab90 100644
--- a/tests/multivalentTests/src/com/android/launcher3/widget/picker/OWNERS
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/OWNERS
@@ -5,7 +5,6 @@
 #
 
 # Widget Picker OWNERS
-zakcohen@google.com
 shamalip@google.com
 wvk@google.com
 
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProviderTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProviderTest.kt
index 1da74cb..c1827bc 100644
--- a/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProviderTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProviderTest.kt
@@ -40,6 +40,7 @@
 import com.android.launcher3.widget.model.WidgetsListHeaderEntry
 import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider.WidgetPickerDataChangeListener
 import com.google.common.truth.Truth.assertThat
+import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -72,14 +73,14 @@
 
     private lateinit var appWidgetItem: WidgetItem
 
-    private var underTest = WidgetPickerDataProvider()
+    private lateinit var underTest: WidgetPickerDataProvider
 
     @Before
     fun setUp() {
         userHandle = UserHandle.CURRENT
         context = ActivityContextWrapper(ApplicationProvider.getApplicationContext())
         testInvariantProfile = LauncherAppState.getIDP(context)
-
+        underTest = WidgetPickerDataProvider(context)
         doAnswer { invocation: InvocationOnMock ->
                 val componentWithLabel = invocation.getArgument<Any>(0) as CachedObject
                 componentWithLabel.getComponent().shortClassName
@@ -90,6 +91,11 @@
         appWidgetItem = createWidgetItem()
     }
 
+    @After
+    fun tearDown() {
+        underTest.destroy()
+    }
+
     @Test
     fun setWidgets_invokesTheListener_andUpdatedWidgetsAvailable() {
         assertThat(underTest.get().allWidgets).isEmpty()
diff --git a/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt b/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt
index 1e54603..38fad6b 100644
--- a/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt
+++ b/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt
@@ -32,7 +32,6 @@
 import com.android.launcher3.util.rule.BackAndRestoreRule
 import com.android.launcher3.util.rule.setFlags
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -70,7 +69,6 @@
     }
 
     @Test
-    @Ignore("b/385147987")
     fun oldDatabasesNotPresentAfterRestore() {
         val dbController = ModelDbController(getInstrumentation().targetContext)
         if (Flags.gridMigrationRefactor()) {
diff --git a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
index 582cf3c..246219f 100644
--- a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
@@ -48,7 +48,6 @@
 import dagger.BindsInstance
 import dagger.Component
 import java.util.concurrent.CountDownLatch
-import java.util.function.Predicate
 import junit.framework.Assert.assertEquals
 import org.junit.After
 import org.junit.Before
@@ -96,7 +95,6 @@
     @Mock private lateinit var modelDelegate: ModelDelegate
     @Mock private lateinit var launcherBinder: BaseLauncherBinder
     private lateinit var launcherModel: LauncherModel
-    @Mock private lateinit var widgetsFilterDataProvider: WidgetsFilterDataProvider
     @Mock private lateinit var transaction: LoaderTransaction
     @Mock private lateinit var iconCache: IconCache
     @Mock private lateinit var idleLock: LooperIdleLock
@@ -130,7 +128,6 @@
         `when`(launcherBinder.newIdleLock(any())).thenReturn(idleLock)
         `when`(idleLock.awaitLocked(1000)).thenReturn(false)
         `when`(iconCache.getUpdateHandler()).thenReturn(iconCacheUpdateHandler)
-        `when`(widgetsFilterDataProvider.getDefaultWidgetsFilter()).thenReturn(Predicate { true })
         context.initDaggerComponent(
             DaggerLoaderTaskTest_TestComponent.builder()
                 .bindUserCache(userCache)
@@ -160,14 +157,7 @@
             val mockUserHandles = arrayListOf<UserHandle>(MAIN_HANDLE)
             `when`(userCache.userProfiles).thenReturn(mockUserHandles)
             `when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 1))
-            LoaderTask(
-                    app,
-                    bgAllAppsList,
-                    this,
-                    modelDelegate,
-                    launcherBinder,
-                    widgetsFilterDataProvider,
-                )
+            LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder)
                 .runSyncOnBackgroundThread()
             Truth.assertThat(
                     itemsIdMap
@@ -188,19 +178,11 @@
                 )
                 .isAtLeast(8)
             Truth.assertThat(itemsIdMap.size()).isAtLeast(40)
-            Truth.assertThat(widgetsModel.defaultWidgetsFilter).isNotNull()
         }
 
     @Test
     fun bindsLoadedDataCorrectly() {
-        LoaderTask(
-                app,
-                bgAllAppsList,
-                BgDataModel(),
-                modelDelegate,
-                launcherBinder,
-                widgetsFilterDataProvider,
-            )
+        LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
             .runSyncOnBackgroundThread()
 
         verify(launcherBinder).bindWorkspace(true, false)
@@ -209,7 +191,6 @@
         verify(launcherBinder).bindAllApps()
         verify(iconCacheUpdateHandler, times(4)).updateIcons(any(), any<CachingLogic<Any>>(), any())
         verify(launcherBinder).bindDeepShortcuts()
-        verify(widgetsFilterDataProvider).initPeriodicDataRefresh(any())
         verify(launcherBinder).bindWidgets()
         verify(modelDelegate).loadAndBindOtherItems(anyOrNull())
         verify(iconCacheUpdateHandler).finish()
@@ -227,15 +208,7 @@
             `when`(userManagerState?.isUserQuiet(MAIN_HANDLE)).thenReturn(true)
             `when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 1))
 
-            LoaderTask(
-                    app,
-                    bgAllAppsList,
-                    this,
-                    modelDelegate,
-                    launcherBinder,
-                    widgetsFilterDataProvider,
-                    userManagerState,
-                )
+            LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder, userManagerState)
                 .runSyncOnBackgroundThread()
 
             verify(bgAllAppsList)
@@ -256,15 +229,7 @@
             `when`(userManagerState?.isUserQuiet(MAIN_HANDLE)).thenReturn(true)
             `when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 3))
 
-            LoaderTask(
-                    app,
-                    bgAllAppsList,
-                    this,
-                    modelDelegate,
-                    launcherBinder,
-                    widgetsFilterDataProvider,
-                    userManagerState,
-                )
+            LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder, userManagerState)
                 .runSyncOnBackgroundThread()
 
             verify(bgAllAppsList)
@@ -303,14 +268,7 @@
         RestoreDbTask.setPending(spyContext)
 
         // When
-        LoaderTask(
-                app,
-                bgAllAppsList,
-                BgDataModel(),
-                modelDelegate,
-                launcherBinder,
-                widgetsFilterDataProvider,
-            )
+        LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
             .runSyncOnBackgroundThread()
 
         // Then
@@ -378,14 +336,7 @@
         Settings.Secure.putInt(spyContext.contentResolver, "launcher_broadcast_installed_apps", 0)
 
         // When
-        LoaderTask(
-                app,
-                bgAllAppsList,
-                BgDataModel(),
-                modelDelegate,
-                launcherBinder,
-                widgetsFilterDataProvider,
-            )
+        LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
             .runSyncOnBackgroundThread()
 
         // Then
@@ -424,14 +375,7 @@
         RestoreDbTask.setPending(spyContext)
 
         // When
-        LoaderTask(
-                app,
-                bgAllAppsList,
-                BgDataModel(),
-                modelDelegate,
-                launcherBinder,
-                widgetsFilterDataProvider,
-            )
+        LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
             .runSyncOnBackgroundThread()
 
         // Then
@@ -470,14 +414,7 @@
         RestoreDbTask.setPending(spyContext)
 
         // When
-        LoaderTask(
-                app,
-                bgAllAppsList,
-                BgDataModel(),
-                modelDelegate,
-                launcherBinder,
-                widgetsFilterDataProvider,
-            )
+        LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
             .runSyncOnBackgroundThread()
 
         // Then
@@ -507,15 +444,7 @@
             )
         val expectedAppInfo = AppInfo().apply { componentName = expectedComponent }
         // When
-        val loader =
-            LoaderTask(
-                app,
-                bgAllAppsList,
-                BgDataModel(),
-                modelDelegate,
-                launcherBinder,
-                widgetsFilterDataProvider,
-            )
+        val loader = LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
         val actualIconRequest =
             loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
         // Then
@@ -545,15 +474,7 @@
             )
         val expectedAppInfo = AppInfo().apply { componentName = expectedComponent }
         // When
-        val loader =
-            LoaderTask(
-                app,
-                bgAllAppsList,
-                BgDataModel(),
-                modelDelegate,
-                launcherBinder,
-                widgetsFilterDataProvider,
-            )
+        val loader = LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
         val actualIconRequest =
             loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
         // Then
@@ -584,15 +505,7 @@
         val expectedAppInfo =
             AppInfo().apply { componentName = ComponentName("differentPkg", "differentClass") }
         // When
-        val loader =
-            LoaderTask(
-                app,
-                bgAllAppsList,
-                BgDataModel(),
-                modelDelegate,
-                launcherBinder,
-                widgetsFilterDataProvider,
-            )
+        val loader = LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
         val actualIconRequest =
             loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
         // Then
@@ -619,15 +532,7 @@
             )
         val expectedAppInfo = AppInfo()
         // When
-        val loader =
-            LoaderTask(
-                app,
-                bgAllAppsList,
-                BgDataModel(),
-                modelDelegate,
-                launcherBinder,
-                widgetsFilterDataProvider,
-            )
+        val loader = LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
         val actualIconRequest =
             loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
         // Then
diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
index 124c18f..e38cfec 100644
--- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
+++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
@@ -10,7 +10,6 @@
 import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.tapl.TestHelpers;
 import com.android.launcher3.util.rule.FailureWatcher;
-import com.android.launcher3.util.window.WindowManagerProxy;
 
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
@@ -97,19 +96,20 @@
             }
 
             private void evaluateInLandscape() throws Throwable {
-                if (Flags.oneGridSpecs()
-                        && WindowManagerProxy.INSTANCE.get(mTest.mTargetContext)
-                        .isTaskbarDrawnInProcess()) {
-                    mTest.executeOnLauncher(launcher -> LauncherPrefs.get(launcher)
-                            .put(FIXED_LANDSCAPE_MODE, true)
-                    );
-                }
+                mTest.executeOnLauncher(launcher -> LauncherPrefs.get(launcher)
+                        .put(FIXED_LANDSCAPE_MODE, shouldHaveFixedLandscape(launcher)));
                 mTest.mDevice.setOrientationLeft();
                 mTest.mLauncher.setExpectedRotation(Surface.ROTATION_90);
                 AbstractLauncherUiTest.checkDetectedLeaks(mTest.mLauncher, true);
                 base.evaluate();
                 mTest.getDevice().pressHome();
             }
+
+            private boolean shouldHaveFixedLandscape(Launcher launcher) {
+                return Flags.oneGridSpecs()
+                        && !launcher.getDeviceProfile().isTablet
+                        && !launcher.getDeviceProfile().isMultiDisplay;
+            }
         };
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index f633d48..214f158 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -157,7 +157,7 @@
              LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                      "dismissing all tasks")) {
             final BySelector clearAllSelector = mLauncher.getOverviewObjectSelector("clear_all");
-            flingForwardUntilClearAllVisible();
+            flingForwardUntilClearAllVisibleImpl();
 
             final Runnable clickClearAll = () -> mLauncher.clickLauncherObject(
                     mLauncher.waitForObjectInContainer(verifyActiveContainer(),
@@ -181,10 +181,19 @@
      * Scrolls until Clear-all button is visible.
      */
     public void flingForwardUntilClearAllVisible() {
-        final BySelector clearAllSelector = mLauncher.getOverviewObjectSelector("clear_all");
-        for (int i = 0; i < FLINGS_FOR_DISMISS_LIMIT
-                && !verifyActiveContainer().hasObject(clearAllSelector); ++i) {
-            flingForwardImpl();
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+            flingForwardUntilClearAllVisibleImpl();
+        }
+    }
+
+    private void flingForwardUntilClearAllVisibleImpl() {
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "flinging forward to clear all")) {
+            final BySelector clearAllSelector = mLauncher.getOverviewObjectSelector("clear_all");
+            for (int i = 0; i < FLINGS_FOR_DISMISS_LIMIT && !verifyActiveContainer().hasObject(
+                    clearAllSelector); ++i) {
+                flingForwardImpl();
+            }
         }
     }
 
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 16faf14..e0d2f39 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -929,7 +929,11 @@
                     waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
                     waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
                     waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
-                    waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
+                    if (isTaskbarShownOnHome()) {
+                        waitForSystemLauncherObject(TASKBAR_RES_ID);
+                    } else {
+                        waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
+                    }
 
                     return waitForLauncherObject(WORKSPACE_RES_ID);
                 }
@@ -947,7 +951,9 @@
                     waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
                     waitUntilLauncherObjectGone(WIDGETS_RES_ID);
                     waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
-                    waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
+                    if (isTransientTaskbar()) {
+                        waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
+                    }
                     waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
                     waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
 
@@ -959,7 +965,8 @@
                     waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
                     waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
 
-                    if (is3PLauncher() && isTablet() && !isTransientTaskbar()) {
+                    if ((is3PLauncher() && isTablet() && !isTransientTaskbar())
+                            || isTaskbarShownOnHome()) {
                         waitForSystemLauncherObject(TASKBAR_RES_ID);
                     } else {
                         waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
@@ -2421,6 +2428,12 @@
                 .getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
 
+    /** Whether taskbar will be shown on home for current default display. */
+    public boolean isTaskbarShownOnHome() {
+        return getTestInfo(TestProtocol.REQUEST_TASKBAR_SHOWN_ON_HOME).getBoolean(
+                TEST_INFO_RESPONSE_FIELD);
+    }
+
     public boolean isImeDocked() {
         return getTestInfo(TestProtocol.REQUEST_TASKBAR_IME_DOCKED).getBoolean(
                 TestProtocol.TEST_INFO_RESPONSE_FIELD);
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 8fbb5e3..02c6630 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -73,16 +73,18 @@
             return getCombinedSplitTaskHeight();
         }
 
-        UiObject2 taskSnapshot1 = findObjectInTask((isDesktop() ? DESKTOP : DEFAULT).snapshotRes);
-        return taskSnapshot1.getVisibleBounds().height();
+        if (isDesktop()) {
+            return getTaskSnapshot(DESKTOP).getVisibleBounds().height();
+        }
+        return getTaskSnapshot(DEFAULT).getVisibleBounds().height();
     }
 
     /**
      * Calculates the visible height for split tasks, containing 2 snapshot tiles and a divider.
      */
     private int getCombinedSplitTaskHeight() {
-        UiObject2 taskSnapshot1 = findObjectInTask(SPLIT_TOP_OR_LEFT.snapshotRes);
-        UiObject2 taskSnapshot2 = findObjectInTask(SPLIT_BOTTOM_OR_RIGHT.snapshotRes);
+        UiObject2 taskSnapshot1 = getTaskSnapshot(SPLIT_TOP_OR_LEFT);
+        UiObject2 taskSnapshot2 = getTaskSnapshot(SPLIT_BOTTOM_OR_RIGHT);
 
         // If the split task is partly off screen, taskSnapshot1 can be invisible.
         if (taskSnapshot1 == null) {
@@ -97,34 +99,6 @@
         return bottom - top;
     }
 
-    /**
-     * Returns the width of the visible task, or the combined width of two tasks in split with a
-     * divider between.
-     */
-    int getVisibleWidth() {
-        if (isGrouped()) {
-            return getCombinedSplitTaskWidth();
-        }
-
-        UiObject2 taskSnapshot1 = findObjectInTask(DEFAULT.snapshotRes);
-        return taskSnapshot1.getVisibleBounds().width();
-    }
-
-    /**
-     * Calculates the visible width for split tasks, containing 2 snapshot tiles and a divider.
-     */
-    private int getCombinedSplitTaskWidth() {
-        UiObject2 taskSnapshot1 = findObjectInTask(SPLIT_TOP_OR_LEFT.snapshotRes);
-        UiObject2 taskSnapshot2 = findObjectInTask(SPLIT_BOTTOM_OR_RIGHT.snapshotRes);
-
-        int left = Math.min(
-                taskSnapshot1.getVisibleBounds().left, taskSnapshot2.getVisibleBounds().left);
-        int right = Math.max(
-                taskSnapshot1.getVisibleBounds().right, taskSnapshot2.getVisibleBounds().right);
-
-        return right - left;
-    }
-
     public int getTaskCenterX() {
         return mTask.getVisibleCenter().x;
     }
@@ -142,6 +116,22 @@
     }
 
     /**
+     * Returns the task snapshot (thumbnail) for the given `OverviewTaskContainer`.
+     *
+     * For some reason `BySelector` does not work with `hasChild` or `hasParent` so instead we
+     * grab all the views matching the id: "snapshot" and filter for the correct parent.
+     */
+    private UiObject2 getTaskSnapshot(OverviewTaskContainer overviewTaskContainer) {
+        BySelector snapshotSelector = mLauncher.getOverviewObjectSelector("snapshot");
+        List<UiObject2> snapshots = mTask.findObjects(snapshotSelector);
+        return snapshots.stream()
+                .filter(snapshot -> snapshot.getParent().getResourceName()
+                        .contains(overviewTaskContainer.taskContentViewRes))
+                .findFirst()
+                .orElse(snapshots.getFirst());
+    }
+
+    /**
      * Dismisses the task by swiping up.
      */
     public void dismiss() {
@@ -304,17 +294,13 @@
         }
     }
 
-    private UiObject2 findObjectInTask(String resName) {
-        return mTask.findObject(mLauncher.getOverviewObjectSelector(resName));
-    }
-
     /**
      * Returns whether the given String is contained in this Task's contentDescription. Also returns
      * true if both Strings are null.
      */
     public boolean containsContentDescription(String expected,
             OverviewTaskContainer overviewTaskContainer) {
-        String actual = findObjectInTask(overviewTaskContainer.snapshotRes).getContentDescription();
+        String actual = getTaskSnapshot(overviewTaskContainer).getContentDescription();
         if (actual == null && expected == null) {
             return true;
         }
@@ -360,19 +346,19 @@
      */
     public enum OverviewTaskContainer {
         // The main task when the task is not split.
-        DEFAULT("snapshot", "icon"),
+        DEFAULT("task_content_view", "icon"),
         // The first task in split task.
-        SPLIT_TOP_OR_LEFT("snapshot", "icon"),
+        SPLIT_TOP_OR_LEFT("task_content_view", "icon"),
         // The second task in split task.
-        SPLIT_BOTTOM_OR_RIGHT("bottomright_snapshot", "bottomRight_icon"),
+        SPLIT_BOTTOM_OR_RIGHT("bottomright_task_content_view", "bottomRight_icon"),
         // The desktop task.
         DESKTOP("background", "icon");
 
-        public final String snapshotRes;
+        public final String taskContentViewRes;
         public final String iconAppRes;
 
-        OverviewTaskContainer(String snapshotRes, String iconAppRes) {
-            this.snapshotRes = snapshotRes;
+        OverviewTaskContainer(String taskContentViewRes, String iconAppRes) {
+            this.taskContentViewRes = taskContentViewRes;
             this.iconAppRes = iconAppRes;
         }
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/Taskbar.java b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
index b4aaab7..d4e6d31 100644
--- a/tests/tapl/com/android/launcher3/tapl/Taskbar.java
+++ b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
@@ -115,6 +115,23 @@
         }
     }
 
+    /**
+     *  Opens the Home all apps page by clicking the taskbar all apps icon. To be used to open all
+     *  apps when taskbar is visible on home.
+     */
+    public HomeAllApps openAllAppsOnHome() {
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to open home all apps from taskbar");
+             LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+
+            mLauncher.clickLauncherObject(mLauncher.waitForObjectInContainer(
+                    mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID),
+                    getAllAppsButtonSelector()));
+
+            return mLauncher.getAllApps();
+        }
+    }
+
     /** Opens the Taskbar all apps page with the meta keyboard shortcut. */
     public TaskbarAllApps openAllAppsFromKeyboardShortcut() {
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 4230643..68c7049 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -58,7 +58,7 @@
  */
 public final class Workspace extends Home {
     private static final int FLING_STEPS = 10;
-    private static final int DEFAULT_DRAG_STEPS = 15;
+    private static final int DEFAULT_DRAG_STEPS = 20;
     private static final String DROP_BAR_RES_ID = "drop_target_bar";
     private static final String DELETE_TARGET_TEXT_ID = "delete_target_text";
     private static final String UNINSTALL_TARGET_TEXT_ID = "uninstall_target_text";
@@ -609,7 +609,7 @@
                 launcher,
                 launchable,
                 destSupplier,
-                /* isDecelerating= */ false,
+                /* isDecelerating= */ !isDraggingToFolder,
                 () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
                 /* expectDropEvents= */ null,
                 /* startsActivity = */ false,
@@ -682,7 +682,7 @@
 
             launcher.movePointer(dragStart, targetDest,
                     DEFAULT_DRAG_STEPS, isDecelerating, downTime, SystemClock.uptimeMillis(),
-                    true, LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
+                    !isDraggingToFolder, LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
 
             dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents, startsActivity);
         }