Merge "Update creationFlag state only on non PSApp" into main
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
index 35a91d7..a880538 100644
--- a/aconfig/launcher_growth.aconfig
+++ b/aconfig/launcher_growth.aconfig
@@ -1,5 +1,5 @@
 package: "com.android.launcher3"
-container: "system"
+container: "system_ext"
 
 flag {
     name: "enable_growth_nudge"
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 8555376..1698050 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -165,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,
@@ -205,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,
@@ -223,7 +223,7 @@
             // the correct index.
             mQuickSwitchViewController.openQuickSwitchView(
                     mTasks,
-                    mNumHiddenTasks,
+                    wasOpenedFromTaskbar ? 0 : mNumHiddenTasks,
                     /* updateTasks= */ true,
                     currentFocusedIndex == -1 && !mControllerCallbacks.isFirstTaskRunning()
                             ? 0 : currentFocusedIndex,
@@ -232,7 +232,7 @@
                     mWasDesktopTaskFilteredOut,
                     wasOpenedFromTaskbar);
         }, shouldShowDesktopTasks ? RecentsFilterState.EMPTY_FILTER
-                : RecentsFilterState.getEmptyDesktopTaskFilter());
+                : RecentsFilterState.getDesktopTaskFilter());
     }
 
     private boolean shouldExcludeTask(GroupTask task, Set<Integer> taskIdsToExclude) {
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 943c44e..e8e5b30 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -15,8 +15,8 @@
  */
 package com.android.launcher3.taskbar;
 
-import static android.content.Context.RECEIVER_NOT_EXPORTED;
 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;
 
@@ -25,6 +25,7 @@
 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;
@@ -32,7 +33,6 @@
 import static com.android.launcher3.util.DisplayController.CHANGE_TASKBAR_PINNING;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
-import static com.android.launcher3.taskbar.growth.GrowthConstants.BROADCAST_SHOW_NUDGE;
 import static com.android.quickstep.util.SystemActionConstants.ACTION_SHOW_TASKBAR;
 import static com.android.quickstep.util.SystemActionConstants.SYSTEM_ACTION_ID_TASKBAR;
 
@@ -81,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;
@@ -178,6 +179,12 @@
      */
     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) {
@@ -206,10 +213,12 @@
                 if ((flags & CHANGE_SHOW_LOCKED_TASKBAR) != 0) {
                     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)) {
                         recreateTaskbars();
                     }
@@ -232,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);
                 }
             }
         }
@@ -295,6 +301,7 @@
                                 displayId);
                         if (taskbarActivityContext != null
                                 && !taskbarActivityContext.isInOverview()) {
+                            mShouldIgnoreNextDesktopModeChangeFromDisplayController = true;
                             AnimatorSet animatorSet = taskbarActivityContext.onDestroyAnimation(
                                     TASKBAR_DESTROY_DURATION);
                             animatorSet.addListener(AnimatorListeners.forEndCallback(
@@ -311,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();
+                        }
                     }
                 }
 
@@ -511,14 +522,14 @@
     /**
      * 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);
         }
     }
 
@@ -618,7 +629,7 @@
     /** Creates a {@link TaskbarUIController} to use with non default displays. */
     private TaskbarUIController createTaskbarUIControllerForNonDefaultDisplay(int displayId) {
         debugPrimaryTaskbar("createTaskbarUIControllerForNonDefaultDisplay");
-        if (RecentsDisplayModel.enableOverviewInWindow()) {
+        if (RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
             RecentsViewContainer rvc = mRecentsDisplayModel.getRecentsWindowManager(displayId);
             if (rvc != null) {
                 return createTaskbarUIControllerForRecentsViewContainer(rvc);
@@ -722,8 +733,7 @@
                         displayId);
                 taskbar.updateDeviceProfile(dp);
             }
-            mSharedState.startTaskbarVariantIsTransient =
-                    DisplayController.isTransientTaskbar(taskbar);
+            mSharedState.startTaskbarVariantIsTransient = taskbar.isTransientTaskbar();
             mSharedState.allAppsVisible = mSharedState.allAppsVisible && isLargeScreenTaskbar;
             taskbar.init(mSharedState, duration);
 
@@ -756,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 */);
         }
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/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 aab8ad1..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);
@@ -1455,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 04e1905..23dc81d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
@@ -36,8 +36,8 @@
 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.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE
 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
@@ -76,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(
@@ -168,7 +165,7 @@
             builder.setFloat(
                 recentsView,
                 DESK_EXPLODE_PROGRESS,
-                if (toState.isRecentsViewVisible) 1f else 0f,
+                if (toState.showExplodedDesktopView()) 1f else 0f,
                 getOverviewInterpolator(fromState, toState),
             )
         }
@@ -303,8 +300,8 @@
         )
         recentsView.addDeskButton?.let {
             propertySetter.setFloat(
-                it.visibilityAlphaProperty,
-                MULTI_PROPERTY_VALUE,
+                it,
+                AddDesktopButton.VISIBILITY_ALPHA,
                 if (state.areElementsVisible(launcher, LauncherState.ADD_DESK_BUTTON)) 1f else 0f,
                 LINEAR,
             )
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index b1196af..a5b1ee7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -97,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/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 5fdedcc..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;
 
@@ -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 f47937c..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;
 
@@ -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 a178c3c..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) {
@@ -410,7 +453,7 @@
                                     tmpLockedUsers.get(task2Key.userId) /* isLocked */);
                 } else {
                     // Is fullscreen task
-                    if (!isFirstVisibleTaskFound) {
+                    if (isFirstVisibleTaskFound) {
                         boolean isExcluded = (taskInfo1.baseIntent.getFlags()
                                 & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
                         if (taskInfo1.isTopActivityTransparent && isExcluded) {
@@ -460,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/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/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 537092f..554cea2 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -26,7 +26,6 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
-import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
 import static com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
 import static com.android.quickstep.views.RecentsView.DESKTOP_CAROUSEL_DETACH_PROGRESS;
@@ -52,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;
@@ -98,12 +98,12 @@
     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().getVisibilityAlphaProperty(),
-                    MULTI_PROPERTY_VALUE, addDeskButtonAlpha, LINEAR);
+            setter.setFloat(mRecentsView.getAddDeskButton(), AddDesktopButton.VISIBILITY_ALPHA,
+                    addDeskButtonAlpha, LINEAR);
         }
         float overviewButtonAlpha = state.hasOverviewActions() ? 1 : 0;
         setter.setFloat(mRecentsViewContainer.getActionsView().getVisibilityAlpha(),
@@ -132,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 f722c5d..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;
@@ -46,16 +47,18 @@
     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_ADD_DESK_BUTTON);
+                    | FLAG_ADD_DESK_BUTTON | FLAG_SHOW_EXPLODED_DESKTOP_VIEW);
     public static final RecentsState MODAL_TASK = new ModalState(1,
             FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_ACTIONS | FLAG_MODAL
-                    | FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_RECENTS_VIEW_VISIBLE);
+                    | 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
@@ -64,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) {
@@ -174,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 244c3b2..37359a1 100644
--- a/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt
+++ b/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt
@@ -20,10 +20,12 @@
 import android.graphics.Canvas
 import android.graphics.Rect
 import android.util.AttributeSet
-import android.view.View
+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
@@ -36,44 +38,17 @@
 class AddDesktopButton @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
     ImageButton(context, attrs) {
 
-    private enum class Alpha {
-        CONTENT,
-        VISIBILITY,
-    }
-
     private val addDeskButtonAlpha = MultiValueAlpha(this, Alpha.entries.size)
-
-    var contentAlpha
-        set(value) {
-            addDeskButtonAlpha.get(Alpha.CONTENT.ordinal).value = value
-        }
-        get() = addDeskButtonAlpha.get(Alpha.CONTENT.ordinal).value
-
-    val visibilityAlphaProperty: MultiPropertyFactory<View>.MultiProperty
-        get() = addDeskButtonAlpha.get(Alpha.VISIBILITY.ordinal)
-
-    private enum class TranslationX {
-        GRID,
-        OFFSET,
-    }
+    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(
@@ -109,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 =
@@ -123,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 6b91df1..bb2aa75 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;
@@ -74,6 +73,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 +143,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 +200,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;
@@ -212,6 +212,7 @@
 import com.android.quickstep.recents.viewmodel.RecentsViewModel;
 import com.android.quickstep.util.ActiveGestureProtoLogProxy;
 import com.android.quickstep.util.AnimUtils;
+import com.android.quickstep.util.DesksUtils;
 import com.android.quickstep.util.DesktopTask;
 import com.android.quickstep.util.GroupTask;
 import com.android.quickstep.util.LayoutUtils;
@@ -254,11 +255,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 +574,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 +603,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 +863,6 @@
     private final Matrix mTmpMatrix = new Matrix();
 
     private int mTaskViewCount = 0;
-
     @Nullable
     public TaskView getFirstTaskView() {
         return mUtils.getFirstTaskView();
@@ -884,22 +882,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 +910,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 +1285,7 @@
                 Log.e(TAG, "Ongoing initializations could not be killed", e);
             }
             mHelper.onDestroy();
-            RecentsDependencies.destroy();
+            RecentsDependencies.destroy(getContext());
         }
     }
 
@@ -1462,7 +1460,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 +1472,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 +1508,7 @@
 
     @Nullable
     private TaskView getLastGridTaskView() {
-        return getLastGridTaskView(getTopRowIdArray(), getBottomRowIdArray());
+        return getLastGridTaskView(mUtils.getTopRowIdArray(), mUtils.getBottomRowIdArray());
     }
 
     @Nullable
@@ -1543,7 +1554,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 +1589,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,6 +1926,8 @@
             return;
         }
 
+        // TODO: b/400532675 - The use of `currentTaskIds`, `runningTaskIds`, and `focusedTaskIds`
+        // needs to be audited so that they can work with empty desks that have no tasks.
         int[] currentTaskIds;
         TaskView currentTaskView = getTaskViewAt(mCurrentPage);
         if (currentTaskView != null) {
@@ -2493,6 +2509,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 +2830,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 +2916,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 +2936,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 +2954,6 @@
             }
         }
 
-        BaseState<?> endState = mSizeStrategy.stateFromGestureEndTarget(endTarget);
         if (endState.displayOverviewTasksAsGrid(mContainer.getDeviceProfile())) {
             TaskView runningTaskView = getRunningTaskView();
             float runningTaskGridTranslationX = 0;
@@ -2999,9 +3026,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 +3070,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 +3098,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 +3131,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 +3174,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 +3574,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 +3582,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 +3786,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 +3910,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 +4050,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 +4145,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 +4254,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 +4270,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 +4353,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 +4477,6 @@
                         animationEndProgress
                 )
         );
-
-        if (view instanceof TaskView) {
-            mTaskViewsDismissPrimaryTranslations.put((TaskView) view, scrollDiffPerPage);
-        }
         if (mEnableDrawingLiveTile && view instanceof TaskView
                 && ((TaskView) view).isRunningTask()) {
             pendingAnimation.addOnFrameCallback(() -> {
@@ -4470,41 +4521,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 +4530,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 +4549,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 (DesksUtils.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 +4595,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 +4641,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 +4702,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 +4730,7 @@
     private void createDesk(View view) {
         SystemUiProxy.INSTANCE
                 .get(getContext())
-                .createDesktop(mContainer.getDisplay().getDisplayId());
+                .createDesk(mContainer.getDisplay().getDisplayId());
     }
 
     @Override
@@ -4954,6 +4994,10 @@
         }
         setPivotX(mTempPointF.x);
         setPivotY(mTempPointF.y);
+        if (enableGridOnlyOverview()) {
+            runActionOnRemoteHandles(remoteTargetHandle ->
+                    remoteTargetHandle.getTaskViewSimulator().setPivotOverride(mTempPointF));
+        }
     }
 
     /**
@@ -5341,8 +5385,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()));
                 }
@@ -5400,7 +5443,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);
@@ -5408,7 +5451,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);
             }
@@ -5783,6 +5826,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) {
@@ -6019,7 +6065,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());
@@ -6202,9 +6248,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();
@@ -6274,13 +6318,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);
@@ -6295,6 +6332,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);
         }
@@ -6403,7 +6448,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(
@@ -6702,7 +6748,7 @@
                 .displayOverviewTasksAsGrid(mContainer.getDeviceProfile()));
     }
 
-    private boolean showAsFullscreen() {
+    protected boolean showAsFullscreen() {
         return mOverviewFullscreenEnabled
                 && mCurrentGestureEndTarget != GestureState.GestureEndTarget.RECENTS;
     }
@@ -6917,6 +6963,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..037bef6 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
+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 (DesksUtils.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 }
@@ -265,8 +310,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 +350,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.kt b/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
index 95336cf..6bc0666 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
@@ -22,7 +22,6 @@
 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
@@ -32,6 +31,7 @@
 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
@@ -67,6 +67,17 @@
     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
     }
@@ -75,7 +86,7 @@
         if (ev.action == MotionEvent.ACTION_DOWN) {
             if (!recentsViewContainer.dragLayer.isEventOverView(this, ev)) {
                 // TODO: log this once we have a new container type for it?
-                close(true)
+                animateOpenOrClosed(true)
                 return true
             }
         }
@@ -83,7 +94,7 @@
     }
 
     override fun handleClose(animate: Boolean) {
-        animateClose()
+        animateOpenOrClosed(true, animated = false)
     }
 
     override fun isOfType(type: Int): Boolean = (type and TYPE_TASK_MENU) != 0
@@ -103,13 +114,9 @@
 
     override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
         var heightMeasure = heightMeasureSpec
-        if (!(enableOverviewIconMenu() && taskView.isOnGridBottomRow())) {
-            // TODO(b/326952853): Cap menu height for grid bottom row in a way that doesn't break
-            // additionalTranslationY.
-            val maxMenuHeight = calculateMaxHeight()
-            if (MeasureSpec.getSize(heightMeasure) > maxMenuHeight) {
-                heightMeasure = MeasureSpec.makeMeasureSpec(maxMenuHeight, MeasureSpec.AT_MOST)
-            }
+        val maxMenuHeight = calculateMaxHeight()
+        if (MeasureSpec.getSize(heightMeasure) > maxMenuHeight) {
+            heightMeasure = MeasureSpec.makeMeasureSpec(maxMenuHeight, MeasureSpec.AT_MOST)
         }
         super.onMeasure(widthMeasureSpec, heightMeasure)
     }
@@ -157,7 +164,18 @@
             recentsViewContainer.layoutInflater.inflate(R.layout.task_view_menu_option, this, false)
                 as LinearLayout
         if (enableOverviewIconMenu()) {
-            (menuOptionView.background as GradientDrawable).cornerRadius = 0f
+            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),
@@ -196,7 +214,7 @@
                 taskContainer.stagePosition,
             )
         // Gravity set to Left instead of Start as sTempRect.left measures Left distance not Start
-        params.gravity = Gravity.START
+        params.gravity = Gravity.LEFT
         layoutParams = params
         scaleX = taskView.scaleX
         scaleY = taskView.scaleY
@@ -208,6 +226,13 @@
         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,
@@ -260,11 +285,7 @@
     private val iconView: View
         get() = taskContainer.iconView.asView()
 
-    private fun animateClose() {
-        animateOpenOrClosed(true)
-    }
-
-    private fun animateOpenOrClosed(closing: Boolean) {
+    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
@@ -312,7 +333,12 @@
                 }
             }
         )
-        val animationDuration = if (closing) REVEAL_CLOSE_DURATION else REVEAL_OPEN_DURATION
+        val animationDuration =
+            when {
+                animated && closing -> REVEAL_CLOSE_DURATION
+                animated && !closing -> REVEAL_OPEN_DURATION
+                else -> 0L
+            }
         openCloseAnimator!!.setDuration(animationDuration)
         openCloseAnimator!!.start()
     }
@@ -344,15 +370,17 @@
      * view will scroll. The maximum menu size will sit inside the task with a margin on the top and
      * bottom.
      */
-    private fun calculateMaxHeight(): Int {
-        val taskInsetMargin = resources.getDimension(R.dimen.task_card_margin)
-        return taskView.pagedOrientationHandler.getTaskMenuHeight(
-            taskInsetMargin,
-            recentsViewContainer.deviceProfile,
-            translationX,
-            translationY,
+    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
@@ -361,18 +389,23 @@
     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()) {
-            // Animate menu up for enough room to display full menu when task on bottom row.
-            val menuBottom = height + menuTranslationYBeforeOpen
-            val taskBottom = taskView.height + taskView.persistentTranslationY
-            val taskbarTop =
-                (recentsViewContainer.deviceProfile.heightPx -
-                        recentsViewContainer.deviceProfile.overviewActionsClaimedSpaceBelow)
-                    .toFloat()
-            val midpoint = (taskBottom + taskbarTop) / 2f
-            additionalTranslationY = (-max((menuBottom - midpoint).toDouble(), 0.0)).toFloat()
+            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,
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 27db6d6..55432b8 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
@@ -140,6 +141,7 @@
         /** Returns whether the task is part of overview grid and not being focused. */
         get() = container.deviceProfile.isTablet && !isLargeTile
 
+    // TODO: b/400532675 - This will not work for empty desks until b/400532675 is fixed.
     val isRunningTask: Boolean
         get() = this === recentsView?.runningTaskView
 
@@ -220,7 +222,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 +303,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 +443,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 +492,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 +501,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 +522,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 +534,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 +721,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 +741,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 +776,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 +811,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 +844,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 +863,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 +881,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 +910,7 @@
 
     protected fun createTaskContainer(
         task: Task,
+        @IdRes taskContentViewId: Int,
         @IdRes thumbnailViewId: Int,
         @IdRes iconViewId: Int,
         @IdRes showWindowViewId: Int,
@@ -921,10 +919,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 +1012,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 +1026,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 +1476,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 +1603,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 +1619,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 +1667,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 +1705,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 +1719,7 @@
         updateFullscreenParams(thumbnailFullscreenParams)
         taskContainers.forEach {
             if (enableRefactorTaskThumbnail()) {
-                it.thumbnailView.cornerRadius = thumbnailFullscreenParams.currentCornerRadius
+                it.taskContentView.cornerRadius = thumbnailFullscreenParams.currentCornerRadius
             } else {
                 it.thumbnailViewDeprecated.setFullscreenParams(thumbnailFullscreenParams)
             }
@@ -1765,7 +1768,7 @@
         dismissScale = 1f
         translationZ = 0f
         setIconVisibleForGesture(true)
-        settledProgressDismiss.value = 1f
+        settledProgressDismiss = 1f
         setColorTint(0f, 0)
     }
 
@@ -1787,23 +1790,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 +1825,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/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/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/TaskbarWindowSandboxContext.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt
index e6806b7..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.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,6 +138,20 @@
     @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 =
@@ -144,6 +160,7 @@
             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/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 b9f0ba2..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>
@@ -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 abaaa89..578d35d 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/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-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 e016f20..24235d1 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -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 d2bca52..7c676e9 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -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 7aa709d..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>
@@ -440,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 b3a4309..30e3a2b 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -20,6 +20,10 @@
 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;
@@ -35,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;
@@ -129,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;
@@ -164,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;
@@ -201,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;
@@ -209,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.
@@ -242,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);
@@ -287,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 =
@@ -337,6 +384,11 @@
         mForceHideDot = false;
         setBackground(null);
 
+        mLineIndicatorColor = Color.TRANSPARENT;
+        mLineIndicatorScale = 0;
+        mLineIndicatorAnimStartDelay = 0;
+        cancelLineIndicatorAnim();
+
         setTag(null);
         if (mIconLoadRequest != null) {
             mIconLoadRequest.cancel();
@@ -429,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) {
@@ -772,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,
@@ -965,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/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/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/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/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 37f5189..a9a26a1 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -54,6 +54,9 @@
 /**
  * {@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 {
 
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 b1653d0..c4fed71 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -29,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;
@@ -57,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;
@@ -83,7 +83,6 @@
     private boolean mAppDrawerShown = false;
 
     private StringCache mStringCache;
-    private boolean mBindingItems = false;
     private SecondaryDisplayPredictions mSecondaryDisplayPredictions;
 
     private final int[] mTempXY = new int[2];
@@ -95,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);
@@ -131,8 +109,7 @@
         }
 
         mDragController.addDragListener(this);
-        mPopupDataProvider = new PopupDataProvider(
-                mAppsView.getAppsStore()::updateNotificationDots);
+        mPopupDataProvider = new PopupDataProvider(this);
 
         mModel.addCallbacksAndLoad(this);
     }
@@ -260,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);
     }
@@ -306,6 +272,8 @@
         mStringCache = cache;
     }
 
+    @Override
+    @NonNull
     public PopupDataProvider getPopupDataProvider() {
         return mPopupDataProvider;
     }
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/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/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/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/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/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);
         }