[automerger skipped] [automerged blank] Import translations. DO NOT MERGE ANYWHERE 2p: f2e186c6df am: a72c102031 -s ours

am skip reason: subject contains skip directive

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/16097527

Change-Id: I62b04a7d14e92ab68cfa04d179314b9b63334c3c
diff --git a/Android.bp b/Android.bp
index 45d022f..c8d9186 100644
--- a/Android.bp
+++ b/Android.bp
@@ -33,6 +33,9 @@
 
 android_library {
     name: "launcher-aosp-tapl",
+    libs: [
+        "framework-statsd",
+    ],
     static_libs: [
         "androidx.annotation_annotation",
         "androidx.test.runner",
@@ -192,6 +195,9 @@
     resource_dirs: [
         "quickstep/res",
     ],
+    libs: [
+        "framework-statsd",
+    ],
     static_libs: [
         "Launcher3ResLib",
         "SystemUISharedLib",
@@ -224,7 +230,6 @@
     srcs: ["proguard.flags"],
 }
 
-
 // Library with all the dependencies for building Launcher Go
 android_library {
     name: "LauncherGoResLib",
@@ -253,3 +258,27 @@
     },
 }
 
+// Build rule for Quickstep library
+android_library {
+    name: "Launcher3QuickStepLib",
+    srcs: [
+        ":launcher-src-no-build-config",
+    ],
+    resource_dirs: [
+        "quickstep/res",
+    ],
+    libs: [
+        "framework-statsd",
+    ],
+    static_libs: [
+        "SystemUI-statsd",
+        "SystemUISharedLib",
+        "Launcher3CommonDepsLib"
+    ],
+    manifest: "quickstep/AndroidManifest.xml",
+    platform_apis: true,
+    min_sdk_version: "current",
+    lint: {
+        baseline_filename: "lint-baseline-launcher3.xml",
+    },
+}
diff --git a/Android.mk b/Android.mk
index c222f24..c1dbc53 100644
--- a/Android.mk
+++ b/Android.mk
@@ -53,42 +53,6 @@
 include $(BUILD_PACKAGE)
 
 #
-# Build rule for Quickstep library.
-#
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_AAPT2_ONLY := true
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    SystemUI-statsd \
-    SystemUISharedLib
-ifneq (,$(wildcard frameworks/base))
-  LOCAL_PRIVATE_PLATFORM_APIS := true
-else
-  LOCAL_SDK_VERSION := system_current
-  LOCAL_MIN_SDK_VERSION := 26
-endif
-LOCAL_MODULE := Launcher3QuickStepLib
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib
-
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src) \
-    $(call all-java-files-under, quickstep/src) \
-    $(call all-java-files-under, src_shortcuts_overrides)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/quickstep/res
-LOCAL_PROGUARD_ENABLED := disabled
-
-
-LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-#
 # Build rule for Quickstep app.
 #
 include $(CLEAR_VARS)
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 4eecf29..eee6db5 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -30,7 +30,6 @@
     with some minor changed based on the derivative app.
     -->
 
-    <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.CALL_PHONE" />
     <uses-permission android:name="android.permission.SET_WALLPAPER" />
     <uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 8066816..c72f62d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -31,6 +31,7 @@
         android:fullBackupOnly="true"
         android:fullBackupContent="@xml/backupscheme"
         android:hardwareAccelerated="true"
+        android:debuggable="true"
         android:icon="@drawable/ic_launcher_home"
         android:label="@string/derived_app_name"
         android:theme="@style/AppTheme"
diff --git a/buglist_with_title.txt b/buglist_with_title.txt
deleted file mode 100644
index aa8b413..0000000
--- a/buglist_with_title.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-144170434  twickham  P1        FIXED   Improve Overview ->  Home transition ----
-149934536  twickham  P2        FIXED   Update gesture nav pullback logic ----
-154951045  peanutbutter  P1        FIXED   Odd animation occuring at times when swiping to home ----
-154964045  awickham  P2        FIXED   "Clear all" text is not in the middle of app's window vertically ----
-158701272  twickham  P4        FIXED   Discontinuities when long-swiping to home ----
-160361464  tracyzhou  P2        FIXED   Place launcher above the target app in live tile mode ----
-160568387  twickham  P2        FIXED   Can't get to app switcher by swiping up (motion pause not detected) ----
-160718310  xuqiu     P1        FIXED   With "Select" overview action selected, App icon is missing in other overview apps after orientation change ----
-160748731  sunnygoyal  P2        ASSIGNED  Unify prediction model with Launcher model ----
-160759508  twickham  P2        FIXED   Swipe up cannot back to home screen in overview. ----
-161273376  xuqiu     P2        FIXED   [Overview Actions] Add logging and helpful messages ----
-161536946  twickham  P2        FIXED   Haptics don't indicate snap-to in overview,  ----
-161685099  winsonc   P2        FIXED   Screen still stay at the quick settings/notification when I swipe up with 3 finger to check the all apps. ----
-161801331  hyunyoungs  P2        FIXED   Change AllAppsSearch plugin to support only data fetch ----
-161901771  xuqiu     P1        FIXED   Overlapping layer of highlights with app layout getting darker when keep rotating the device from "Feedback" viewpoint in split screen ----
-161939759  sunnygoyal  P2        FIXED   RD1A: Going to overview in landscape mode clips the screen content ----
-162012217  perumaal  P2        ASSIGNED  Leaked Activity Caused by Gleams ----
-162454040  bookatz   P2        ASSIGNED  Create multiuser test that checks that opening an app works properly ----
-162480567  sfufa     P4        FIXED   Enable Item Decorations for search items ----
-162564471  tracyzhou  P2        FIXED   [Live tile] Handle tapping overview actions in live tile mode ----
-162623012  zakcohen  P1        ASSIGNED  Enable chips flag ----
-162812884  winsonc   P2        ASSIGNED  [R]The color have not changed in some page after turning on the dark theme. ----
-162861289  hyunyoungs  P2        FIXED   Add FocusIndicator support to DEVICE_SEARCH feature in S ----
-162871508  sfufa     P2        ASSIGNED  Introduce support for Hero app section ----
diff --git a/build.gradle b/build.gradle
index a7eef13..617738a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,7 +2,6 @@
     repositories {
         mavenCentral()
         google()
-        jcenter()
     }
     dependencies {
         classpath GRADLE_CLASS_PATH
diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
index 72b8d3f..0f61d14 100644
--- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
+++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
@@ -18,6 +18,8 @@
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
+import android.app.Activity;
+import android.app.Application;
 import android.content.Context;
 import android.os.Binder;
 import android.os.Bundle;
@@ -31,7 +33,10 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.LinkedList;
+import java.util.Map;
+import java.util.WeakHashMap;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -41,9 +46,48 @@
 public class DebugTestInformationHandler extends TestInformationHandler {
     private static LinkedList sLeaks;
     private static Collection<String> sEvents;
+    private static Application.ActivityLifecycleCallbacks sActivityLifecycleCallbacks;
+    private static final Map<Activity, Boolean> sActivities =
+            Collections.synchronizedMap(new WeakHashMap<>());
+    private static int sActivitiesCreatedCount = 0;
 
     public DebugTestInformationHandler(Context context) {
         init(context);
+        if (sActivityLifecycleCallbacks == null) {
+            sActivityLifecycleCallbacks = new Application.ActivityLifecycleCallbacks() {
+                @Override
+                public void onActivityCreated(Activity activity, Bundle bundle) {
+                    sActivities.put(activity, true);
+                    ++sActivitiesCreatedCount;
+                }
+
+                @Override
+                public void onActivityStarted(Activity activity) {
+                }
+
+                @Override
+                public void onActivityResumed(Activity activity) {
+                }
+
+                @Override
+                public void onActivityPaused(Activity activity) {
+                }
+
+                @Override
+                public void onActivityStopped(Activity activity) {
+                }
+
+                @Override
+                public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
+                }
+
+                @Override
+                public void onActivityDestroyed(Activity activity) {
+                }
+            };
+            ((Application) context.getApplicationContext())
+                    .registerActivityLifecycleCallbacks(sActivityLifecycleCallbacks);
+        }
     }
 
     private static void runGcAndFinalizersSync() {
@@ -80,7 +124,7 @@
     }
 
     @Override
-    public Bundle call(String method) {
+    public Bundle call(String method, String arg) {
         final Bundle response = new Bundle();
         switch (method) {
             case TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS: {
@@ -160,8 +204,22 @@
                 }
             }
 
+            case TestProtocol.REQUEST_GET_ACTIVITIES_CREATED_COUNT: {
+                response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, sActivitiesCreatedCount);
+                return response;
+            }
+
+            case TestProtocol.REQUEST_GET_ACTIVITIES: {
+                response.putStringArray(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+                        sActivities.keySet().stream().map(
+                                a -> a.getClass().getSimpleName() + " ("
+                                        + (a.isDestroyed() ? "destroyed" : "current") + ")")
+                                .toArray(String[]::new));
+                return response;
+            }
+
             default:
-                return super.call(method);
+                return super.call(method, arg);
         }
     }
 }
diff --git a/go/quickstep/res/layout/overview_actions_container.xml b/go/quickstep/res/layout/overview_actions_container.xml
index 0e718ca..196541f 100644
--- a/go/quickstep/res/layout/overview_actions_container.xml
+++ b/go/quickstep/res/layout/overview_actions_container.xml
@@ -14,12 +14,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<!-- NOTE! don't add dimensions for margins / gravity to root view in this file, they need to be
-     loaded at runtime. -->
 <com.android.quickstep.views.GoOverviewActionsView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content">
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_horizontal|bottom">
 
     <LinearLayout
         android:id="@+id/action_buttons"
@@ -106,14 +105,21 @@
 
         <!-- Unused. Included only for compatibility with parent class. -->
         <Button
-            android:id="@+id/action_share"
+            android:id="@+id/action_split"
             style="@style/GoOverviewActionButton"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:drawableStart="@drawable/ic_share"
-            android:text="@string/action_share"
+            android:drawableStart="@drawable/ic_split_screen"
+            android:text="@string/action_split"
             android:theme="@style/ThemeControlHighlightWorkspaceColor"
             android:visibility="gone" />
+
+        <Space
+            android:id="@+id/action_split_space"
+            android:layout_width="0dp"
+            android:layout_height="1dp"
+            android:layout_weight="1"
+            android:visibility="gone" />
     </LinearLayout>
 
 </com.android.quickstep.views.GoOverviewActionsView>
\ No newline at end of file
diff --git a/go/quickstep/src/com/android/quickstep/views/GoOverviewActionsView.java b/go/quickstep/src/com/android/quickstep/views/GoOverviewActionsView.java
index 97ba590..492611f 100644
--- a/go/quickstep/src/com/android/quickstep/views/GoOverviewActionsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/GoOverviewActionsView.java
@@ -117,7 +117,7 @@
      */
     public void updateOrientationState(RecentsOrientedState orientedState) {
         // dismiss tooltip
-        boolean canLauncherRotate = orientedState.canRecentsActivityRotate();
+        boolean canLauncherRotate = orientedState.isRecentsActivityRotationAllowed();
         if (mArrowTipView != null && !canLauncherRotate) {
             mArrowTipView.close(/* animate= */ false);
         }
diff --git a/go/src/com/android/launcher3/model/WidgetsModel.java b/go/src/com/android/launcher3/model/WidgetsModel.java
index f8448da..1aa5d03 100644
--- a/go/src/com/android/launcher3/model/WidgetsModel.java
+++ b/go/src/com/android/launcher3/model/WidgetsModel.java
@@ -79,12 +79,13 @@
     }
 
     public WidgetItem getWidgetProviderInfoByProviderName(
-            ComponentName providerName) {
+            ComponentName providerName, UserHandle user) {
         return null;
     }
 
     /** Returns {@link PackageItemInfo} of a pending widget. */
-    public static PackageItemInfo newPendingItemInfo(ComponentName provider) {
-        return new PackageItemInfo(provider.getPackageName());
+    public static PackageItemInfo newPendingItemInfo(
+            Context context, ComponentName provider, UserHandle userHandle) {
+        return new PackageItemInfo(provider.getPackageName(), userHandle);
     }
 }
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 7f4c609..d5c1d77 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,6 +1,7 @@
 # Until all the dependencies move to android X
 android.useAndroidX = true
 android.enableJetifier = true
+org.gradle.parallel=true
 
 ANDROID_X_VERSION=1+
 
diff --git a/lint-baseline-launcher3.xml b/lint-baseline-launcher3.xml
index 9a68405..94345a6 100644
--- a/lint-baseline-launcher3.xml
+++ b/lint-baseline-launcher3.xml
@@ -576,12 +576,12 @@
     <issue
         id="NewApi"
         message="Call requires API level 31 (current min is 26): `android.appwidget.AppWidgetHostView#setColorResources`"
-        errorLine1="            view.setColorResources(mWallpaperColorResources);"
-        errorLine2="                 ~~~~~~~~~~~~~~~~~">
+        errorLine1="                setColorResources(mWallpaperColorResources);"
+        errorLine2="                ~~~~~~~~~~~~~~~~~">
         <location
             file="packages/apps/Launcher3/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java"
-            line="381"
-            column="18"/>
+            line="528"
+            column="17"/>
     </issue>
 
     <issue
@@ -591,7 +591,7 @@
         errorLine2="                                                            ~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/apps/Launcher3/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java"
-            line="270"
+            line="288"
             column="61"/>
     </issue>
 
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index 6d49d75..d1bf152 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -59,6 +59,7 @@
     SettingsContainer settings_container = 9;
     PredictedHotseatContainer predicted_hotseat_container = 10;
     TaskSwitcherContainer task_switcher_container = 11;
+    TaskBarContainer task_bar_container = 12;
     ExtendedContainers extended_containers = 20;
   }
 }
@@ -100,6 +101,16 @@
 message TaskSwitcherContainer {
 }
 
+// Container for taskbar.
+// Configured to show up on large screens(tablet-sized) such as foldables in expanded state, within
+// an app view(not in launcher screen).
+message TaskBarContainer {
+  optional int32 index = 1;
+
+  // Bit encoded value to capture pinned and predicted taskbar positions.
+  optional int32 cardinality = 2;
+}
+
 enum Attribute {
   UNKNOWN = 0;
   DEFAULT_LAYOUT = 1;       // icon automatically placed in workspace, folder, hotseat
@@ -141,6 +152,8 @@
   ALL_APPS_SEARCH_RESULT_NAVVYSITE = 25;
   ALL_APPS_SEARCH_RESULT_TIPS = 26;
   ALL_APPS_SEARCH_RESULT_PEOPLE_TILE = 27;
+  ALL_APPS_SEARCH_RESULT_LEGACY_SHORTCUT = 30;
+  ALL_APPS_SEARCH_RESULT_ASSISTANT_MEMORY = 31;
 
   WIDGETS_BOTTOM_TRAY = 28;
   WIDGETS_TRAY_PREDICTION = 29;
@@ -230,6 +243,7 @@
   oneof ParentContainer {
     WorkspaceContainer workspace = 4;
     HotseatContainer hotseat = 5;
+    TaskBarContainer taskbar = 6;
   }
 }
 
diff --git a/quickstep/Android.bp b/quickstep/Android.bp
index 38c9919..7b3e6c4 100644
--- a/quickstep/Android.bp
+++ b/quickstep/Android.bp
@@ -26,3 +26,19 @@
     path: "robolectric_tests",
     srcs: ["robolectric_tests/src/**/*.java"],
 }
+
+filegroup {
+    name: "launcher3-quickstep-tests-src",
+    path: "tests",
+    srcs: ["tests/src/**/*.java"],
+}
+
+filegroup {
+    name: "launcher3-quickstep-oop-tests-src",
+    path: "tests",
+    srcs: [
+        "tests/src/com/android/quickstep/NavigationModeSwitchRule.java",
+        "tests/src/com/android/quickstep/AbstractQuickStepTest.java",
+        "tests/src/com/android/quickstep/TaplTestsQuickstep.java",
+    ]
+}
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index dc92731..124cd57 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -100,7 +100,6 @@
         <activity android:name="com.android.quickstep.interaction.GestureSandboxActivity"
             android:autoRemoveFromRecents="true"
             android:excludeFromRecents="true"
-            android:screenOrientation="portrait"
             android:exported="true">
             <intent-filter>
                 <action android:name="com.android.quickstep.action.GESTURE_SANDBOX"/>
@@ -116,7 +115,6 @@
         <activity android:name="com.android.quickstep.interaction.AllSetActivity"
             android:autoRemoveFromRecents="true"
             android:excludeFromRecents="true"
-            android:screenOrientation="portrait"
             android:permission="android.permission.REBOOT"
             android:theme="@style/AllSetTheme"
             android:label="@string/allset_title"
diff --git a/quickstep/res/drawable/bg_overview_clear_all_button.xml b/quickstep/res/drawable/bg_overview_clear_all_button.xml
new file mode 100644
index 0000000..47cbd9f
--- /dev/null
+++ b/quickstep/res/drawable/bg_overview_clear_all_button.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<ripple android:color="?android:attr/colorControlHighlight"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item>
+        <shape android:shape="rectangle"
+            android:tint="?colorButtonNormal">
+            <corners android:radius="18dp" />
+            <solid android:color="?androidprv:attr/colorSurface"/>
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/quickstep/res/drawable/button_taskbar_edu_bordered.xml b/quickstep/res/drawable/button_taskbar_edu_bordered.xml
new file mode 100644
index 0000000..47f8e8f
--- /dev/null
+++ b/quickstep/res/drawable/button_taskbar_edu_bordered.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<inset
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <ripple
+        android:color="?android:attr/colorControlHighlight">
+        <item>
+            <shape android:shape="rectangle">
+                <corners android:radius="16dp"  />
+                <solid android:color="?androidprv:attr/colorSurface"/>
+                <stroke
+                    android:width="1dp"
+                    android:color="?androidprv:attr/colorAccentPrimaryVariant"/>
+            </shape>
+        </item>
+    </ripple>
+</inset>
\ No newline at end of file
diff --git a/quickstep/res/drawable/button_taskbar_edu_colored.xml b/quickstep/res/drawable/button_taskbar_edu_colored.xml
new file mode 100644
index 0000000..70bfc9f
--- /dev/null
+++ b/quickstep/res/drawable/button_taskbar_edu_colored.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<inset
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <ripple
+        android:color="?android:attr/colorControlHighlight">
+        <item>
+            <shape android:shape="rectangle">
+                <corners android:radius="16dp"/>
+                <solid android:color="?androidprv:attr/colorAccentPrimary"/>
+            </shape>
+        </item>
+    </ripple>
+</inset>
\ No newline at end of file
diff --git a/quickstep/res/drawable/default_sandbox_mock_launcher.xml b/quickstep/res/drawable/default_sandbox_mock_launcher.xml
deleted file mode 100644
index 38fbcf0..0000000
--- a/quickstep/res/drawable/default_sandbox_mock_launcher.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="360dp"
-    android:height="146dp"
-    android:viewportWidth="360"
-    android:viewportHeight="146">
-  <path
-      android:pathData="M25,96L335,96A25,25 0,0 1,360 121L360,121A25,25 0,0 1,335 146L25,146A25,25 0,0 1,0 121L0,121A25,25 0,0 1,25 96z"
-      android:fillColor="#3C4043"/>
-  <path
-      android:pathData="M30,30m-30,0a30,30 0,1 1,60 0a30,30 0,1 1,-60 0"
-      android:fillColor="#8AB4F8"/>
-  <path
-      android:pathData="M130,30m-30,0a30,30 0,1 1,60 0a30,30 0,1 1,-60 0"
-      android:fillColor="#F28B82"/>
-  <path
-      android:pathData="M230,30m-30,0a30,30 0,1 1,60 0a30,30 0,1 1,-60 0"
-      android:fillColor="#FDD663"/>
-  <path
-      android:pathData="M330,30m-30,0a30,30 0,1 1,60 0a30,30 0,1 1,-60 0"
-      android:fillColor="#81C995"/>
-</vector>
diff --git a/quickstep/res/drawable/gesture_tutorial_finger_dot.xml b/quickstep/res/drawable/gesture_tutorial_finger_dot.xml
new file mode 100644
index 0000000..5f8aafd
--- /dev/null
+++ b/quickstep/res/drawable/gesture_tutorial_finger_dot.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <solid android:color="@color/gesture_tutorial_primary_color" />
+    <size android:width="92dp" android:height="92dp"/>
+</shape>
\ No newline at end of file
diff --git a/quickstep/res/drawable/gesture_tutorial_motion_back.xml b/quickstep/res/drawable/gesture_tutorial_motion_back.xml
deleted file mode 100644
index a6860fa..0000000
--- a/quickstep/res/drawable/gesture_tutorial_motion_back.xml
+++ /dev/null
@@ -1,1233 +0,0 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:aapt="http://schemas.android.com/aapt">
-    <target android:name="_R_G_L_3_G_L_0_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1217"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_0_G_D_1_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1217"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_0_G_D_2_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1217"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_0_G_D_3_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1217"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_0_G_D_4_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1217"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_0_G_D_5_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1217"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_0_G_D_6_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1217"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_0_G_D_7_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1217"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_0_G_D_8_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1217"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_0_G_D_9_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1217"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_0_G_D_10_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1217"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_0_G_D_11_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1217"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_0_G_D_12_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1217"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_0_G_D_13_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1217"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="1367"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="283"
-                    android:propertyName="scaleX"
-                    android:startOffset="1217"
-                    android:valueFrom="1"
-                    android:valueTo="0.88012"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,0.536 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="283"
-                    android:propertyName="scaleY"
-                    android:startOffset="1217"
-                    android:valueFrom="1"
-                    android:valueTo="0.88012"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,0.536 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2417"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_1_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_2_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_3_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_4_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_5_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_6_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_7_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_8_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_9_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_10_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_11_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_12_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_13_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_14_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_15_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_16_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_17_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_18_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_19_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_20_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_21_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_22_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_23_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G_D_24_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="333"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_L_0_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="1417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="667"
-                    android:pathData="M 123.282,129.757C 134.28199999999998,129.757 178.282,129.757 189.282,129.757"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="217">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="333"
-                    android:pathData="M 189.282,129.757C 189.282,129.757 189.282,129.757 189.282,129.757"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="883">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0.333 0.667,0.667 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="250"
-                    android:pathData="M 189.282,129.757C 178.282,129.757 134.28199999999998,129.757 123.282,129.757"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="1217">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="217"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2383"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0.75"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="967"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="217"
-                    android:valueFrom="0.75"
-                    android:valueTo="0.75"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1183"
-                    android:valueFrom="0.75"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="pathData"
-                    android:startOffset="0"
-                    android:valueFrom="M-206.5 13.5 C-186.34,13.5 -170,29.84 -170,50 C-170,70.16 -186.34,86.5 -206.5,86.5 C-226.66,86.5 -243,70.16 -243,50 C-243,29.84 -226.66,13.5 -206.5,13.5c "
-                    android:valueTo="M-206 0 C-178.39,0 -156,22.39 -156,50 C-156,77.61 -178.39,100 -206,100 C-233.61,100 -256,77.61 -256,50 C-256,22.39 -233.61,0 -206,0c "
-                    android:valueType="pathType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="583"
-                    android:propertyName="pathData"
-                    android:startOffset="217"
-                    android:valueFrom="M-206 0 C-178.39,0 -156,22.39 -156,50 C-156,77.61 -178.39,100 -206,100 C-233.61,100 -256,77.61 -256,50 C-256,22.39 -233.61,0 -206,0c "
-                    android:valueTo="M0 0 C27.61,0 50,22.39 50,50 C50,77.61 27.61,100 0,100 C-27.61,100 -50,77.61 -50,50 C-50,22.39 -27.61,0 0,0c "
-                    android:valueType="pathType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="383"
-                    android:propertyName="pathData"
-                    android:startOffset="800"
-                    android:valueFrom="M0 0 C27.61,0 50,22.39 50,50 C50,77.61 27.61,100 0,100 C-27.61,100 -50,77.61 -50,50 C-50,22.39 -27.61,0 0,0c "
-                    android:valueTo="M0 0 C27.61,0 50,22.39 50,50 C50,77.61 27.61,100 0,100 C-27.61,100 -50,77.61 -50,50 C-50,22.39 -27.61,0 0,0c "
-                    android:valueType="pathType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="pathData"
-                    android:startOffset="1183"
-                    android:valueFrom="M0 0 C27.61,0 50,22.39 50,50 C50,77.61 27.61,100 0,100 C-27.61,100 -50,77.61 -50,50 C-50,22.39 -27.61,0 0,0c "
-                    android:valueTo="M0 13.5 C20.16,13.5 36.5,29.84 36.5,50 C36.5,70.16 20.16,86.5 0,86.5 C-20.16,86.5 -36.5,70.16 -36.5,50 C-36.5,29.84 -20.16,13.5 0,13.5c "
-                    android:valueType="pathType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="time_group">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="1767"
-                    android:propertyName="translateX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <aapt:attr name="android:drawable">
-        <vector
-            android:width="412dp"
-            android:height="892dp"
-            android:viewportHeight="892"
-            android:viewportWidth="412">
-            <group android:name="_R_G">
-                <group
-                    android:name="_R_G_L_4_G"
-                    android:translateX="206"
-                    android:translateY="446">
-                    <path
-                        android:name="_R_G_L_4_G_D_0_P_0"
-                        android:fillAlpha="1"
-                        android:fillColor="#dadce0"
-                        android:fillType="nonZero"
-                        android:pathData=" M206 -446 C206,-446 206,446 206,446 C206,446 -206,446 -206,446 C-206,446 -206,-446 -206,-446 C-206,-446 206,-446 206,-446c " />
-                </group>
-                <group
-                    android:name="_R_G_L_3_G"
-                    android:pivotX="206"
-                    android:pivotY="446"
-                    android:scaleX="1"
-                    android:scaleY="1">
-                    <group android:name="_R_G_L_3_G_L_0_G">
-                        <group android:name="_R_G_L_3_G_L_0_G_L_0_G">
-                            <path
-                                android:name="_R_G_L_3_G_L_0_G_L_0_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#f1f3f4"
-                                android:fillType="nonZero"
-                                android:pathData=" M412 101 C412,101 412,892 412,892 C412,892 0,892 0,892 C0,892 0,101 0,101 C0,101 412,101 412,101c " />
-                            <path
-                                android:name="_R_G_L_3_G_L_0_G_L_0_G_D_1_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#e8eaed"
-                                android:fillType="nonZero"
-                                android:pathData=" M412 0 C412,0 412,101 412,101 C412,101 0,101 0,101 C0,101 0,0 0,0 C0,0 412,0 412,0c " />
-                            <path
-                                android:name="_R_G_L_3_G_L_0_G_L_0_G_D_2_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#dadce0"
-                                android:fillType="nonZero"
-                                android:pathData=" M383 804 C383,816.15 373.15,826 361,826 C361,826 51,826 51,826 C38.85,826 29,816.15 29,804 C29,791.85 38.85,782 51,782 C51,782 361,782 361,782 C373.15,782 383,791.85 383,804c " />
-                            <path
-                                android:name="_R_G_L_3_G_L_0_G_L_0_G_D_3_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#dadce0"
-                                android:fillType="nonZero"
-                                android:pathData=" M201 47 C201,47 201,75 201,75 C201,77.21 199.21,79 197,79 C197,79 38,79 38,79 C35.79,79 34,77.21 34,75 C34,75 34,47 34,47 C34,44.79 35.79,43 38,43 C38,43 197,43 197,43 C199.21,43 201,44.79 201,47c " />
-                            <path
-                                android:name="_R_G_L_3_G_L_0_G_L_0_G_D_4_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#dadce0"
-                                android:fillType="nonZero"
-                                android:pathData=" M388 47 C388,47 388,75 388,75 C388,77.21 386.21,79 384,79 C384,79 356,79 356,79 C353.79,79 352,77.21 352,75 C352,75 352,47 352,47 C352,44.79 353.79,43 356,43 C356,43 384,43 384,43 C386.21,43 388,44.79 388,47c " />
-                            <path
-                                android:name="_R_G_L_3_G_L_0_G_L_0_G_D_5_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#dadce0"
-                                android:fillType="nonZero"
-                                android:pathData=" M336 47 C336,47 336,75 336,75 C336,77.21 334.21,79 332,79 C332,79 304,79 304,79 C301.79,79 300,77.21 300,75 C300,75 300,47 300,47 C300,44.79 301.79,43 304,43 C304,43 332,43 332,43 C334.21,43 336,44.79 336,47c " />
-                            <path
-                                android:name="_R_G_L_3_G_L_0_G_L_0_G_D_6_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#dadce0"
-                                android:fillType="nonZero"
-                                android:pathData=" M70 618 C70,630.15 60.15,640 48,640 C35.85,640 26,630.15 26,618 C26,605.85 35.85,596 48,596 C60.15,596 70,605.85 70,618c " />
-                            <path
-                                android:name="_R_G_L_3_G_L_0_G_L_0_G_D_7_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#dadce0"
-                                android:fillType="nonZero"
-                                android:pathData=" M70 396 C70,408.15 60.15,418 48,418 C35.85,418 26,408.15 26,396 C26,383.85 35.85,374 48,374 C60.15,374 70,383.85 70,396c " />
-                            <path
-                                android:name="_R_G_L_3_G_L_0_G_L_0_G_D_8_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M394 248 C394,248 394,324 394,324 C394,333.94 385.94,342 376,342 C376,342 142,342 142,342 C132.06,342 124,333.94 124,324 C124,324 124,248 124,248 C124,238.06 132.06,230 142,230 C142,230 376,230 376,230 C385.94,230 394,238.06 394,248c " />
-                            <path
-                                android:name="_R_G_L_3_G_L_0_G_L_0_G_D_9_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M393.94 468.38 C393.94,468.38 393.94,481.5 393.94,481.5 C393.94,483.71 392.15,485.5 389.94,485.5 C389.94,485.5 303.5,485.5 303.5,485.5 C301.29,485.5 299.5,483.71 299.5,481.5 C299.5,481.5 299.5,468.38 299.5,468.38 C299.5,466.17 301.29,464.38 303.5,464.38 C303.5,464.38 389.94,464.38 389.94,464.38 C392.15,464.38 393.94,466.17 393.94,468.38c  M394 468 C394,477.67 386.17,485.5 376.5,485.5 C376.5,485.5 290,485.5 290,485.5 C280.33,485.5 272.5,477.67 272.5,468 C272.5,458.34 280.33,450.5 290,450.5 C290,450.5 376.5,450.5 376.5,450.5 C386.17,450.5 394,458.34 394,468c " />
-                            <path
-                                android:name="_R_G_L_3_G_L_0_G_L_0_G_D_10_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M394 494 C394,494 394,547 394,547 C394,549.21 392.21,551 390,551 C390,551 164,551 164,551 C161.79,551 160,549.21 160,547 C160,547 160,494 160,494 C160,491.79 161.79,490 164,490 C164,490 390,490 390,490 C392.21,490 394,491.79 394,494c  M394 508 C394,508 394,545 394,545 C394,554.94 385.94,563 376,563 C376,563 142,563 142,563 C132.06,563 124,554.94 124,545 C124,545 124,508 124,508 C124,498.06 132.06,490 142,490 C142,490 376,490 376,490 C385.94,490 394,498.06 394,508c " />
-                            <path
-                                android:name="_R_G_L_3_G_L_0_G_L_0_G_D_11_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M394 690 C394,690 394,727 394,727 C394,736.94 385.94,745 376,745 C376,745 142,745 142,745 C132.06,745 124,736.94 124,727 C124,727 124,690 124,690 C124,680.06 132.06,672 142,672 C142,672 376,672 376,672 C385.94,672 394,680.06 394,690c " />
-                            <path
-                                android:name="_R_G_L_3_G_L_0_G_L_0_G_D_12_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#e8eaed"
-                                android:fillType="nonZero"
-                                android:pathData=" M267.5 617 C267.5,626.67 259.67,634.5 250,634.5 C250,634.5 104.5,634.5 104.5,634.5 C94.84,634.5 87,626.67 87,617 C87,607.34 94.84,599.5 104.5,599.5 C104.5,599.5 250,599.5 250,599.5 C259.67,599.5 267.5,607.34 267.5,617c " />
-                            <path
-                                android:name="_R_G_L_3_G_L_0_G_L_0_G_D_13_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#e8eaed"
-                                android:fillType="nonZero"
-                                android:pathData=" M299 395.5 C299,405.17 291.16,413 281.5,413 C281.5,413 104.5,413 104.5,413 C94.84,413 87,405.17 87,395.5 C87,385.84 94.84,378 104.5,378 C104.5,378 281.5,378 281.5,378 C291.16,378 299,385.84 299,395.5c " />
-                        </group>
-                    </group>
-                </group>
-                <group
-                    android:name="_R_G_L_2_G"
-                    android:scaleY="0">
-                    <group
-                        android:name="_R_G_L_2_G_L_0_G"
-                        android:scaleY="0">
-                        <group
-                            android:name="_R_G_L_2_G_L_0_G_L_0_G"
-                            android:scaleY="0">
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_0_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#dadce0"
-                                android:fillType="nonZero"
-                                android:pathData=" M412 0 C412,0 412,892 412,892 C412,892 0,892 0,892 C0,892 0,0 0,0 C0,0 412,0 412,0c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_1_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#e8eaed"
-                                android:fillType="nonZero"
-                                android:pathData=" M412 0 C412,0 412,101 412,101 C412,101 0,101 0,101 C0,101 0,0 0,0 C0,0 412,0 412,0c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_2_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M195 143 C195,143 195,153 195,153 C195,155.21 193.21,157 191,157 C191,157 106,157 106,157 C103.79,157 102,155.21 102,153 C102,153 102,143 102,143 C102,140.79 103.79,139 106,139 C106,139 191,139 191,139 C193.21,139 195,140.79 195,143c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_3_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M270 165 C270,165 270,173 270,173 C270,175.21 268.21,177 266,177 C266,177 106,177 106,177 C103.79,177 102,175.21 102,173 C102,173 102,165 102,165 C102,162.79 103.79,161 106,161 C106,161 266,161 266,161 C268.21,161 270,162.79 270,165c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_4_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M222 231 C222,231 222,241 222,241 C222,243.21 220.21,245 218,245 C218,245 106,245 106,245 C103.79,245 102,243.21 102,241 C102,241 102,231 102,231 C102,228.79 103.79,227 106,227 C106,227 218,227 218,227 C220.21,227 222,228.79 222,231c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_5_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M241 253 C241,253 241,261 241,261 C241,263.21 239.21,265 237,265 C237,265 106,265 106,265 C103.79,265 102,263.21 102,261 C102,261 102,253 102,253 C102,250.79 103.79,249 106,249 C106,249 237,249 237,249 C239.21,249 241,250.79 241,253c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_6_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M214 319 C214,319 214,329 214,329 C214,331.21 212.21,333 210,333 C210,333 106,333 106,333 C103.79,333 102,331.21 102,329 C102,329 102,319 102,319 C102,316.79 103.79,315 106,315 C106,315 210,315 210,315 C212.21,315 214,316.79 214,319c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_7_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M333 341 C333,341 333,349 333,349 C333,351.21 331.21,353 329,353 C329,353 106,353 106,353 C103.79,353 102,351.21 102,349 C102,349 102,341 102,341 C102,338.79 103.79,337 106,337 C106,337 329,337 329,337 C331.21,337 333,338.79 333,341c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_8_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M238 407 C238,407 238,417 238,417 C238,419.21 236.21,421 234,421 C234,421 106,421 106,421 C103.79,421 102,419.21 102,417 C102,417 102,407 102,407 C102,404.79 103.79,403 106,403 C106,403 234,403 234,403 C236.21,403 238,404.79 238,407c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_9_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M295 429 C295,429 295,437 295,437 C295,439.21 293.21,441 291,441 C291,441 106,441 106,441 C103.79,441 102,439.21 102,437 C102,437 102,429 102,429 C102,426.79 103.79,425 106,425 C106,425 291,425 291,425 C293.21,425 295,426.79 295,429c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_10_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M168 495 C168,495 168,505 168,505 C168,507.21 166.21,509 164,509 C164,509 106,509 106,509 C103.79,509 102,507.21 102,505 C102,505 102,495 102,495 C102,492.79 103.79,491 106,491 C106,491 164,491 164,491 C166.21,491 168,492.79 168,495c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_11_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M269 517 C269,517 269,525 269,525 C269,527.21 267.21,529 265,529 C265,529 106,529 106,529 C103.79,529 102,527.21 102,525 C102,525 102,517 102,517 C102,514.79 103.79,513 106,513 C106,513 265,513 265,513 C267.21,513 269,514.79 269,517c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_12_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M235 583 C235,583 235,593 235,593 C235,595.21 233.21,597 231,597 C231,597 106,597 106,597 C103.79,597 102,595.21 102,593 C102,593 102,583 102,583 C102,580.79 103.79,579 106,579 C106,579 231,579 231,579 C233.21,579 235,580.79 235,583c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_13_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M295 605 C295,605 295,613 295,613 C295,615.21 293.21,617 291,617 C291,617 106,617 106,617 C103.79,617 102,615.21 102,613 C102,613 102,605 102,605 C102,602.79 103.79,601 106,601 C106,601 291,601 291,601 C293.21,601 295,602.79 295,605c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_14_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M223 671 C223,671 223,681 223,681 C223,683.21 221.21,685 219,685 C219,685 106,685 106,685 C103.79,685 102,683.21 102,681 C102,681 102,671 102,671 C102,668.79 103.79,667 106,667 C106,667 219,667 219,667 C221.21,667 223,668.79 223,671c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_15_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M246 693 C246,693 246,701 246,701 C246,703.21 244.21,705 242,705 C242,705 106,705 106,705 C103.79,705 102,703.21 102,701 C102,701 102,693 102,693 C102,690.79 103.79,689 106,689 C106,689 242,689 242,689 C244.21,689 246,690.79 246,693c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_16_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M388 798 C388,798 388,798 388,798 C388,813.45 375.45,826 360,826 C360,826 267,826 267,826 C251.55,826 239,813.45 239,798 C239,798 239,798 239,798 C239,782.55 251.55,770 267,770 C267,770 360,770 360,770 C375.45,770 388,782.55 388,798c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_17_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#f8f9fa"
-                                android:fillType="nonZero"
-                                android:pathData=" M377 47 C377,47 377,75 377,75 C377,77.21 375.21,79 373,79 C373,79 38,79 38,79 C35.79,79 34,77.21 34,75 C34,75 34,47 34,47 C34,44.79 35.79,43 38,43 C38,43 373,43 373,43 C375.21,43 377,44.79 377,47c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_18_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#9aa0a6"
-                                android:fillType="nonZero"
-                                android:pathData=" M82 157 C82,172.46 69.46,185 54,185 C38.54,185 26,172.46 26,157 C26,141.54 38.54,129 54,129 C69.46,129 82,141.54 82,157c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_19_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#9aa0a6"
-                                android:fillType="nonZero"
-                                android:pathData=" M82 245 C82,260.46 69.46,273 54,273 C38.54,273 26,260.46 26,245 C26,229.54 38.54,217 54,217 C69.46,217 82,229.54 82,245c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_20_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#9aa0a6"
-                                android:fillType="nonZero"
-                                android:pathData=" M82 333 C82,348.46 69.46,361 54,361 C38.54,361 26,348.46 26,333 C26,317.54 38.54,305 54,305 C69.46,305 82,317.54 82,333c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_21_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#9aa0a6"
-                                android:fillType="nonZero"
-                                android:pathData=" M82 421 C82,436.46 69.46,449 54,449 C38.54,449 26,436.46 26,421 C26,405.54 38.54,393 54,393 C69.46,393 82,405.54 82,421c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_22_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#9aa0a6"
-                                android:fillType="nonZero"
-                                android:pathData=" M82 509 C82,524.46 69.46,537 54,537 C38.54,537 26,524.46 26,509 C26,493.54 38.54,481 54,481 C69.46,481 82,493.54 82,509c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_23_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#9aa0a6"
-                                android:fillType="nonZero"
-                                android:pathData=" M82 597 C82,612.46 69.46,625 54,625 C38.54,625 26,612.46 26,597 C26,581.54 38.54,569 54,569 C69.46,569 82,581.54 82,597c " />
-                            <path
-                                android:name="_R_G_L_2_G_L_0_G_L_0_G_D_24_P_0"
-                                android:fillAlpha="0"
-                                android:fillColor="#9aa0a6"
-                                android:fillType="nonZero"
-                                android:pathData=" M82 685 C82,700.46 69.46,713 54,713 C38.54,713 26,700.46 26,685 C26,669.54 38.54,657 54,657 C69.46,657 82,669.54 82,685c " />
-                        </group>
-                    </group>
-                </group>
-                <group
-                    android:name="_R_G_L_1_G"
-                    android:scaleY="0"
-                    android:translateX="-17.875"
-                    android:translateY="322.017">
-                    <group
-                        android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0"
-                        android:translateX="123.282"
-                        android:translateY="129.757">
-                        <path
-                            android:name="_R_G_L_1_G_D_0_P_0"
-                            android:fillAlpha="1"
-                            android:fillColor="#3b4043"
-                            android:fillType="nonZero"
-                            android:pathData=" M-109 27.43 C-109,27.43 -112.61,23.81 -112.61,23.81 C-112.61,23.81 -133.03,44.23 -133.03,44.23 C-133.03,44.23 -112.61,64.64 -112.61,64.64 C-112.61,64.64 -109,61.03 -109,61.03 C-109,61.03 -125.8,44.23 -125.8,44.23 C-125.8,44.23 -109,27.43 -109,27.43c " />
-                    </group>
-                </group>
-                <group
-                    android:name="_R_G_L_0_G"
-                    android:translateX="206"
-                    android:translateY="446">
-                    <path
-                        android:name="_R_G_L_0_G_D_0_P_0"
-                        android:fillAlpha="0"
-                        android:fillColor="@color/gesture_tutorial_primary_color"
-                        android:fillType="nonZero"
-                        android:pathData=" M-206.5 13.5 C-186.34,13.5 -170,29.84 -170,50 C-170,70.16 -186.34,86.5 -206.5,86.5 C-226.66,86.5 -243,70.16 -243,50 C-243,29.84 -226.66,13.5 -206.5,13.5c " />
-                </group>
-            </group>
-            <group android:name="time_group" />
-        </vector>
-    </aapt:attr>
-</animated-vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/gesture_tutorial_motion_home_dark_mode.xml b/quickstep/res/drawable/gesture_tutorial_motion_home_dark_mode.xml
deleted file mode 100644
index aff35c1..0000000
--- a/quickstep/res/drawable/gesture_tutorial_motion_home_dark_mode.xml
+++ /dev/null
@@ -1,1254 +0,0 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:aapt="http://schemas.android.com/aapt">
-    <target android:name="_R_G_L_2_G_L_4_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="50"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="600"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="650"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_4_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="50"
-                    android:pathData="M 206,776C 206,776 206,776 206,776"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="600">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.27,0 0,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="700"
-                    android:pathData="M 206,776C 206,776 206,797 206,797"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="650">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.27,0 0,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_4_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="650"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_3_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="600"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_3_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="700"
-                    android:pathData="M 56,673C 56,673 56,706 56,706"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="600">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.27,0 0,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_3_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="600"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_2_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="600"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_2_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="700"
-                    android:pathData="M 156,673C 156,673 156,706 156,706"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="600">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.27,0 0,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_2_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="600"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_1_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="600"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_1_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="700"
-                    android:pathData="M 256,673C 256,673 256,706 256,706"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="600">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.27,0 0,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_1_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="600"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="600"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="700"
-                    android:pathData="M 356,673C 356,673 356,706 356,706"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="600">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.27,0 0,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="600"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="600"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_11_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#dadce0"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.215 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_10_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_9_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_8_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_7_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_6_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_5_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_4_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_3_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_2_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_1_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="517"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_3_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_3_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="517"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_2_G_L_2_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#e8eaed"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.232 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_2_G_L_1_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#80868b"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.674 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_2_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#80868b"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.674 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_2_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="517"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_1_G_L_2_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="150"
-                    android:propertyName="fillColor"
-                    android:startOffset="350"
-                    android:valueFrom="#202124"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.69 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_1_G_L_1_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="150"
-                    android:propertyName="fillColor"
-                    android:startOffset="350"
-                    android:valueFrom="#202124"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.69 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_1_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="150"
-                    android:propertyName="fillColor"
-                    android:startOffset="350"
-                    android:valueFrom="#3c4043"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.69 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_1_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="517"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="fillColor"
-                    android:startOffset="500"
-                    android:valueFrom="#bac4d6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="50"
-                    android:propertyName="fillColor"
-                    android:startOffset="633"
-                    android:valueFrom="#bac4d6"
-                    android:valueTo="#8ab4f8"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,-0.214 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="283"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="500"
-                    android:valueFrom="1"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.999,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="283"
-                    android:propertyName="pathData"
-                    android:startOffset="500"
-                    android:valueFrom="M206.06 -430.06 C206.06,-430.06 206,431 206,431 C206,446 189.75,446 189.79,446 C189.79,446 -189.98,446 -189.98,446 C-189.94,446 -206,446 -206,431 C-206,431 -206,-430 -206,-430 C-206,-446 -189.97,-446 -190.01,-446 C-190.01,-446 188.98,-446.06 188.98,-446.06 C188.94,-446.06 206,-446 206.06,-430.06c "
-                    android:valueTo="M60 -0.06 C60,-0.06 60,0.06 60,0.06 C60,28 36,60.25 -0.02,60.25 C-0.02,60.25 0.02,60.25 0.02,60.25 C-32.5,60.25 -60,31.5 -60,0.06 C-60,0.06 -60,-0.06 -60,-0.06 C-60,-31.25 -34,-59.25 0.02,-59.25 C0.02,-59.25 -0.02,-59.25 -0.02,-59.25 C33.5,-59.25 60,-38 60,-0.06c "
-                    android:valueType="pathType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.999,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_0_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="500"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_0_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="850"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_T_1">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="283"
-                    android:pathData="M 206,446C 201.417,411.133 195,385.297 195,385.297"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="217">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="333"
-                    android:pathData="M 195,385.297C 195,385.297 105.5,148.09000000000003 56,691.5"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="500">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.443,0.093 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_T_1">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="467"
-                    android:propertyName="scaleX"
-                    android:startOffset="217"
-                    android:valueFrom="1"
-                    android:valueTo="0.5"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="467"
-                    android:propertyName="scaleY"
-                    android:startOffset="217"
-                    android:valueFrom="1"
-                    android:valueTo="0.5"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_T_1">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2167"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0.75"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="233"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="217"
-                    android:valueFrom="0.75"
-                    android:valueTo="0.75"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="450"
-                    android:valueFrom="0.75"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="pathData"
-                    android:startOffset="0"
-                    android:valueFrom="M0 411 C19.33,411 35,426.67 35,446 C35,465.33 19.33,481 0,481 C-19.33,481 -35,465.33 -35,446 C-35,426.67 -19.33,411 0,411c "
-                    android:valueTo="M0 396 C27.61,396 50,418.39 50,446 C50,473.61 27.61,496 0,496 C-27.61,496 -50,473.61 -50,446 C-50,418.39 -27.61,396 0,396c "
-                    android:valueType="pathType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="433"
-                    android:propertyName="pathData"
-                    android:startOffset="217"
-                    android:valueFrom="M0 396 C27.61,396 50,418.39 50,446 C50,473.61 27.61,496 0,496 C-27.61,496 -50,473.61 -50,446 C-50,418.39 -27.61,396 0,396c "
-                    android:valueTo="M0 68 C27.61,68 50,90.39 50,118 C50,145.61 27.61,168 0,168 C-27.61,168 -50,145.61 -50,118 C-50,90.39 -27.61,68 0,68c "
-                    android:valueType="pathType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2167"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="time_group">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="1367"
-                    android:propertyName="translateX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <aapt:attr name="android:drawable">
-        <vector
-            android:width="412dp"
-            android:height="892dp"
-            android:viewportHeight="892"
-            android:viewportWidth="412">
-            <group android:name="_R_G">
-                <group
-                    android:name="_R_G_L_3_G"
-                    android:translateX="206"
-                    android:translateY="446">
-                    <path
-                        android:name="_R_G_L_3_G_D_0_P_0"
-                        android:fillAlpha="1"
-                        android:fillColor="@color/fake_wallpaper_color_dark_mode"
-                        android:fillType="nonZero"
-                        android:pathData=" M206 -446 C206,-446 206,446 206,446 C206,446 -206,446 -206,446 C-206,446 -206,-446 -206,-446 C-206,-446 206,-446 206,-446c " />
-                </group>
-                <group
-                    android:name="_R_G_L_2_G"
-                    android:scaleY="0">
-                    <group
-                        android:name="_R_G_L_2_G_L_4_G"
-                        android:scaleY="0"
-                        android:translateX="206"
-                        android:translateY="776">
-                        <path
-                            android:name="_R_G_L_2_G_L_4_G_D_0_P_0"
-                            android:fillAlpha="0"
-                            android:fillColor="#3c4043"
-                            android:fillType="nonZero"
-                            android:pathData=" M180 0 C180,0 180,0 180,0 C180,13.8 168.8,25 155,25 C155,25 -155,25 -155,25 C-168.8,25 -180,13.8 -180,0 C-180,0 -180,0 -180,0 C-180,-13.8 -168.8,-25 -155,-25 C-155,-25 155,-25 155,-25 C168.8,-25 180,-13.8 180,0c " />
-                    </group>
-                    <group
-                        android:name="_R_G_L_2_G_L_3_G"
-                        android:scaleY="0"
-                        android:translateX="56"
-                        android:translateY="673">
-                        <path
-                            android:name="_R_G_L_2_G_L_3_G_D_0_P_0"
-                            android:fillAlpha="0"
-                            android:fillColor="#8ab4f8"
-                            android:fillType="nonZero"
-                            android:pathData=" M0 -30 C16.56,-30 30,-16.56 30,0 C30,16.56 16.56,30 0,30 C-16.56,30 -30,16.56 -30,0 C-30,-16.56 -16.56,-30 0,-30c " />
-                    </group>
-                    <group
-                        android:name="_R_G_L_2_G_L_2_G"
-                        android:scaleY="0"
-                        android:translateX="156"
-                        android:translateY="673">
-                        <path
-                            android:name="_R_G_L_2_G_L_2_G_D_0_P_0"
-                            android:fillAlpha="0"
-                            android:fillColor="#f28b82"
-                            android:fillType="nonZero"
-                            android:pathData=" M0 -30 C16.56,-30 30,-16.56 30,0 C30,16.56 16.56,30 0,30 C-16.56,30 -30,16.56 -30,0 C-30,-16.56 -16.56,-30 0,-30c " />
-                    </group>
-                    <group
-                        android:name="_R_G_L_2_G_L_1_G"
-                        android:scaleY="0"
-                        android:translateX="256"
-                        android:translateY="673">
-                        <path
-                            android:name="_R_G_L_2_G_L_1_G_D_0_P_0"
-                            android:fillAlpha="0"
-                            android:fillColor="#fdd663"
-                            android:fillType="nonZero"
-                            android:pathData=" M0 -30 C16.56,-30 30,-16.56 30,0 C30,16.56 16.56,30 0,30 C-16.56,30 -30,16.56 -30,0 C-30,-16.56 -16.56,-30 0,-30c " />
-                    </group>
-                    <group
-                        android:name="_R_G_L_2_G_L_0_G"
-                        android:scaleY="0"
-                        android:translateX="356"
-                        android:translateY="673">
-                        <path
-                            android:name="_R_G_L_2_G_L_0_G_D_0_P_0"
-                            android:fillAlpha="0"
-                            android:fillColor="#81c995"
-                            android:fillType="nonZero"
-                            android:pathData=" M0 -30 C16.56,-30 30,-16.56 30,0 C30,16.56 16.56,30 0,30 C-16.56,30 -30,16.56 -30,0 C-30,-16.56 -16.56,-30 0,-30c " />
-                    </group>
-                </group>
-                <group
-                    android:name="_R_G_L_1_G_T_1"
-                    android:scaleX="1"
-                    android:scaleY="1"
-                    android:translateX="206"
-                    android:translateY="446">
-                    <group
-                        android:name="_R_G_L_1_G"
-                        android:translateX="-206"
-                        android:translateY="-446">
-                        <group android:name="_R_G_L_1_G_L_4_G">
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_11_G"
-                                android:scaleX="0.87473"
-                                android:scaleY="0.98643"
-                                android:translateX="206"
-                                android:translateY="472.769">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_11_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#dadce0"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M235.5 -407 C235.5,-407 235.5,407 235.5,407 C235.5,416.93 227.43,425 217.5,425 C217.5,425 -217.5,425 -217.5,425 C-227.43,425 -235.5,416.93 -235.5,407 C-235.5,407 -235.5,-407 -235.5,-407 C-235.5,-416.93 -227.43,-425 -217.5,-425 C-217.5,-425 217.5,-425 217.5,-425 C227.43,-425 235.5,-416.93 235.5,-407c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_10_G"
-                                android:translateX="182.5"
-                                android:translateY="831">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_10_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M158.5 -3 C158.5,-3 158.5,3 158.5,3 C158.5,7.42 154.92,11 150.5,11 C150.5,11 -150.5,11 -150.5,11 C-154.92,11 -158.5,7.42 -158.5,3 C-158.5,3 -158.5,-3 -158.5,-3 C-158.5,-7.42 -154.92,-11 -150.5,-11 C-150.5,-11 150.5,-11 150.5,-11 C154.92,-11 158.5,-7.42 158.5,-3c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_9_G"
-                                android:translateX="186"
-                                android:translateY="801">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_9_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M162 -3 C162,-3 162,3 162,3 C162,7.42 158.42,11 154,11 C154,11 -154,11 -154,11 C-158.42,11 -162,7.42 -162,3 C-162,3 -162,-3 -162,-3 C-162,-7.42 -158.42,-11 -154,-11 C-154,-11 154,-11 154,-11 C158.42,-11 162,-7.42 162,-3c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_8_G"
-                                android:translateX="119"
-                                android:translateY="755">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_8_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M95 -3 C95,-3 95,3 95,3 C95,7.42 91.42,11 87,11 C87,11 -87,11 -87,11 C-91.42,11 -95,7.42 -95,3 C-95,3 -95,-3 -95,-3 C-95,-7.42 -91.42,-11 -87,-11 C-87,-11 87,-11 87,-11 C91.42,-11 95,-7.42 95,-3c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_7_G"
-                                android:translateX="182.5"
-                                android:translateY="725">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_7_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M158.5 -3 C158.5,-3 158.5,3 158.5,3 C158.5,7.42 154.92,11 150.5,11 C150.5,11 -150.5,11 -150.5,11 C-154.92,11 -158.5,7.42 -158.5,3 C-158.5,3 -158.5,-3 -158.5,-3 C-158.5,-7.42 -154.92,-11 -150.5,-11 C-150.5,-11 150.5,-11 150.5,-11 C154.92,-11 158.5,-7.42 158.5,-3c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_6_G"
-                                android:translateX="197.5"
-                                android:translateY="695">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_6_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M173.5 -3 C173.5,-3 173.5,3 173.5,3 C173.5,7.42 169.92,11 165.5,11 C165.5,11 -165.5,11 -165.5,11 C-169.92,11 -173.5,7.42 -173.5,3 C-173.5,3 -173.5,-3 -173.5,-3 C-173.5,-7.42 -169.92,-11 -165.5,-11 C-165.5,-11 165.5,-11 165.5,-11 C169.92,-11 173.5,-7.42 173.5,-3c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_5_G"
-                                android:translateX="192"
-                                android:translateY="665">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_5_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M168 -3 C168,-3 168,3 168,3 C168,7.42 164.42,11 160,11 C160,11 -160,11 -160,11 C-164.42,11 -168,7.42 -168,3 C-168,3 -168,-3 -168,-3 C-168,-7.42 -164.42,-11 -160,-11 C-160,-11 160,-11 160,-11 C164.42,-11 168,-7.42 168,-3c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_4_G"
-                                android:translateX="105.5"
-                                android:translateY="360">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_4_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M23.5 -2 C23.5,-2 23.5,2 23.5,2 C23.5,4.21 21.71,6 19.5,6 C19.5,6 -19.5,6 -19.5,6 C-21.71,6 -23.5,4.21 -23.5,2 C-23.5,2 -23.5,-2 -23.5,-2 C-23.5,-4.21 -21.71,-6 -19.5,-6 C-19.5,-6 19.5,-6 19.5,-6 C21.71,-6 23.5,-4.21 23.5,-2c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_3_G"
-                                android:translateX="47.5"
-                                android:translateY="360">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_3_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M23.5 -2 C23.5,-2 23.5,2 23.5,2 C23.5,4.21 21.71,6 19.5,6 C19.5,6 -19.5,6 -19.5,6 C-21.71,6 -23.5,4.21 -23.5,2 C-23.5,2 -23.5,-2 -23.5,-2 C-23.5,-4.21 -21.71,-6 -19.5,-6 C-19.5,-6 19.5,-6 19.5,-6 C21.71,-6 23.5,-4.21 23.5,-2c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_2_G"
-                                android:translateX="142.5"
-                                android:translateY="328">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_2_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M118.5 -10 C118.5,-10 118.5,10 118.5,10 C118.5,14.42 114.92,18 110.5,18 C110.5,18 -110.5,18 -110.5,18 C-114.92,18 -118.5,14.42 -118.5,10 C-118.5,10 -118.5,-10 -118.5,-10 C-118.5,-14.42 -114.92,-18 -110.5,-18 C-110.5,-18 110.5,-18 110.5,-18 C114.92,-18 118.5,-14.42 118.5,-10c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_1_G"
-                                android:translateX="186"
-                                android:translateY="284">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_1_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M162 -10 C162,-10 162,10 162,10 C162,14.42 158.42,18 154,18 C154,18 -154,18 -154,18 C-158.42,18 -162,14.42 -162,10 C-162,10 -162,-10 -162,-10 C-162,-14.42 -158.42,-18 -154,-18 C-154,-18 154,-18 154,-18 C158.42,-18 162,-14.42 162,-10c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_0_G"
-                                android:translateX="155"
-                                android:translateY="240">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_0_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M131 -10 C131,-10 131,10 131,10 C131,14.42 127.42,18 123,18 C123,18 -123,18 -123,18 C-127.42,18 -131,14.42 -131,10 C-131,10 -131,-10 -131,-10 C-131,-14.42 -127.42,-18 -123,-18 C-123,-18 123,-18 123,-18 C127.42,-18 131,-14.42 131,-10c " />
-                            </group>
-                        </group>
-                        <group
-                            android:name="_R_G_L_1_G_L_3_G"
-                            android:translateX="24"
-                            android:translateY="390">
-                            <group
-                                android:name="_R_G_L_1_G_L_3_G_L_0_G"
-                                android:translateX="182"
-                                android:translateY="120">
-                                <path
-                                    android:name="_R_G_L_1_G_L_3_G_L_0_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M182 -98 C182,-98 182,98 182,98 C182,110.14 172.14,120 160,120 C160,120 -160,120 -160,120 C-172.14,120 -182,110.14 -182,98 C-182,98 -182,-98 -182,-98 C-182,-110.14 -172.14,-120 -160,-120 C-160,-120 160,-120 160,-120 C172.14,-120 182,-110.14 182,-98c " />
-                            </group>
-                        </group>
-                        <group android:name="_R_G_L_1_G_L_2_G">
-                            <group
-                                android:name="_R_G_L_1_G_L_2_G_L_2_G"
-                                android:translateX="206"
-                                android:translateY="145">
-                                <path
-                                    android:name="_R_G_L_1_G_L_2_G_L_2_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#e8eaed"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -95.63 C206,-95.63 206,42.37 206,42.37 C206,43.47 205.1,44.37 204,44.37 C204,44.37 -204,44.37 -204,44.37 C-205.1,44.37 -206,43.47 -206,42.37 C-206,42.37 -206,-95.63 -206,-95.63 C-206,-96.73 -205.1,-97.63 -204,-97.63 C-204,-97.63 204,-97.63 204,-97.63 C205.1,-97.63 206,-96.73 206,-95.63c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_2_G_L_1_G"
-                                android:translateX="206"
-                                android:translateY="145">
-                                <path
-                                    android:name="_R_G_L_1_G_L_2_G_L_1_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#80868b"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M109 -14 C109,-14 109,14 109,14 C109,15.1 108.1,16 107,16 C107,16 -107,16 -107,16 C-108.1,16 -109,15.1 -109,14 C-109,14 -109,-14 -109,-14 C-109,-15.1 -108.1,-16 -107,-16 C-107,-16 107,-16 107,-16 C108.1,-16 109,-15.1 109,-14c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_2_G_L_0_G"
-                                android:translateX="46"
-                                android:translateY="145">
-                                <path
-                                    android:name="_R_G_L_1_G_L_2_G_L_0_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#80868b"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M22 -14 C22,-14 22,14 22,14 C22,18.42 18.42,22 14,22 C14,22 -14,22 -14,22 C-18.42,22 -22,18.42 -22,14 C-22,14 -22,-14 -22,-14 C-22,-18.42 -18.42,-22 -14,-22 C-14,-22 14,-22 14,-22 C18.42,-22 22,-18.42 22,-14c " />
-                            </group>
-                        </group>
-                        <group android:name="_R_G_L_1_G_L_1_G">
-                            <group
-                                android:name="_R_G_L_1_G_L_1_G_L_2_G"
-                                android:translateX="206"
-                                android:translateY="51">
-                                <path
-                                    android:name="_R_G_L_1_G_L_1_G_L_2_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#202124"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -0.27 C206,-0.27 206,49.73 206,49.73 C206,49.73 -206,49.73 -206,49.73 C-206,49.73 -206,-0.27 -206,-0.27 C-206,-0.27 206,-0.27 206,-0.27c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_1_G_L_1_G"
-                                android:translateX="206"
-                                android:translateY="50.5">
-                                <path
-                                    android:name="_R_G_L_1_G_L_1_G_L_1_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#202124"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -32.5 C206,-32.5 206,32.5 206,32.5 C206,42.43 197.93,50.5 188,50.5 C188,50.5 -188,50.5 -188,50.5 C-197.93,50.5 -206,42.43 -206,32.5 C-206,32.5 -206,-32.5 -206,-32.5 C-206,-42.43 -197.93,-50.5 -188,-50.5 C-188,-50.5 188,-50.5 188,-50.5 C197.93,-50.5 206,-42.43 206,-32.5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_1_G_L_0_G"
-                                android:translateX="206"
-                                android:translateY="66.5">
-                                <path
-                                    android:name="_R_G_L_1_G_L_1_G_L_0_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#3c4043"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M190 0 C190,0 190,0 190,0 C190,10.21 181.71,18.5 171.5,18.5 C171.5,18.5 -171.5,18.5 -171.5,18.5 C-181.71,18.5 -190,10.21 -190,0 C-190,0 -190,0 -190,0 C-190,-10.21 -181.71,-18.5 -171.5,-18.5 C-171.5,-18.5 171.5,-18.5 171.5,-18.5 C181.71,-18.5 190,-10.21 190,0c " />
-                            </group>
-                        </group>
-                        <group
-                            android:name="_R_G_L_1_G_L_0_G"
-                            android:scaleY="0"
-                            android:translateX="206"
-                            android:translateY="446">
-                            <path
-                                android:name="_R_G_L_1_G_L_0_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bac4d6"
-                                android:fillType="nonZero"
-                                android:pathData=" M206.06 -430.06 C206.06,-430.06 206,431 206,431 C206,446 189.75,446 189.79,446 C189.79,446 -189.98,446 -189.98,446 C-189.94,446 -206,446 -206,431 C-206,431 -206,-430 -206,-430 C-206,-446 -189.97,-446 -190.01,-446 C-190.01,-446 188.98,-446.06 188.98,-446.06 C188.94,-446.06 206,-446 206.06,-430.06c " />
-                        </group>
-                    </group>
-                </group>
-                <group
-                    android:name="_R_G_L_0_G"
-                    android:translateX="206"
-                    android:translateY="446">
-                    <path
-                        android:name="_R_G_L_0_G_D_0_P_0"
-                        android:fillAlpha="0"
-                        android:fillColor="@color/gesture_tutorial_primary_color"
-                        android:fillType="nonZero"
-                        android:pathData=" M0 411 C19.33,411 35,426.67 35,446 C35,465.33 19.33,481 0,481 C-19.33,481 -35,465.33 -35,446 C-35,426.67 -19.33,411 0,411c " />
-                </group>
-            </group>
-            <group android:name="time_group" />
-        </vector>
-    </aapt:attr>
-</animated-vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/gesture_tutorial_motion_home_light_mode.xml b/quickstep/res/drawable/gesture_tutorial_motion_home_light_mode.xml
deleted file mode 100644
index 98d97ad..0000000
--- a/quickstep/res/drawable/gesture_tutorial_motion_home_light_mode.xml
+++ /dev/null
@@ -1,1254 +0,0 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:aapt="http://schemas.android.com/aapt">
-    <target android:name="_R_G_L_2_G_L_4_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="50"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="600"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="650"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_4_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="50"
-                    android:pathData="M 206,776C 206,776 206,776 206,776"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="600">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.27,0 0,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="700"
-                    android:pathData="M 206,776C 206,776 206,797 206,797"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="650">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.27,0 0,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_4_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="650"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_3_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="600"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_3_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="700"
-                    android:pathData="M 56,673C 56,673 56,706 56,706"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="600">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.27,0 0,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_3_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="600"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_2_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="600"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_2_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="700"
-                    android:pathData="M 156,673C 156,673 156,706 156,706"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="600">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.27,0 0,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_2_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="600"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_1_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="600"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_1_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="700"
-                    android:pathData="M 256,673C 256,673 256,706 256,706"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="600">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.27,0 0,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_1_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="600"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="600"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="700"
-                    android:pathData="M 356,673C 356,673 356,706 356,706"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="600">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.27,0 0,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_L_0_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="600"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="600"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_11_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#dadce0"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.215 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_10_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_9_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_8_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_7_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_6_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_5_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_4_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_3_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_2_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_1_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_4_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="517"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_3_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#bdc1c6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.92 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_3_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="517"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_2_G_L_2_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#e8eaed"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1.232 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_2_G_L_1_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#80868b"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.674 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_2_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="fillColor"
-                    android:startOffset="400"
-                    android:valueFrom="#80868b"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.674 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_2_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="517"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_1_G_L_2_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="150"
-                    android:propertyName="fillColor"
-                    android:startOffset="350"
-                    android:valueFrom="#6e7175"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.674 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_1_G_L_1_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="150"
-                    android:propertyName="fillColor"
-                    android:startOffset="350"
-                    android:valueFrom="#6e7175"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.676 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_1_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="150"
-                    android:propertyName="fillColor"
-                    android:startOffset="350"
-                    android:valueFrom="#9a9a9a"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.584 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_1_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="517"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="fillColor"
-                    android:startOffset="500"
-                    android:valueFrom="#bac4d6"
-                    android:valueTo="#bac4d6"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="50"
-                    android:propertyName="fillColor"
-                    android:startOffset="633"
-                    android:valueFrom="#bac4d6"
-                    android:valueTo="#8ab4f8"
-                    android:valueType="colorType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,-0.214 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="283"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="500"
-                    android:valueFrom="1"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.999,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="283"
-                    android:propertyName="pathData"
-                    android:startOffset="500"
-                    android:valueFrom="M206.06 -430.06 C206.06,-430.06 206,431 206,431 C206,446 189.75,446 189.79,446 C189.79,446 -189.98,446 -189.98,446 C-189.94,446 -206,446 -206,431 C-206,431 -206,-430 -206,-430 C-206,-446 -189.97,-446 -190.01,-446 C-190.01,-446 188.98,-446.06 188.98,-446.06 C188.94,-446.06 206,-446 206.06,-430.06c "
-                    android:valueTo="M60 -0.06 C60,-0.06 60,0.06 60,0.06 C60,28 36,60.25 -0.02,60.25 C-0.02,60.25 0.02,60.25 0.02,60.25 C-32.5,60.25 -60,31.5 -60,0.06 C-60,0.06 -60,-0.06 -60,-0.06 C-60,-31.25 -34,-59.25 0.02,-59.25 C0.02,-59.25 -0.02,-59.25 -0.02,-59.25 C33.5,-59.25 60,-38 60,-0.06c "
-                    android:valueType="pathType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.999,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_0_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="500"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_L_0_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="850"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_T_1">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="283"
-                    android:pathData="M 206,446C 201.417,411.133 195,385.297 195,385.297"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="217">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="333"
-                    android:pathData="M 195,385.297C 195,385.297 105.5,148.09000000000003 56,691.5"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="500">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.443,0.093 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_T_1">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="467"
-                    android:propertyName="scaleX"
-                    android:startOffset="217"
-                    android:valueFrom="1"
-                    android:valueTo="0.5"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="467"
-                    android:propertyName="scaleY"
-                    android:startOffset="217"
-                    android:valueFrom="1"
-                    android:valueTo="0.5"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_T_1">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2167"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0.75"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="233"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="217"
-                    android:valueFrom="0.75"
-                    android:valueTo="0.75"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="450"
-                    android:valueFrom="0.75"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="pathData"
-                    android:startOffset="0"
-                    android:valueFrom="M0 411 C19.33,411 35,426.67 35,446 C35,465.33 19.33,481 0,481 C-19.33,481 -35,465.33 -35,446 C-35,426.67 -19.33,411 0,411c "
-                    android:valueTo="M0 396 C27.61,396 50,418.39 50,446 C50,473.61 27.61,496 0,496 C-27.61,496 -50,473.61 -50,446 C-50,418.39 -27.61,396 0,396c "
-                    android:valueType="pathType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="433"
-                    android:propertyName="pathData"
-                    android:startOffset="217"
-                    android:valueFrom="M0 396 C27.61,396 50,418.39 50,446 C50,473.61 27.61,496 0,496 C-27.61,496 -50,473.61 -50,446 C-50,418.39 -27.61,396 0,396c "
-                    android:valueTo="M0 68 C27.61,68 50,90.39 50,118 C50,145.61 27.61,168 0,168 C-27.61,168 -50,145.61 -50,118 C-50,90.39 -27.61,68 0,68c "
-                    android:valueType="pathType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2167"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="time_group">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="1367"
-                    android:propertyName="translateX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <aapt:attr name="android:drawable">
-        <vector
-            android:width="412dp"
-            android:height="892dp"
-            android:viewportHeight="892"
-            android:viewportWidth="412">
-            <group android:name="_R_G">
-                <group
-                    android:name="_R_G_L_3_G"
-                    android:translateX="206"
-                    android:translateY="446">
-                    <path
-                        android:name="_R_G_L_3_G_D_0_P_0"
-                        android:fillAlpha="1"
-                        android:fillColor="@color/fake_wallpaper_color_light_mode"
-                        android:fillType="nonZero"
-                        android:pathData=" M206 -446 C206,-446 206,446 206,446 C206,446 -206,446 -206,446 C-206,446 -206,-446 -206,-446 C-206,-446 206,-446 206,-446c " />
-                </group>
-                <group
-                    android:name="_R_G_L_2_G"
-                    android:scaleY="0">
-                    <group
-                        android:name="_R_G_L_2_G_L_4_G"
-                        android:scaleY="0"
-                        android:translateX="206"
-                        android:translateY="776">
-                        <path
-                            android:name="_R_G_L_2_G_L_4_G_D_0_P_0"
-                            android:fillAlpha="0"
-                            android:fillColor="#d9d9da"
-                            android:fillType="nonZero"
-                            android:pathData=" M180 0 C180,0 180,0 180,0 C180,13.8 168.8,25 155,25 C155,25 -155,25 -155,25 C-168.8,25 -180,13.8 -180,0 C-180,0 -180,0 -180,0 C-180,-13.8 -168.8,-25 -155,-25 C-155,-25 155,-25 155,-25 C168.8,-25 180,-13.8 180,0c " />
-                    </group>
-                    <group
-                        android:name="_R_G_L_2_G_L_3_G"
-                        android:scaleY="0"
-                        android:translateX="56"
-                        android:translateY="673">
-                        <path
-                            android:name="_R_G_L_2_G_L_3_G_D_0_P_0"
-                            android:fillAlpha="0"
-                            android:fillColor="#8ab4f8"
-                            android:fillType="nonZero"
-                            android:pathData=" M0 -30 C16.56,-30 30,-16.56 30,0 C30,16.56 16.56,30 0,30 C-16.56,30 -30,16.56 -30,0 C-30,-16.56 -16.56,-30 0,-30c " />
-                    </group>
-                    <group
-                        android:name="_R_G_L_2_G_L_2_G"
-                        android:scaleY="0"
-                        android:translateX="156"
-                        android:translateY="673">
-                        <path
-                            android:name="_R_G_L_2_G_L_2_G_D_0_P_0"
-                            android:fillAlpha="0"
-                            android:fillColor="#f28b82"
-                            android:fillType="nonZero"
-                            android:pathData=" M0 -30 C16.56,-30 30,-16.56 30,0 C30,16.56 16.56,30 0,30 C-16.56,30 -30,16.56 -30,0 C-30,-16.56 -16.56,-30 0,-30c " />
-                    </group>
-                    <group
-                        android:name="_R_G_L_2_G_L_1_G"
-                        android:scaleY="0"
-                        android:translateX="256"
-                        android:translateY="673">
-                        <path
-                            android:name="_R_G_L_2_G_L_1_G_D_0_P_0"
-                            android:fillAlpha="0"
-                            android:fillColor="#fdd663"
-                            android:fillType="nonZero"
-                            android:pathData=" M0 -30 C16.56,-30 30,-16.56 30,0 C30,16.56 16.56,30 0,30 C-16.56,30 -30,16.56 -30,0 C-30,-16.56 -16.56,-30 0,-30c " />
-                    </group>
-                    <group
-                        android:name="_R_G_L_2_G_L_0_G"
-                        android:scaleY="0"
-                        android:translateX="356"
-                        android:translateY="673">
-                        <path
-                            android:name="_R_G_L_2_G_L_0_G_D_0_P_0"
-                            android:fillAlpha="0"
-                            android:fillColor="#81c995"
-                            android:fillType="nonZero"
-                            android:pathData=" M0 -30 C16.56,-30 30,-16.56 30,0 C30,16.56 16.56,30 0,30 C-16.56,30 -30,16.56 -30,0 C-30,-16.56 -16.56,-30 0,-30c " />
-                    </group>
-                </group>
-                <group
-                    android:name="_R_G_L_1_G_T_1"
-                    android:scaleX="1"
-                    android:scaleY="1"
-                    android:translateX="206"
-                    android:translateY="446">
-                    <group
-                        android:name="_R_G_L_1_G"
-                        android:translateX="-206"
-                        android:translateY="-446">
-                        <group android:name="_R_G_L_1_G_L_4_G">
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_11_G"
-                                android:scaleX="0.87473"
-                                android:scaleY="0.98643"
-                                android:translateX="206"
-                                android:translateY="472.769">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_11_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#dadce0"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M235.5 -407 C235.5,-407 235.5,407 235.5,407 C235.5,416.93 227.43,425 217.5,425 C217.5,425 -217.5,425 -217.5,425 C-227.43,425 -235.5,416.93 -235.5,407 C-235.5,407 -235.5,-407 -235.5,-407 C-235.5,-416.93 -227.43,-425 -217.5,-425 C-217.5,-425 217.5,-425 217.5,-425 C227.43,-425 235.5,-416.93 235.5,-407c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_10_G"
-                                android:translateX="182.5"
-                                android:translateY="831">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_10_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M158.5 -3 C158.5,-3 158.5,3 158.5,3 C158.5,7.42 154.92,11 150.5,11 C150.5,11 -150.5,11 -150.5,11 C-154.92,11 -158.5,7.42 -158.5,3 C-158.5,3 -158.5,-3 -158.5,-3 C-158.5,-7.42 -154.92,-11 -150.5,-11 C-150.5,-11 150.5,-11 150.5,-11 C154.92,-11 158.5,-7.42 158.5,-3c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_9_G"
-                                android:translateX="186"
-                                android:translateY="801">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_9_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M162 -3 C162,-3 162,3 162,3 C162,7.42 158.42,11 154,11 C154,11 -154,11 -154,11 C-158.42,11 -162,7.42 -162,3 C-162,3 -162,-3 -162,-3 C-162,-7.42 -158.42,-11 -154,-11 C-154,-11 154,-11 154,-11 C158.42,-11 162,-7.42 162,-3c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_8_G"
-                                android:translateX="119"
-                                android:translateY="755">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_8_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M95 -3 C95,-3 95,3 95,3 C95,7.42 91.42,11 87,11 C87,11 -87,11 -87,11 C-91.42,11 -95,7.42 -95,3 C-95,3 -95,-3 -95,-3 C-95,-7.42 -91.42,-11 -87,-11 C-87,-11 87,-11 87,-11 C91.42,-11 95,-7.42 95,-3c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_7_G"
-                                android:translateX="182.5"
-                                android:translateY="725">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_7_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M158.5 -3 C158.5,-3 158.5,3 158.5,3 C158.5,7.42 154.92,11 150.5,11 C150.5,11 -150.5,11 -150.5,11 C-154.92,11 -158.5,7.42 -158.5,3 C-158.5,3 -158.5,-3 -158.5,-3 C-158.5,-7.42 -154.92,-11 -150.5,-11 C-150.5,-11 150.5,-11 150.5,-11 C154.92,-11 158.5,-7.42 158.5,-3c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_6_G"
-                                android:translateX="197.5"
-                                android:translateY="695">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_6_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M173.5 -3 C173.5,-3 173.5,3 173.5,3 C173.5,7.42 169.92,11 165.5,11 C165.5,11 -165.5,11 -165.5,11 C-169.92,11 -173.5,7.42 -173.5,3 C-173.5,3 -173.5,-3 -173.5,-3 C-173.5,-7.42 -169.92,-11 -165.5,-11 C-165.5,-11 165.5,-11 165.5,-11 C169.92,-11 173.5,-7.42 173.5,-3c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_5_G"
-                                android:translateX="192"
-                                android:translateY="665">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_5_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M168 -3 C168,-3 168,3 168,3 C168,7.42 164.42,11 160,11 C160,11 -160,11 -160,11 C-164.42,11 -168,7.42 -168,3 C-168,3 -168,-3 -168,-3 C-168,-7.42 -164.42,-11 -160,-11 C-160,-11 160,-11 160,-11 C164.42,-11 168,-7.42 168,-3c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_4_G"
-                                android:translateX="105.5"
-                                android:translateY="360">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_4_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M23.5 -2 C23.5,-2 23.5,2 23.5,2 C23.5,4.21 21.71,6 19.5,6 C19.5,6 -19.5,6 -19.5,6 C-21.71,6 -23.5,4.21 -23.5,2 C-23.5,2 -23.5,-2 -23.5,-2 C-23.5,-4.21 -21.71,-6 -19.5,-6 C-19.5,-6 19.5,-6 19.5,-6 C21.71,-6 23.5,-4.21 23.5,-2c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_3_G"
-                                android:translateX="47.5"
-                                android:translateY="360">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_3_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M23.5 -2 C23.5,-2 23.5,2 23.5,2 C23.5,4.21 21.71,6 19.5,6 C19.5,6 -19.5,6 -19.5,6 C-21.71,6 -23.5,4.21 -23.5,2 C-23.5,2 -23.5,-2 -23.5,-2 C-23.5,-4.21 -21.71,-6 -19.5,-6 C-19.5,-6 19.5,-6 19.5,-6 C21.71,-6 23.5,-4.21 23.5,-2c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_2_G"
-                                android:translateX="142.5"
-                                android:translateY="328">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_2_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M118.5 -10 C118.5,-10 118.5,10 118.5,10 C118.5,14.42 114.92,18 110.5,18 C110.5,18 -110.5,18 -110.5,18 C-114.92,18 -118.5,14.42 -118.5,10 C-118.5,10 -118.5,-10 -118.5,-10 C-118.5,-14.42 -114.92,-18 -110.5,-18 C-110.5,-18 110.5,-18 110.5,-18 C114.92,-18 118.5,-14.42 118.5,-10c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_1_G"
-                                android:translateX="186"
-                                android:translateY="284">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_1_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M162 -10 C162,-10 162,10 162,10 C162,14.42 158.42,18 154,18 C154,18 -154,18 -154,18 C-158.42,18 -162,14.42 -162,10 C-162,10 -162,-10 -162,-10 C-162,-14.42 -158.42,-18 -154,-18 C-154,-18 154,-18 154,-18 C158.42,-18 162,-14.42 162,-10c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_4_G_L_0_G"
-                                android:translateX="155"
-                                android:translateY="240">
-                                <path
-                                    android:name="_R_G_L_1_G_L_4_G_L_0_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M131 -10 C131,-10 131,10 131,10 C131,14.42 127.42,18 123,18 C123,18 -123,18 -123,18 C-127.42,18 -131,14.42 -131,10 C-131,10 -131,-10 -131,-10 C-131,-14.42 -127.42,-18 -123,-18 C-123,-18 123,-18 123,-18 C127.42,-18 131,-14.42 131,-10c " />
-                            </group>
-                        </group>
-                        <group
-                            android:name="_R_G_L_1_G_L_3_G"
-                            android:translateX="24"
-                            android:translateY="390">
-                            <group
-                                android:name="_R_G_L_1_G_L_3_G_L_0_G"
-                                android:translateX="182"
-                                android:translateY="120">
-                                <path
-                                    android:name="_R_G_L_1_G_L_3_G_L_0_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M182 -98 C182,-98 182,98 182,98 C182,110.14 172.14,120 160,120 C160,120 -160,120 -160,120 C-172.14,120 -182,110.14 -182,98 C-182,98 -182,-98 -182,-98 C-182,-110.14 -172.14,-120 -160,-120 C-160,-120 160,-120 160,-120 C172.14,-120 182,-110.14 182,-98c " />
-                            </group>
-                        </group>
-                        <group android:name="_R_G_L_1_G_L_2_G">
-                            <group
-                                android:name="_R_G_L_1_G_L_2_G_L_2_G"
-                                android:translateX="206"
-                                android:translateY="145">
-                                <path
-                                    android:name="_R_G_L_1_G_L_2_G_L_2_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#e8eaed"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -95.63 C206,-95.63 206,42.37 206,42.37 C206,43.47 205.1,44.37 204,44.37 C204,44.37 -204,44.37 -204,44.37 C-205.1,44.37 -206,43.47 -206,42.37 C-206,42.37 -206,-95.63 -206,-95.63 C-206,-96.73 -205.1,-97.63 -204,-97.63 C-204,-97.63 204,-97.63 204,-97.63 C205.1,-97.63 206,-96.73 206,-95.63c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_2_G_L_1_G"
-                                android:translateX="206"
-                                android:translateY="145">
-                                <path
-                                    android:name="_R_G_L_1_G_L_2_G_L_1_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#80868b"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M109 -14 C109,-14 109,14 109,14 C109,15.1 108.1,16 107,16 C107,16 -107,16 -107,16 C-108.1,16 -109,15.1 -109,14 C-109,14 -109,-14 -109,-14 C-109,-15.1 -108.1,-16 -107,-16 C-107,-16 107,-16 107,-16 C108.1,-16 109,-15.1 109,-14c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_2_G_L_0_G"
-                                android:translateX="46"
-                                android:translateY="145">
-                                <path
-                                    android:name="_R_G_L_1_G_L_2_G_L_0_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#80868b"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M22 -14 C22,-14 22,14 22,14 C22,18.42 18.42,22 14,22 C14,22 -14,22 -14,22 C-18.42,22 -22,18.42 -22,14 C-22,14 -22,-14 -22,-14 C-22,-18.42 -18.42,-22 -14,-22 C-14,-22 14,-22 14,-22 C18.42,-22 22,-18.42 22,-14c " />
-                            </group>
-                        </group>
-                        <group android:name="_R_G_L_1_G_L_1_G">
-                            <group
-                                android:name="_R_G_L_1_G_L_1_G_L_2_G"
-                                android:translateX="206"
-                                android:translateY="51">
-                                <path
-                                    android:name="_R_G_L_1_G_L_1_G_L_2_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#6e7175"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -0.27 C206,-0.27 206,49.73 206,49.73 C206,49.73 -206,49.73 -206,49.73 C-206,49.73 -206,-0.27 -206,-0.27 C-206,-0.27 206,-0.27 206,-0.27c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_1_G_L_1_G"
-                                android:translateX="206"
-                                android:translateY="50.5">
-                                <path
-                                    android:name="_R_G_L_1_G_L_1_G_L_1_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#6e7175"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -32.5 C206,-32.5 206,32.5 206,32.5 C206,42.43 197.93,50.5 188,50.5 C188,50.5 -188,50.5 -188,50.5 C-197.93,50.5 -206,42.43 -206,32.5 C-206,32.5 -206,-32.5 -206,-32.5 C-206,-42.43 -197.93,-50.5 -188,-50.5 C-188,-50.5 188,-50.5 188,-50.5 C197.93,-50.5 206,-42.43 206,-32.5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_1_G_L_1_G_L_0_G"
-                                android:translateX="206"
-                                android:translateY="66.5">
-                                <path
-                                    android:name="_R_G_L_1_G_L_1_G_L_0_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9a9a9a"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M190 0 C190,0 190,0 190,0 C190,10.21 181.71,18.5 171.5,18.5 C171.5,18.5 -171.5,18.5 -171.5,18.5 C-181.71,18.5 -190,10.21 -190,0 C-190,0 -190,0 -190,0 C-190,-10.21 -181.71,-18.5 -171.5,-18.5 C-171.5,-18.5 171.5,-18.5 171.5,-18.5 C181.71,-18.5 190,-10.21 190,0c " />
-                            </group>
-                        </group>
-                        <group
-                            android:name="_R_G_L_1_G_L_0_G"
-                            android:scaleY="0"
-                            android:translateX="206"
-                            android:translateY="446">
-                            <path
-                                android:name="_R_G_L_1_G_L_0_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bac4d6"
-                                android:fillType="nonZero"
-                                android:pathData=" M206.06 -430.06 C206.06,-430.06 206,431 206,431 C206,446 189.75,446 189.79,446 C189.79,446 -189.98,446 -189.98,446 C-189.94,446 -206,446 -206,431 C-206,431 -206,-430 -206,-430 C-206,-446 -189.97,-446 -190.01,-446 C-190.01,-446 188.98,-446.06 188.98,-446.06 C188.94,-446.06 206,-446 206.06,-430.06c " />
-                        </group>
-                    </group>
-                </group>
-                <group
-                    android:name="_R_G_L_0_G"
-                    android:translateX="206"
-                    android:translateY="446">
-                    <path
-                        android:name="_R_G_L_0_G_D_0_P_0"
-                        android:fillAlpha="0"
-                        android:fillColor="#84ba69"
-                        android:fillType="nonZero"
-                        android:pathData=" M0 411 C19.33,411 35,426.67 35,446 C35,465.33 19.33,481 0,481 C-19.33,481 -35,465.33 -35,446 C-35,426.67 -19.33,411 0,411c " />
-                </group>
-            </group>
-            <group android:name="time_group" />
-        </vector>
-    </aapt:attr>
-</animated-vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/gesture_tutorial_motion_overview_dark_mode.xml b/quickstep/res/drawable/gesture_tutorial_motion_overview_dark_mode.xml
deleted file mode 100644
index b007d20..0000000
--- a/quickstep/res/drawable/gesture_tutorial_motion_overview_dark_mode.xml
+++ /dev/null
@@ -1,1623 +0,0 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:aapt="http://schemas.android.com/aapt">
-    <target android:name="_R_G_L_4_G_N_3_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="1050"
-                    android:pathData="M 206,446C 206,446 206,395 206,395"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="217">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_4_G_N_3_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="1050"
-                    android:propertyName="scaleX"
-                    android:startOffset="217"
-                    android:valueFrom="1"
-                    android:valueTo="0.6"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="1050"
-                    android:propertyName="scaleY"
-                    android:startOffset="217"
-                    android:valueFrom="1"
-                    android:valueTo="0.6"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_4_G_N_3_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="3400"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_28_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_27_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_26_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_25_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_24_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_23_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_22_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_21_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_20_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_19_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_18_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_17_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_16_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_15_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_14_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_13_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_12_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_11_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_10_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_9_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_8_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_7_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_6_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_5_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_4_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_3_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_2_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_1_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_0_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_N_2_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="350"
-                    android:pathData="M 206,395C 206,403.5 206,437.5 206,446"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="2083">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.34,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_N_2_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="350"
-                    android:propertyName="scaleX"
-                    android:startOffset="2083"
-                    android:valueFrom="0.6"
-                    android:valueTo="0.72718"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.34,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="350"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0.6"
-                    android:valueTo="0.72718"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.34,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="scaleX"
-                    android:startOffset="2433"
-                    android:valueFrom="0.72718"
-                    android:valueTo="0.72"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.51,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="scaleY"
-                    android:startOffset="2433"
-                    android:valueFrom="0.72718"
-                    android:valueTo="0.72"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.51,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_N_2_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="0.6"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_D_0_P_0_G_0_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="scaleX"
-                    android:startOffset="2567"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="scaleY"
-                    android:startOffset="2567"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_N_2_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="350"
-                    android:pathData="M 206,395C 206,403.5 206,437.5 206,446"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="2083">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.34,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_N_2_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="350"
-                    android:propertyName="scaleX"
-                    android:startOffset="2083"
-                    android:valueFrom="0.6"
-                    android:valueTo="0.72718"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.34,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="350"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0.6"
-                    android:valueTo="0.72718"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.34,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="scaleX"
-                    android:startOffset="2433"
-                    android:valueFrom="0.72718"
-                    android:valueTo="0.72"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.51,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="scaleY"
-                    android:startOffset="2433"
-                    android:valueFrom="0.72718"
-                    android:valueTo="0.72"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.51,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_N_2_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2567"
-                    android:valueFrom="0"
-                    android:valueTo="0.6"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="250"
-                    android:pathData="M -556.176,-7.307C -556.176,-7.307 -421.176,-7.307 -421.176,-7.307"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="1350">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.272,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="417"
-                    android:pathData="M -421.176,-7.307C -421.176,-7.307 -429.51,-7.307 -429.51,-7.307"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="1600">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_N_2_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="350"
-                    android:pathData="M 206,395C 206,403.5 206,437.5 206,446"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="2083">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.34,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_N_2_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="350"
-                    android:propertyName="scaleX"
-                    android:startOffset="2083"
-                    android:valueFrom="0.6"
-                    android:valueTo="0.72718"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.34,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="350"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0.6"
-                    android:valueTo="0.72718"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.34,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="scaleX"
-                    android:startOffset="2433"
-                    android:valueFrom="0.72718"
-                    android:valueTo="0.72"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.51,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="scaleY"
-                    android:startOffset="2433"
-                    android:valueFrom="0.72718"
-                    android:valueTo="0.72"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.51,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_N_2_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="1350"
-                    android:valueFrom="0"
-                    android:valueTo="0.6"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0.75"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="1833"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="217"
-                    android:valueFrom="0.75"
-                    android:valueTo="0.75"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="2050"
-                    android:valueFrom="0.75"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="pathData"
-                    android:startOffset="0"
-                    android:valueFrom="M0 406 C21.54,406 39,423.46 39,445 C39,466.54 21.54,484 0,484 C-21.54,484 -39,466.54 -39,445 C-39,423.46 -21.54,406 0,406c "
-                    android:valueTo="M0 395 C27.61,395 50,417.39 50,445 C50,472.61 27.61,495 0,495 C-27.61,495 -50,472.61 -50,445 C-50,417.39 -27.61,395 0,395c "
-                    android:valueType="pathType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="1050"
-                    android:propertyName="pathData"
-                    android:startOffset="217"
-                    android:valueFrom="M0 395 C27.61,395 50,417.39 50,445 C50,472.61 27.61,495 0,495 C-27.61,495 -50,472.61 -50,445 C-50,417.39 -27.61,395 0,395c "
-                    android:valueTo="M0 166 C27.61,166 50,188.39 50,216 C50,243.61 27.61,266 0,266 C-27.61,266 -50,243.61 -50,216 C-50,188.39 -27.61,166 0,166c "
-                    android:valueType="pathType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="783"
-                    android:propertyName="pathData"
-                    android:startOffset="1267"
-                    android:valueFrom="M0 166 C27.61,166 50,188.39 50,216 C50,243.61 27.61,266 0,266 C-27.61,266 -50,243.61 -50,216 C-50,188.39 -27.61,166 0,166c "
-                    android:valueTo="M0 166 C27.61,166 50,188.39 50,216 C50,243.61 27.61,266 0,266 C-27.61,266 -50,243.61 -50,216 C-50,188.39 -27.61,166 0,166c "
-                    android:valueType="pathType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="pathData"
-                    android:startOffset="2050"
-                    android:valueFrom="M0 166 C27.61,166 50,188.39 50,216 C50,243.61 27.61,266 0,266 C-27.61,266 -50,243.61 -50,216 C-50,188.39 -27.61,166 0,166c "
-                    android:valueTo="M0 180 C19.88,180 36,196.12 36,216 C36,235.88 19.88,252 0,252 C-19.88,252 -36,235.88 -36,216 C-36,196.12 -19.88,180 0,180c "
-                    android:valueType="pathType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="time_group">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="2750"
-                    android:propertyName="translateX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <aapt:attr name="android:drawable">
-        <vector
-            android:width="412dp"
-            android:height="892dp"
-            android:viewportHeight="892"
-            android:viewportWidth="412">
-            <group android:name="_R_G">
-                <group
-                    android:name="_R_G_L_5_G"
-                    android:translateX="206"
-                    android:translateY="446">
-                    <path
-                        android:name="_R_G_L_5_G_D_0_P_0"
-                        android:fillAlpha="1"
-                        android:fillColor="@color/fake_wallpaper_color_dark_mode"
-                        android:fillType="nonZero"
-                        android:pathData=" M206 -446 C206,-446 206,446 206,446 C206,446 -206,446 -206,446 C-206,446 -206,-446 -206,-446 C-206,-446 206,-446 206,-446c " />
-                </group>
-                <group
-                    android:name="_R_G_L_4_G_N_3_T_0"
-                    android:scaleX="1"
-                    android:scaleY="1"
-                    android:translateX="206"
-                    android:translateY="446">
-                    <group
-                        android:name="_R_G_L_4_G"
-                        android:translateX="-206"
-                        android:translateY="-446">
-                        <group android:name="_R_G_L_4_G_L_0_G">
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_28_G"
-                                android:translateX="206"
-                                android:translateY="446">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_28_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#000000"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -422 C206,-422 206,422 206,422 C206,435.25 195.25,446 182,446 C182,446 -182,446 -182,446 C-195.25,446 -206,435.25 -206,422 C-206,422 -206,-422 -206,-422 C-206,-435.25 -195.25,-446 -182,-446 C-182,-446 182,-446 182,-446 C195.25,-446 206,-435.25 206,-422c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_27_G"
-                                android:translateX="206"
-                                android:translateY="422.5">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_27_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#dadce0"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -395.5 C206,-395.5 206,395.5 206,395.5 C206,395.5 -206,395.5 -206,395.5 C-206,395.5 -206,-395.5 -206,-395.5 C-206,-395.5 206,-395.5 206,-395.5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_26_G"
-                                android:translateX="206"
-                                android:translateY="496.5">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_26_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#dadce0"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -377.5 C206,-377.5 206,377.5 206,377.5 C206,387.43 197.93,395.5 188,395.5 C188,395.5 -188,395.5 -188,395.5 C-197.93,395.5 -206,387.43 -206,377.5 C-206,377.5 -206,-377.5 -206,-377.5 C-206,-387.43 -197.93,-395.5 -188,-395.5 C-188,-395.5 188,-395.5 188,-395.5 C197.93,-395.5 206,-387.43 206,-377.5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_25_G"
-                                android:translateX="206"
-                                android:translateY="50.5">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_25_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#e8eaed"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -23.5 C206,-23.5 206,50.5 206,50.5 C206,50.5 -206,50.5 -206,50.5 C-206,50.5 -206,-23.5 -206,-23.5 C-206,-23.5 206,-23.5 206,-23.5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_24_G"
-                                android:translateX="206"
-                                android:translateY="50.5">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_24_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#e8eaed"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -32.5 C206,-32.5 206,32.5 206,32.5 C206,42.43 197.93,50.5 188,50.5 C188,50.5 -188,50.5 -188,50.5 C-197.93,50.5 -206,42.43 -206,32.5 C-206,32.5 -206,-32.5 -206,-32.5 C-206,-42.43 -197.93,-50.5 -188,-50.5 C-188,-50.5 188,-50.5 188,-50.5 C197.93,-50.5 206,-42.43 206,-32.5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_23_G"
-                                android:translateX="54"
-                                android:translateY="157">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_23_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_22_G"
-                                android:translateX="54"
-                                android:translateY="157">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_22_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_21_G"
-                                android:translateX="148.5"
-                                android:translateY="148">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_21_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M46.5 -5 C46.5,-5 46.5,5 46.5,5 C46.5,7.21 44.71,9 42.5,9 C42.5,9 -42.5,9 -42.5,9 C-44.71,9 -46.5,7.21 -46.5,5 C-46.5,5 -46.5,-5 -46.5,-5 C-46.5,-7.21 -44.71,-9 -42.5,-9 C-42.5,-9 42.5,-9 42.5,-9 C44.71,-9 46.5,-7.21 46.5,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_20_G"
-                                android:translateX="186"
-                                android:translateY="169">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_20_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M84 -4 C84,-4 84,4 84,4 C84,6.21 82.21,8 80,8 C80,8 -80,8 -80,8 C-82.21,8 -84,6.21 -84,4 C-84,4 -84,-4 -84,-4 C-84,-6.21 -82.21,-8 -80,-8 C-80,-8 80,-8 80,-8 C82.21,-8 84,-6.21 84,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_19_G"
-                                android:translateX="54"
-                                android:translateY="245">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_19_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_18_G"
-                                android:translateX="162"
-                                android:translateY="236">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_18_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M60 -5 C60,-5 60,5 60,5 C60,7.21 58.21,9 56,9 C56,9 -56,9 -56,9 C-58.21,9 -60,7.21 -60,5 C-60,5 -60,-5 -60,-5 C-60,-7.21 -58.21,-9 -56,-9 C-56,-9 56,-9 56,-9 C58.21,-9 60,-7.21 60,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_17_G"
-                                android:translateX="171.5"
-                                android:translateY="257">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_17_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M69.5 -4 C69.5,-4 69.5,4 69.5,4 C69.5,6.21 67.71,8 65.5,8 C65.5,8 -65.5,8 -65.5,8 C-67.71,8 -69.5,6.21 -69.5,4 C-69.5,4 -69.5,-4 -69.5,-4 C-69.5,-6.21 -67.71,-8 -65.5,-8 C-65.5,-8 65.5,-8 65.5,-8 C67.71,-8 69.5,-6.21 69.5,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_16_G"
-                                android:translateX="54"
-                                android:translateY="333">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_16_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_15_G"
-                                android:translateX="158"
-                                android:translateY="324">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_15_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M56 -5 C56,-5 56,5 56,5 C56,7.21 54.21,9 52,9 C52,9 -52,9 -52,9 C-54.21,9 -56,7.21 -56,5 C-56,5 -56,-5 -56,-5 C-56,-7.21 -54.21,-9 -52,-9 C-52,-9 52,-9 52,-9 C54.21,-9 56,-7.21 56,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_14_G"
-                                android:translateX="217.5"
-                                android:translateY="345">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_14_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M115.5 -4 C115.5,-4 115.5,4 115.5,4 C115.5,6.21 113.71,8 111.5,8 C111.5,8 -111.5,8 -111.5,8 C-113.71,8 -115.5,6.21 -115.5,4 C-115.5,4 -115.5,-4 -115.5,-4 C-115.5,-6.21 -113.71,-8 -111.5,-8 C-111.5,-8 111.5,-8 111.5,-8 C113.71,-8 115.5,-6.21 115.5,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_13_G"
-                                android:translateX="54"
-                                android:translateY="421">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_13_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_12_G"
-                                android:translateX="170"
-                                android:translateY="412">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_12_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M68 -5 C68,-5 68,5 68,5 C68,7.21 66.21,9 64,9 C64,9 -64,9 -64,9 C-66.21,9 -68,7.21 -68,5 C-68,5 -68,-5 -68,-5 C-68,-7.21 -66.21,-9 -64,-9 C-64,-9 64,-9 64,-9 C66.21,-9 68,-7.21 68,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_11_G"
-                                android:translateX="198.5"
-                                android:translateY="433">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_11_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M96.5 -4 C96.5,-4 96.5,4 96.5,4 C96.5,6.21 94.71,8 92.5,8 C92.5,8 -92.5,8 -92.5,8 C-94.71,8 -96.5,6.21 -96.5,4 C-96.5,4 -96.5,-4 -96.5,-4 C-96.5,-6.21 -94.71,-8 -92.5,-8 C-92.5,-8 92.5,-8 92.5,-8 C94.71,-8 96.5,-6.21 96.5,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_10_G"
-                                android:translateX="54"
-                                android:translateY="509">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_10_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_9_G"
-                                android:translateX="135"
-                                android:translateY="500">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_9_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M33 -5 C33,-5 33,5 33,5 C33,7.21 31.21,9 29,9 C29,9 -29,9 -29,9 C-31.21,9 -33,7.21 -33,5 C-33,5 -33,-5 -33,-5 C-33,-7.21 -31.21,-9 -29,-9 C-29,-9 29,-9 29,-9 C31.21,-9 33,-7.21 33,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_8_G"
-                                android:translateX="185.5"
-                                android:translateY="521">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_8_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M83.5 -4 C83.5,-4 83.5,4 83.5,4 C83.5,6.21 81.71,8 79.5,8 C79.5,8 -79.5,8 -79.5,8 C-81.71,8 -83.5,6.21 -83.5,4 C-83.5,4 -83.5,-4 -83.5,-4 C-83.5,-6.21 -81.71,-8 -79.5,-8 C-79.5,-8 79.5,-8 79.5,-8 C81.71,-8 83.5,-6.21 83.5,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_7_G"
-                                android:translateX="54"
-                                android:translateY="597">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_7_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_6_G"
-                                android:translateX="168.5"
-                                android:translateY="588">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_6_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M66.5 -5 C66.5,-5 66.5,5 66.5,5 C66.5,7.21 64.71,9 62.5,9 C62.5,9 -62.5,9 -62.5,9 C-64.71,9 -66.5,7.21 -66.5,5 C-66.5,5 -66.5,-5 -66.5,-5 C-66.5,-7.21 -64.71,-9 -62.5,-9 C-62.5,-9 62.5,-9 62.5,-9 C64.71,-9 66.5,-7.21 66.5,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_5_G"
-                                android:translateX="198.5"
-                                android:translateY="609">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_5_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M96.5 -4 C96.5,-4 96.5,4 96.5,4 C96.5,6.21 94.71,8 92.5,8 C92.5,8 -92.5,8 -92.5,8 C-94.71,8 -96.5,6.21 -96.5,4 C-96.5,4 -96.5,-4 -96.5,-4 C-96.5,-6.21 -94.71,-8 -92.5,-8 C-92.5,-8 92.5,-8 92.5,-8 C94.71,-8 96.5,-6.21 96.5,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_4_G"
-                                android:translateX="54"
-                                android:translateY="685">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_4_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_3_G"
-                                android:translateX="162.5"
-                                android:translateY="676">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_3_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M60.5 -5 C60.5,-5 60.5,5 60.5,5 C60.5,7.21 58.71,9 56.5,9 C56.5,9 -56.5,9 -56.5,9 C-58.71,9 -60.5,7.21 -60.5,5 C-60.5,5 -60.5,-5 -60.5,-5 C-60.5,-7.21 -58.71,-9 -56.5,-9 C-56.5,-9 56.5,-9 56.5,-9 C58.71,-9 60.5,-7.21 60.5,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_2_G"
-                                android:translateX="174"
-                                android:translateY="697">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_2_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M72 -4 C72,-4 72,4 72,4 C72,6.21 70.21,8 68,8 C68,8 -68,8 -68,8 C-70.21,8 -72,6.21 -72,4 C-72,4 -72,-4 -72,-4 C-72,-6.21 -70.21,-8 -68,-8 C-68,-8 68,-8 68,-8 C70.21,-8 72,-6.21 72,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_1_G"
-                                android:translateX="313.5"
-                                android:translateY="798">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_1_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M74.5 0 C74.5,0 74.5,0 74.5,0 C74.5,15.45 61.95,28 46.5,28 C46.5,28 -46.5,28 -46.5,28 C-61.95,28 -74.5,15.45 -74.5,0 C-74.5,0 -74.5,0 -74.5,0 C-74.5,-15.45 -61.95,-28 -46.5,-28 C-46.5,-28 46.5,-28 46.5,-28 C61.95,-28 74.5,-15.45 74.5,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_0_G"
-                                android:translateX="205.5"
-                                android:translateY="61">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_0_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#f8f9fa"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M171.5 -14 C171.5,-14 171.5,14 171.5,14 C171.5,16.21 169.71,18 167.5,18 C167.5,18 -167.5,18 -167.5,18 C-169.71,18 -171.5,16.21 -171.5,14 C-171.5,14 -171.5,-14 -171.5,-14 C-171.5,-16.21 -169.71,-18 -167.5,-18 C-167.5,-18 167.5,-18 167.5,-18 C169.71,-18 171.5,-16.21 171.5,-14c " />
-                            </group>
-                        </group>
-                    </group>
-                </group>
-                <group
-                    android:name="_R_G_L_3_G_N_2_T_0"
-                    android:scaleX="0.6"
-                    android:scaleY="0"
-                    android:translateX="206"
-                    android:translateY="395">
-                    <group
-                        android:name="_R_G_L_3_G"
-                        android:translateX="-206"
-                        android:translateY="-446">
-                        <group
-                            android:name="_R_G_L_3_G_L_0_G"
-                            android:scaleY="0">
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_28_G"
-                                android:scaleY="0"
-                                android:translateX="206"
-                                android:translateY="446">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_28_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#000000"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -422 C206,-422 206,422 206,422 C206,435.25 195.25,446 182,446 C182,446 -182,446 -182,446 C-195.25,446 -206,435.25 -206,422 C-206,422 -206,-422 -206,-422 C-206,-435.25 -195.25,-446 -182,-446 C-182,-446 182,-446 182,-446 C195.25,-446 206,-435.25 206,-422c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_27_G"
-                                android:scaleY="0"
-                                android:translateX="206"
-                                android:translateY="422.5">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_27_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#dadce0"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -395.5 C206,-395.5 206,395.5 206,395.5 C206,395.5 -206,395.5 -206,395.5 C-206,395.5 -206,-395.5 -206,-395.5 C-206,-395.5 206,-395.5 206,-395.5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_26_G"
-                                android:scaleY="0"
-                                android:translateX="206"
-                                android:translateY="496.5">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_26_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#dadce0"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -377.5 C206,-377.5 206,377.5 206,377.5 C206,387.43 197.93,395.5 188,395.5 C188,395.5 -188,395.5 -188,395.5 C-197.93,395.5 -206,387.43 -206,377.5 C-206,377.5 -206,-377.5 -206,-377.5 C-206,-387.43 -197.93,-395.5 -188,-395.5 C-188,-395.5 188,-395.5 188,-395.5 C197.93,-395.5 206,-387.43 206,-377.5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_25_G"
-                                android:scaleY="0"
-                                android:translateX="206"
-                                android:translateY="50.5">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_25_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#e8eaed"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -23.5 C206,-23.5 206,50.5 206,50.5 C206,50.5 -206,50.5 -206,50.5 C-206,50.5 -206,-23.5 -206,-23.5 C-206,-23.5 206,-23.5 206,-23.5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_24_G"
-                                android:scaleY="0"
-                                android:translateX="206"
-                                android:translateY="50.5">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_24_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#e8eaed"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -32.5 C206,-32.5 206,32.5 206,32.5 C206,42.43 197.93,50.5 188,50.5 C188,50.5 -188,50.5 -188,50.5 C-197.93,50.5 -206,42.43 -206,32.5 C-206,32.5 -206,-32.5 -206,-32.5 C-206,-42.43 -197.93,-50.5 -188,-50.5 C-188,-50.5 188,-50.5 188,-50.5 C197.93,-50.5 206,-42.43 206,-32.5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_23_G"
-                                android:scaleY="0"
-                                android:translateX="54"
-                                android:translateY="157">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_23_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_22_G"
-                                android:scaleY="0"
-                                android:translateX="54"
-                                android:translateY="157">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_22_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_21_G"
-                                android:scaleY="0"
-                                android:translateX="148.5"
-                                android:translateY="148">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_21_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M46.5 -5 C46.5,-5 46.5,5 46.5,5 C46.5,7.21 44.71,9 42.5,9 C42.5,9 -42.5,9 -42.5,9 C-44.71,9 -46.5,7.21 -46.5,5 C-46.5,5 -46.5,-5 -46.5,-5 C-46.5,-7.21 -44.71,-9 -42.5,-9 C-42.5,-9 42.5,-9 42.5,-9 C44.71,-9 46.5,-7.21 46.5,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_20_G"
-                                android:scaleY="0"
-                                android:translateX="186"
-                                android:translateY="169">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_20_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M84 -4 C84,-4 84,4 84,4 C84,6.21 82.21,8 80,8 C80,8 -80,8 -80,8 C-82.21,8 -84,6.21 -84,4 C-84,4 -84,-4 -84,-4 C-84,-6.21 -82.21,-8 -80,-8 C-80,-8 80,-8 80,-8 C82.21,-8 84,-6.21 84,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_19_G"
-                                android:scaleY="0"
-                                android:translateX="54"
-                                android:translateY="245">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_19_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_18_G"
-                                android:scaleY="0"
-                                android:translateX="162"
-                                android:translateY="236">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_18_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M60 -5 C60,-5 60,5 60,5 C60,7.21 58.21,9 56,9 C56,9 -56,9 -56,9 C-58.21,9 -60,7.21 -60,5 C-60,5 -60,-5 -60,-5 C-60,-7.21 -58.21,-9 -56,-9 C-56,-9 56,-9 56,-9 C58.21,-9 60,-7.21 60,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_17_G"
-                                android:scaleY="0"
-                                android:translateX="171.5"
-                                android:translateY="257">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_17_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M69.5 -4 C69.5,-4 69.5,4 69.5,4 C69.5,6.21 67.71,8 65.5,8 C65.5,8 -65.5,8 -65.5,8 C-67.71,8 -69.5,6.21 -69.5,4 C-69.5,4 -69.5,-4 -69.5,-4 C-69.5,-6.21 -67.71,-8 -65.5,-8 C-65.5,-8 65.5,-8 65.5,-8 C67.71,-8 69.5,-6.21 69.5,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_16_G"
-                                android:scaleY="0"
-                                android:translateX="54"
-                                android:translateY="333">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_16_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_15_G"
-                                android:scaleY="0"
-                                android:translateX="158"
-                                android:translateY="324">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_15_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M56 -5 C56,-5 56,5 56,5 C56,7.21 54.21,9 52,9 C52,9 -52,9 -52,9 C-54.21,9 -56,7.21 -56,5 C-56,5 -56,-5 -56,-5 C-56,-7.21 -54.21,-9 -52,-9 C-52,-9 52,-9 52,-9 C54.21,-9 56,-7.21 56,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_14_G"
-                                android:scaleY="0"
-                                android:translateX="217.5"
-                                android:translateY="345">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_14_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M115.5 -4 C115.5,-4 115.5,4 115.5,4 C115.5,6.21 113.71,8 111.5,8 C111.5,8 -111.5,8 -111.5,8 C-113.71,8 -115.5,6.21 -115.5,4 C-115.5,4 -115.5,-4 -115.5,-4 C-115.5,-6.21 -113.71,-8 -111.5,-8 C-111.5,-8 111.5,-8 111.5,-8 C113.71,-8 115.5,-6.21 115.5,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_13_G"
-                                android:scaleY="0"
-                                android:translateX="54"
-                                android:translateY="421">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_13_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_12_G"
-                                android:scaleY="0"
-                                android:translateX="170"
-                                android:translateY="412">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_12_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M68 -5 C68,-5 68,5 68,5 C68,7.21 66.21,9 64,9 C64,9 -64,9 -64,9 C-66.21,9 -68,7.21 -68,5 C-68,5 -68,-5 -68,-5 C-68,-7.21 -66.21,-9 -64,-9 C-64,-9 64,-9 64,-9 C66.21,-9 68,-7.21 68,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_11_G"
-                                android:scaleY="0"
-                                android:translateX="198.5"
-                                android:translateY="433">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_11_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M96.5 -4 C96.5,-4 96.5,4 96.5,4 C96.5,6.21 94.71,8 92.5,8 C92.5,8 -92.5,8 -92.5,8 C-94.71,8 -96.5,6.21 -96.5,4 C-96.5,4 -96.5,-4 -96.5,-4 C-96.5,-6.21 -94.71,-8 -92.5,-8 C-92.5,-8 92.5,-8 92.5,-8 C94.71,-8 96.5,-6.21 96.5,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_10_G"
-                                android:scaleY="0"
-                                android:translateX="54"
-                                android:translateY="509">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_10_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_9_G"
-                                android:scaleY="0"
-                                android:translateX="135"
-                                android:translateY="500">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_9_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M33 -5 C33,-5 33,5 33,5 C33,7.21 31.21,9 29,9 C29,9 -29,9 -29,9 C-31.21,9 -33,7.21 -33,5 C-33,5 -33,-5 -33,-5 C-33,-7.21 -31.21,-9 -29,-9 C-29,-9 29,-9 29,-9 C31.21,-9 33,-7.21 33,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_8_G"
-                                android:scaleY="0"
-                                android:translateX="185.5"
-                                android:translateY="521">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_8_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M83.5 -4 C83.5,-4 83.5,4 83.5,4 C83.5,6.21 81.71,8 79.5,8 C79.5,8 -79.5,8 -79.5,8 C-81.71,8 -83.5,6.21 -83.5,4 C-83.5,4 -83.5,-4 -83.5,-4 C-83.5,-6.21 -81.71,-8 -79.5,-8 C-79.5,-8 79.5,-8 79.5,-8 C81.71,-8 83.5,-6.21 83.5,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_7_G"
-                                android:scaleY="0"
-                                android:translateX="54"
-                                android:translateY="597">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_7_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_6_G"
-                                android:scaleY="0"
-                                android:translateX="168.5"
-                                android:translateY="588">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_6_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M66.5 -5 C66.5,-5 66.5,5 66.5,5 C66.5,7.21 64.71,9 62.5,9 C62.5,9 -62.5,9 -62.5,9 C-64.71,9 -66.5,7.21 -66.5,5 C-66.5,5 -66.5,-5 -66.5,-5 C-66.5,-7.21 -64.71,-9 -62.5,-9 C-62.5,-9 62.5,-9 62.5,-9 C64.71,-9 66.5,-7.21 66.5,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_5_G"
-                                android:scaleY="0"
-                                android:translateX="198.5"
-                                android:translateY="609">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_5_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M96.5 -4 C96.5,-4 96.5,4 96.5,4 C96.5,6.21 94.71,8 92.5,8 C92.5,8 -92.5,8 -92.5,8 C-94.71,8 -96.5,6.21 -96.5,4 C-96.5,4 -96.5,-4 -96.5,-4 C-96.5,-6.21 -94.71,-8 -92.5,-8 C-92.5,-8 92.5,-8 92.5,-8 C94.71,-8 96.5,-6.21 96.5,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_4_G"
-                                android:scaleY="0"
-                                android:translateX="54"
-                                android:translateY="685">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_4_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_3_G"
-                                android:scaleY="0"
-                                android:translateX="162.5"
-                                android:translateY="676">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_3_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M60.5 -5 C60.5,-5 60.5,5 60.5,5 C60.5,7.21 58.71,9 56.5,9 C56.5,9 -56.5,9 -56.5,9 C-58.71,9 -60.5,7.21 -60.5,5 C-60.5,5 -60.5,-5 -60.5,-5 C-60.5,-7.21 -58.71,-9 -56.5,-9 C-56.5,-9 56.5,-9 56.5,-9 C58.71,-9 60.5,-7.21 60.5,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_2_G"
-                                android:scaleY="0"
-                                android:translateX="174"
-                                android:translateY="697">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_2_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M72 -4 C72,-4 72,4 72,4 C72,6.21 70.21,8 68,8 C68,8 -68,8 -68,8 C-70.21,8 -72,6.21 -72,4 C-72,4 -72,-4 -72,-4 C-72,-6.21 -70.21,-8 -68,-8 C-68,-8 68,-8 68,-8 C70.21,-8 72,-6.21 72,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_1_G"
-                                android:scaleY="0"
-                                android:translateX="313.5"
-                                android:translateY="798">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_1_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M74.5 0 C74.5,0 74.5,0 74.5,0 C74.5,15.45 61.95,28 46.5,28 C46.5,28 -46.5,28 -46.5,28 C-61.95,28 -74.5,15.45 -74.5,0 C-74.5,0 -74.5,0 -74.5,0 C-74.5,-15.45 -61.95,-28 -46.5,-28 C-46.5,-28 46.5,-28 46.5,-28 C61.95,-28 74.5,-15.45 74.5,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_0_G"
-                                android:scaleY="0"
-                                android:translateX="205.5"
-                                android:translateY="61">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_0_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#f8f9fa"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M171.5 -14 C171.5,-14 171.5,14 171.5,14 C171.5,16.21 169.71,18 167.5,18 C167.5,18 -167.5,18 -167.5,18 C-169.71,18 -171.5,16.21 -171.5,14 C-171.5,14 -171.5,-14 -171.5,-14 C-171.5,-16.21 -169.71,-18 -167.5,-18 C-167.5,-18 167.5,-18 167.5,-18 C169.71,-18 171.5,-16.21 171.5,-14c " />
-                            </group>
-                        </group>
-                    </group>
-                </group>
-                <group
-                    android:name="_R_G_L_2_G_N_2_T_0"
-                    android:scaleX="0.6"
-                    android:scaleY="0"
-                    android:translateX="206"
-                    android:translateY="395">
-                    <group
-                        android:name="_R_G_L_2_G"
-                        android:scaleX="1.3767699999999998"
-                        android:scaleY="1.3767699999999998"
-                        android:translateY="-508.163">
-                        <group
-                            android:name="_R_G_L_2_G_D_0_P_0_G_0_T_0"
-                            android:scaleX="0"
-                            android:scaleY="0">
-                            <path
-                                android:name="_R_G_L_2_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#9aa0a6"
-                                android:fillType="nonZero"
-                                android:pathData=" M0 25 C13.81,25 25,13.81 25,0 C25,-13.81 13.81,-25 0,-25 C-13.81,-25 -25,-13.81 -25,0 C-25,13.81 -13.81,25 0,25c " />
-                        </group>
-                    </group>
-                </group>
-                <group
-                    android:name="_R_G_L_1_G_N_2_T_0"
-                    android:scaleX="0.6"
-                    android:scaleY="0"
-                    android:translateX="206"
-                    android:translateY="395">
-                    <group
-                        android:name="_R_G_L_1_G"
-                        android:scaleX="1.39"
-                        android:scaleY="1.39"
-                        android:translateX="-556.176"
-                        android:translateY="-7.307">
-                        <path
-                            android:name="_R_G_L_1_G_D_0_P_0"
-                            android:fillAlpha="1"
-                            android:fillColor="@color/gesture_tutorial_fake_previous_task_view_color"
-                            android:fillType="nonZero"
-                            android:pathData=" M135 -301 C135,-301 135,311 135,311 C135,319.28 128.28,326 120,326 C120,326 -120,326 -120,326 C-128.28,326 -135,319.28 -135,311 C-135,311 -135,-301 -135,-301 C-135,-309.28 -128.28,-316 -120,-316 C-120,-316 120,-316 120,-316 C128.28,-316 135,-309.28 135,-301c " />
-                    </group>
-                </group>
-                <group
-                    android:name="_R_G_L_0_G"
-                    android:translateX="206"
-                    android:translateY="446">
-                    <path
-                        android:name="_R_G_L_0_G_D_0_P_0"
-                        android:fillAlpha="0"
-                        android:fillColor="@color/gesture_tutorial_primary_color"
-                        android:fillType="nonZero"
-                        android:pathData=" M0 406 C21.54,406 39,423.46 39,445 C39,466.54 21.54,484 0,484 C-21.54,484 -39,466.54 -39,445 C-39,423.46 -21.54,406 0,406c " />
-                </group>
-            </group>
-            <group android:name="time_group" />
-        </vector>
-    </aapt:attr>
-</animated-vector>
\ No newline at end of file
diff --git a/res/drawable/gesture_tutorial_ripple.xml b/quickstep/res/drawable/gesture_tutorial_ripple.xml
similarity index 100%
rename from res/drawable/gesture_tutorial_ripple.xml
rename to quickstep/res/drawable/gesture_tutorial_ripple.xml
diff --git a/quickstep/res/drawable/ic_sysbar_accessibility_button.xml b/quickstep/res/drawable/ic_sysbar_accessibility_button.xml
new file mode 100644
index 0000000..e0d5406
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_accessibility_button.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="21dp"
+    android:height="21dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:pathData="M20.5,6c-2.61,0.7 -5.67,1 -8.5,1S6.11,6.7 3.5,6L3,8c1.86,0.5 4,0.83 6,1v13h2v-6h2v6h2V9c2,-0.17 4.14,-0.5 6,-1L20.5,6zM12,6c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2s-2,0.9 -2,2S10.9,6 12,6z"
+        android:fillColor="@android:color/white"
+        />
+</vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_sysbar_rotate_button_ccw_start_0.xml b/quickstep/res/drawable/ic_sysbar_rotate_button_ccw_start_0.xml
new file mode 100644
index 0000000..ff5cb9e
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_rotate_button_ccw_start_0.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2020 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.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector android:name="root"
+                android:width="28dp"
+                android:height="28dp"
+                android:viewportWidth="28.0"
+                android:viewportHeight="28.0">
+            <!-- Use scaleX to flip icon so arrows always point in the direction of motion -->
+            <group android:name="icon" android:pivotX="14" android:pivotY="14"
+                   android:scaleX="1">
+                <!-- Tint color to be set directly -->
+                <path android:fillColor="#FFFFFFFF"
+                      android:pathData="M12.02,10.83L9.25,8.06l2.77,-2.77l1.12,1.12l-0.85,0.86h5.16c0.72,0 1.31,0.56 1.31,1.26v9.16l-1.58,-1.58V8.85h-4.89l0.86,0.86L12.02,10.83zM15.98,17.17l-1.12,1.12l0.85,0.86h-4.88v-7.26L9.25,10.3v9.17c0,0.7 0.59,1.26 1.31,1.26h5.16v0.01l-0.85,0.85l1.12,1.12l2.77,-2.77L15.98,17.17z"/>
+            </group>
+        </vector>
+    </aapt:attr>
+
+    <!-- Repeat all animations 5 times but don't fade out at the end -->
+    <target android:name="root">
+        <aapt:attr name="android:animation">
+            <set android:ordering="sequentially">
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+                <!-- Linear fade out -->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="1700"
+                                android:valueFrom="1"
+                                android:valueTo="0"
+                                android:interpolator="@android:anim/linear_interpolator"/>
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+                <!-- Linear fade out -->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="1700"
+                                android:valueFrom="1"
+                                android:valueTo="0"
+                                android:interpolator="@android:anim/linear_interpolator"/>
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+                <!-- Linear fade out -->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="1700"
+                                android:valueFrom="1"
+                                android:valueTo="0"
+                                android:interpolator="@android:anim/linear_interpolator"/>
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+                <!-- Linear fade out -->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="1700"
+                                android:valueFrom="1"
+                                android:valueTo="0"
+                                android:interpolator="@android:anim/linear_interpolator"/>
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="icon">
+        <aapt:attr name="android:animation">
+            <set android:ordering="sequentially">
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="100"
+                                android:duration="600"
+                                android:valueFrom="0"
+                                android:valueTo="-90">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+
+                <!-- Reset rotation position for fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="1300"
+                                android:duration="100"
+                                android:valueFrom="0"
+                                android:valueTo="0"/>
+
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:duration="600"
+                                android:valueFrom="0"
+                                android:valueTo="-90">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+
+                <!-- Reset rotation position for fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="1300"
+                                android:duration="100"
+                                android:valueFrom="0"
+                                android:valueTo="0"/>
+
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:duration="600"
+                                android:valueFrom="0"
+                                android:valueTo="-90">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+
+                <!-- Reset rotation position for fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="1300"
+                                android:duration="100"
+                                android:valueFrom="0"
+                                android:valueTo="0"/>
+
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:duration="600"
+                                android:valueFrom="0"
+                                android:valueTo="-90">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+
+                <!-- Reset rotation position for fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="1300"
+                                android:duration="100"
+                                android:valueFrom="0"
+                                android:valueTo="0"/>
+
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:duration="600"
+                                android:valueFrom="0"
+                                android:valueTo="-90">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_sysbar_rotate_button_ccw_start_90.xml b/quickstep/res/drawable/ic_sysbar_rotate_button_ccw_start_90.xml
new file mode 100644
index 0000000..90fedb1
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_rotate_button_ccw_start_90.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2020 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.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector android:name="root"
+                android:width="28dp"
+                android:height="28dp"
+                android:viewportWidth="28.0"
+                android:viewportHeight="28.0">
+            <!-- Use scaleX to flip icon so arrows always point in the direction of motion -->
+            <group android:name="icon" android:pivotX="14" android:pivotY="14"
+                   android:scaleX="1">
+                <!-- Tint color to be set directly -->
+                <path android:fillColor="#FFFFFFFF"
+                      android:pathData="M12.02,10.83L9.25,8.06l2.77,-2.77l1.12,1.12l-0.85,0.86h5.16c0.72,0 1.31,0.56 1.31,1.26v9.16l-1.58,-1.58V8.85h-4.89l0.86,0.86L12.02,10.83zM15.98,17.17l-1.12,1.12l0.85,0.86h-4.88v-7.26L9.25,10.3v9.17c0,0.7 0.59,1.26 1.31,1.26h5.16v0.01l-0.85,0.85l1.12,1.12l2.77,-2.77L15.98,17.17z"/>
+            </group>
+        </vector>
+    </aapt:attr>
+
+    <!-- Repeat all animations 5 times but don't fade out at the end -->
+    <target android:name="root">
+        <aapt:attr name="android:animation">
+            <set android:ordering="sequentially">
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+                <!-- Linear fade out -->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="1700"
+                                android:valueFrom="1"
+                                android:valueTo="0"
+                                android:interpolator="@android:anim/linear_interpolator"/>
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+                <!-- Linear fade out -->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="1700"
+                                android:valueFrom="1"
+                                android:valueTo="0"
+                                android:interpolator="@android:anim/linear_interpolator"/>
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+                <!-- Linear fade out -->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="1700"
+                                android:valueFrom="1"
+                                android:valueTo="0"
+                                android:interpolator="@android:anim/linear_interpolator"/>
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+                <!-- Linear fade out -->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="1700"
+                                android:valueFrom="1"
+                                android:valueTo="0"
+                                android:interpolator="@android:anim/linear_interpolator"/>
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="icon">
+        <aapt:attr name="android:animation">
+            <set android:ordering="sequentially">
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="100"
+                                android:duration="600"
+                                android:valueFrom="90"
+                                android:valueTo="0">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+
+                <!-- Reset rotation position for fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="1300"
+                                android:duration="100"
+                                android:valueFrom="90"
+                                android:valueTo="90"/>
+
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:duration="600"
+                                android:valueFrom="90"
+                                android:valueTo="0">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+
+                <!-- Reset rotation position for fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="1300"
+                                android:duration="100"
+                                android:valueFrom="90"
+                                android:valueTo="90"/>
+
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:duration="600"
+                                android:valueFrom="90"
+                                android:valueTo="0">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+
+                <!-- Reset rotation position for fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="1300"
+                                android:duration="100"
+                                android:valueFrom="90"
+                                android:valueTo="90"/>
+
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:duration="600"
+                                android:valueFrom="90"
+                                android:valueTo="0">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+
+                <!-- Reset rotation position for fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="1300"
+                                android:duration="100"
+                                android:valueFrom="90"
+                                android:valueTo="90"/>
+
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:duration="600"
+                                android:valueFrom="90"
+                                android:valueTo="0">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_sysbar_rotate_button_cw_start_0.xml b/quickstep/res/drawable/ic_sysbar_rotate_button_cw_start_0.xml
new file mode 100644
index 0000000..a89e7a3
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_rotate_button_cw_start_0.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2020 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.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector android:name="root"
+                android:width="28dp"
+                android:height="28dp"
+                android:viewportWidth="28.0"
+                android:viewportHeight="28.0">
+            <!-- Use scaleX to flip icon so arrows always point in the direction of motion -->
+            <group android:name="icon" android:pivotX="14" android:pivotY="14"
+                   android:scaleX="-1">
+                <!-- Tint color to be set directly -->
+                <path android:fillColor="#FFFFFFFF"
+                      android:pathData="M12.02,10.83L9.25,8.06l2.77,-2.77l1.12,1.12l-0.85,0.86h5.16c0.72,0 1.31,0.56 1.31,1.26v9.16l-1.58,-1.58V8.85h-4.89l0.86,0.86L12.02,10.83zM15.98,17.17l-1.12,1.12l0.85,0.86h-4.88v-7.26L9.25,10.3v9.17c0,0.7 0.59,1.26 1.31,1.26h5.16v0.01l-0.85,0.85l1.12,1.12l2.77,-2.77L15.98,17.17z"/>
+            </group>
+        </vector>
+    </aapt:attr>
+
+    <!-- Repeat all animations 5 times but don't fade out at the end -->
+    <target android:name="root">
+        <aapt:attr name="android:animation">
+            <set android:ordering="sequentially">
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+                <!-- Linear fade out -->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="1700"
+                                android:valueFrom="1"
+                                android:valueTo="0"
+                                android:interpolator="@android:anim/linear_interpolator"/>
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+                <!-- Linear fade out -->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="1700"
+                                android:valueFrom="1"
+                                android:valueTo="0"
+                                android:interpolator="@android:anim/linear_interpolator"/>
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+                <!-- Linear fade out -->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="1700"
+                                android:valueFrom="1"
+                                android:valueTo="0"
+                                android:interpolator="@android:anim/linear_interpolator"/>
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+                <!-- Linear fade out -->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="1700"
+                                android:valueFrom="1"
+                                android:valueTo="0"
+                                android:interpolator="@android:anim/linear_interpolator"/>
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="icon">
+        <aapt:attr name="android:animation">
+            <set android:ordering="sequentially">
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="100"
+                                android:duration="600"
+                                android:valueFrom="0"
+                                android:valueTo="90">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+
+                <!-- Reset rotation position for fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="1300"
+                                android:duration="100"
+                                android:valueFrom="0"
+                                android:valueTo="0"/>
+
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:duration="600"
+                                android:valueFrom="0"
+                                android:valueTo="90">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+
+                <!-- Reset rotation position for fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="1300"
+                                android:duration="100"
+                                android:valueFrom="0"
+                                android:valueTo="0"/>
+
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:duration="600"
+                                android:valueFrom="0"
+                                android:valueTo="90">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+
+                <!-- Reset rotation position for fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="1300"
+                                android:duration="100"
+                                android:valueFrom="0"
+                                android:valueTo="0"/>
+
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:duration="600"
+                                android:valueFrom="0"
+                                android:valueTo="90">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+
+                <!-- Reset rotation position for fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="1300"
+                                android:duration="100"
+                                android:valueFrom="0"
+                                android:valueTo="0"/>
+
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:duration="600"
+                                android:valueFrom="0"
+                                android:valueTo="90">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_sysbar_rotate_button_cw_start_90.xml b/quickstep/res/drawable/ic_sysbar_rotate_button_cw_start_90.xml
new file mode 100644
index 0000000..0dc67b0
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_rotate_button_cw_start_90.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2020 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.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector android:name="root"
+                android:width="28dp"
+                android:height="28dp"
+                android:viewportWidth="28.0"
+                android:viewportHeight="28.0">
+            <!-- Use scaleX to flip icon so arrows always point in the direction of motion -->
+            <group android:name="icon" android:pivotX="14" android:pivotY="14"
+                   android:scaleX="-1">
+                <!-- Tint color to be set directly -->
+                <path android:fillColor="#FFFFFFFF"
+                      android:pathData="M12.02,10.83L9.25,8.06l2.77,-2.77l1.12,1.12l-0.85,0.86h5.16c0.72,0 1.31,0.56 1.31,1.26v9.16l-1.58,-1.58V8.85h-4.89l0.86,0.86L12.02,10.83zM15.98,17.17l-1.12,1.12l0.85,0.86h-4.88v-7.26L9.25,10.3v9.17c0,0.7 0.59,1.26 1.31,1.26h5.16v0.01l-0.85,0.85l1.12,1.12l2.77,-2.77L15.98,17.17z"/>
+            </group>
+        </vector>
+    </aapt:attr>
+
+    <!-- Repeat all animations 5 times but don't fade out at the end -->
+    <target android:name="root">
+        <aapt:attr name="android:animation">
+            <set android:ordering="sequentially">
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+                <!-- Linear fade out -->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="1700"
+                                android:valueFrom="1"
+                                android:valueTo="0"
+                                android:interpolator="@android:anim/linear_interpolator"/>
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+                <!-- Linear fade out -->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="1700"
+                                android:valueFrom="1"
+                                android:valueTo="0"
+                                android:interpolator="@android:anim/linear_interpolator"/>
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+                <!-- Linear fade out -->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="1700"
+                                android:valueFrom="1"
+                                android:valueTo="0"
+                                android:interpolator="@android:anim/linear_interpolator"/>
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+                <!-- Linear fade out -->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="1700"
+                                android:valueFrom="1"
+                                android:valueTo="0"
+                                android:interpolator="@android:anim/linear_interpolator"/>
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="icon">
+        <aapt:attr name="android:animation">
+            <set android:ordering="sequentially">
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="100"
+                                android:duration="600"
+                                android:valueFrom="90"
+                                android:valueTo="180">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+
+                <!-- Reset rotation position for fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="1300"
+                                android:duration="100"
+                                android:valueFrom="90"
+                                android:valueTo="90"/>
+
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:duration="600"
+                                android:valueFrom="90"
+                                android:valueTo="180">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+
+                <!-- Reset rotation position for fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="1300"
+                                android:duration="100"
+                                android:valueFrom="90"
+                                android:valueTo="90"/>
+
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:duration="600"
+                                android:valueFrom="90"
+                                android:valueTo="180">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+
+                <!-- Reset rotation position for fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="1300"
+                                android:duration="100"
+                                android:valueFrom="90"
+                                android:valueTo="90"/>
+
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:duration="600"
+                                android:valueFrom="90"
+                                android:valueTo="180">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+
+                <!-- Reset rotation position for fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="1300"
+                                android:duration="100"
+                                android:valueFrom="90"
+                                android:valueTo="90"/>
+
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:duration="600"
+                                android:valueFrom="90"
+                                android:valueTo="180">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/mock_conversation.xml b/quickstep/res/drawable/mock_conversation.xml
deleted file mode 100644
index 272d9ed..0000000
--- a/quickstep/res/drawable/mock_conversation.xml
+++ /dev/null
@@ -1,212 +0,0 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:aapt="http://schemas.android.com/aapt">
-    <target android:name="time_group">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="83"
-                    android:propertyName="translateX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <aapt:attr name="android:drawable">
-        <vector
-            android:width="412dp"
-            android:height="892dp"
-            android:viewportHeight="892"
-            android:viewportWidth="412">
-            <group android:name="_R_G">
-                <group
-                    android:name="_R_G_L_1_G"
-                    android:translateX="206"
-                    android:translateY="446">
-                    <path
-                        android:name="_R_G_L_1_G_D_0_P_0"
-                        android:fillAlpha="1"
-                        android:fillColor="#dadce0"
-                        android:fillType="nonZero"
-                        android:pathData=" M206 -446 C206,-446 206,446 206,446 C206,446 -206,446 -206,446 C-206,446 -206,-446 -206,-446 C-206,-446 206,-446 206,-446c " />
-                </group>
-                <group
-                    android:name="_R_G_L_0_G"
-                    android:pivotX="206"
-                    android:pivotY="446"
-                    android:scaleX="1"
-                    android:scaleY="1">
-                    <group android:name="_R_G_L_0_G_L_0_G">
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_14_G"
-                            android:translateX="206"
-                            android:translateY="446">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_14_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#000000"
-                                android:fillType="nonZero"
-                                android:pathData=" M206 -422 C206,-422 206,422 206,422 C206,435.25 195.25,446 182,446 C182,446 -182,446 -182,446 C-195.25,446 -206,435.25 -206,422 C-206,422 -206,-422 -206,-422 C-206,-435.25 -195.25,-446 -182,-446 C-182,-446 182,-446 182,-446 C195.25,-446 206,-435.25 206,-422c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_13_G"
-                            android:translateX="206"
-                            android:translateY="496.5">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_13_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#f1f3f4"
-                                android:fillType="nonZero"
-                                android:pathData=" M206 -395.5 C206,-395.5 206,395.5 206,395.5 C206,395.5 -206,395.5 -206,395.5 C-206,395.5 -206,-395.5 -206,-395.5 C-206,-395.5 206,-395.5 206,-395.5c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_12_G"
-                            android:translateX="206"
-                            android:translateY="50.5">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_12_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#e8eaed"
-                                android:fillType="nonZero"
-                                android:pathData=" M206 -50.5 C206,-50.5 206,50.5 206,50.5 C206,50.5 -206,50.5 -206,50.5 C-206,50.5 -206,-50.5 -206,-50.5 C-206,-50.5 206,-50.5 206,-50.5c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_11_G"
-                            android:translateX="206"
-                            android:translateY="804">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_11_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#dadce0"
-                                android:fillType="nonZero"
-                                android:pathData=" M177 0 C177,12.15 167.15,22 155,22 C155,22 -155,22 -155,22 C-167.15,22 -177,12.15 -177,0 C-177,-12.15 -167.15,-22 -155,-22 C-155,-22 155,-22 155,-22 C167.15,-22 177,-12.15 177,0c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_10_G"
-                            android:translateX="117.5"
-                            android:translateY="61">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_10_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#dadce0"
-                                android:fillType="nonZero"
-                                android:pathData=" M83.5 -14 C83.5,-14 83.5,14 83.5,14 C83.5,16.21 81.71,18 79.5,18 C79.5,18 -79.5,18 -79.5,18 C-81.71,18 -83.5,16.21 -83.5,14 C-83.5,14 -83.5,-14 -83.5,-14 C-83.5,-16.21 -81.71,-18 -79.5,-18 C-79.5,-18 79.5,-18 79.5,-18 C81.71,-18 83.5,-16.21 83.5,-14c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_9_G"
-                            android:translateX="370"
-                            android:translateY="61">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_9_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#dadce0"
-                                android:fillType="nonZero"
-                                android:pathData=" M18 -14 C18,-14 18,14 18,14 C18,16.21 16.21,18 14,18 C14,18 -14,18 -14,18 C-16.21,18 -18,16.21 -18,14 C-18,14 -18,-14 -18,-14 C-18,-16.21 -16.21,-18 -14,-18 C-14,-18 14,-18 14,-18 C16.21,-18 18,-16.21 18,-14c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_8_G"
-                            android:translateX="318"
-                            android:translateY="61">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_8_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#dadce0"
-                                android:fillType="nonZero"
-                                android:pathData=" M18 -14 C18,-14 18,14 18,14 C18,16.21 16.21,18 14,18 C14,18 -14,18 -14,18 C-16.21,18 -18,16.21 -18,14 C-18,14 -18,-14 -18,-14 C-18,-16.21 -16.21,-18 -14,-18 C-14,-18 14,-18 14,-18 C16.21,-18 18,-16.21 18,-14c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_7_G"
-                            android:translateX="48"
-                            android:translateY="618">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_7_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#dadce0"
-                                android:fillType="nonZero"
-                                android:pathData=" M22 0 C22,12.15 12.15,22 0,22 C-12.15,22 -22,12.15 -22,0 C-22,-12.15 -12.15,-22 0,-22 C12.15,-22 22,-12.15 22,0c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_6_G"
-                            android:translateX="48"
-                            android:translateY="396">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_6_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#dadce0"
-                                android:fillType="nonZero"
-                                android:pathData=" M22 0 C22,12.15 12.15,22 0,22 C-12.15,22 -22,12.15 -22,0 C-22,-12.15 -12.15,-22 0,-22 C12.15,-22 22,-12.15 22,0c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_5_G"
-                            android:translateX="259"
-                            android:translateY="286">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_5_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M135 -38 C135,-38 135,38 135,38 C135,47.94 126.94,56 117,56 C117,56 -117,56 -117,56 C-126.94,56 -135,47.94 -135,38 C-135,38 -135,-38 -135,-38 C-135,-47.94 -126.94,-56 -117,-56 C-117,-56 117,-56 117,-56 C126.94,-56 135,-47.94 135,-38c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_4_G"
-                            android:translateX="259"
-                            android:translateY="468">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_4_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M134.5 4 C134.5,4 134.5,14 134.5,14 C134.5,16.21 132.71,18 130.5,18 C130.5,18 44.5,18 44.5,18 C42.29,18 40.5,16.21 40.5,14 C40.5,14 40.5,4 40.5,4 C40.5,1.79 42.29,0 44.5,0 C44.5,0 130.5,0 130.5,0 C132.71,0 134.5,1.79 134.5,4c  M135 0 C135,9.66 127.17,17.5 117.5,17.5 C117.5,17.5 31,17.5 31,17.5 C21.34,17.5 13.5,9.66 13.5,0 C13.5,-9.66 21.34,-17.5 31,-17.5 C31,-17.5 117.5,-17.5 117.5,-17.5 C127.17,-17.5 135,-9.66 135,0c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_3_G"
-                            android:translateX="259"
-                            android:translateY="526.5">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_3_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M135 -32.5 C135,-32.5 135,20.5 135,20.5 C135,22.71 133.21,24.5 131,24.5 C131,24.5 -95,24.5 -95,24.5 C-97.21,24.5 -99,22.71 -99,20.5 C-99,20.5 -99,-32.5 -99,-32.5 C-99,-34.71 -97.21,-36.5 -95,-36.5 C-95,-36.5 131,-36.5 131,-36.5 C133.21,-36.5 135,-34.71 135,-32.5c  M135 -18.5 C135,-18.5 135,18.5 135,18.5 C135,28.44 126.94,36.5 117,36.5 C117,36.5 -117,36.5 -117,36.5 C-126.94,36.5 -135,28.44 -135,18.5 C-135,18.5 -135,-18.5 -135,-18.5 C-135,-28.44 -126.94,-36.5 -117,-36.5 C-117,-36.5 117,-36.5 117,-36.5 C126.94,-36.5 135,-28.44 135,-18.5c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_2_G"
-                            android:translateX="259"
-                            android:translateY="708.5">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_2_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M135 -18.5 C135,-18.5 135,18.5 135,18.5 C135,28.44 126.94,36.5 117,36.5 C117,36.5 -117,36.5 -117,36.5 C-126.94,36.5 -135,28.44 -135,18.5 C-135,18.5 -135,-18.5 -135,-18.5 C-135,-28.44 -126.94,-36.5 -117,-36.5 C-117,-36.5 117,-36.5 117,-36.5 C126.94,-36.5 135,-28.44 135,-18.5c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_1_G"
-                            android:translateX="222"
-                            android:translateY="617">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_1_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#e8eaed"
-                                android:fillType="nonZero"
-                                android:pathData=" M45.5 0 C45.5,9.66 37.67,17.5 28,17.5 C28,17.5 -117.5,17.5 -117.5,17.5 C-127.16,17.5 -135,9.66 -135,0 C-135,-9.66 -127.16,-17.5 -117.5,-17.5 C-117.5,-17.5 28,-17.5 28,-17.5 C37.67,-17.5 45.5,-9.66 45.5,0c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_0_G"
-                            android:translateX="222"
-                            android:translateY="395.5">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_0_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#e8eaed"
-                                android:fillType="nonZero"
-                                android:pathData=" M77 0 C77,9.66 69.16,17.5 59.5,17.5 C59.5,17.5 -117.5,17.5 -117.5,17.5 C-127.16,17.5 -135,9.66 -135,0 C-135,-9.66 -127.16,-17.5 -117.5,-17.5 C-117.5,-17.5 59.5,-17.5 59.5,-17.5 C69.16,-17.5 77,-9.66 77,0c " />
-                        </group>
-                    </group>
-                </group>
-            </group>
-            <group android:name="time_group" />
-        </vector>
-    </aapt:attr>
-</animated-vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/mock_conversations_list.xml b/quickstep/res/drawable/mock_conversations_list.xml
deleted file mode 100644
index 2dbc88f..0000000
--- a/quickstep/res/drawable/mock_conversations_list.xml
+++ /dev/null
@@ -1,361 +0,0 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:aapt="http://schemas.android.com/aapt">
-    <target android:name="time_group">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="83"
-                    android:propertyName="translateX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <aapt:attr name="android:drawable">
-        <vector
-            android:width="412dp"
-            android:height="892dp"
-            android:viewportHeight="892"
-            android:viewportWidth="412">
-            <group android:name="_R_G">
-                <group
-                    android:name="_R_G_L_1_G"
-                    android:translateX="206"
-                    android:translateY="446">
-                    <path
-                        android:name="_R_G_L_1_G_D_0_P_0"
-                        android:fillAlpha="1"
-                        android:fillColor="#dadce0"
-                        android:fillType="nonZero"
-                        android:pathData=" M206 -446 C206,-446 206,446 206,446 C206,446 -206,446 -206,446 C-206,446 -206,-446 -206,-446 C-206,-446 206,-446 206,-446c " />
-                </group>
-                <group android:name="_R_G_L_0_G">
-                    <group android:name="_R_G_L_0_G_L_0_G">
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_28_G"
-                            android:translateX="206"
-                            android:translateY="446">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_28_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#000000"
-                                android:fillType="nonZero"
-                                android:pathData=" M206 -422 C206,-422 206,422 206,422 C206,435.25 195.25,446 182,446 C182,446 -182,446 -182,446 C-195.25,446 -206,435.25 -206,422 C-206,422 -206,-422 -206,-422 C-206,-435.25 -195.25,-446 -182,-446 C-182,-446 182,-446 182,-446 C195.25,-446 206,-435.25 206,-422c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_27_G"
-                            android:translateX="206"
-                            android:translateY="422.5">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_27_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#dadce0"
-                                android:fillType="nonZero"
-                                android:pathData=" M206 -395.5 C206,-395.5 206,395.5 206,395.5 C206,395.5 -206,395.5 -206,395.5 C-206,395.5 -206,-395.5 -206,-395.5 C-206,-395.5 206,-395.5 206,-395.5c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_26_G"
-                            android:translateX="206"
-                            android:translateY="496.5">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_26_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#dadce0"
-                                android:fillType="nonZero"
-                                android:pathData=" M206 -377.5 C206,-377.5 206,377.5 206,377.5 C206,387.43 197.93,395.5 188,395.5 C188,395.5 -188,395.5 -188,395.5 C-197.93,395.5 -206,387.43 -206,377.5 C-206,377.5 -206,-377.5 -206,-377.5 C-206,-387.43 -197.93,-395.5 -188,-395.5 C-188,-395.5 188,-395.5 188,-395.5 C197.93,-395.5 206,-387.43 206,-377.5c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_25_G"
-                            android:translateX="206"
-                            android:translateY="50.5">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_25_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#e8eaed"
-                                android:fillType="nonZero"
-                                android:pathData=" M206 -23.5 C206,-23.5 206,50.5 206,50.5 C206,50.5 -206,50.5 -206,50.5 C-206,50.5 -206,-23.5 -206,-23.5 C-206,-23.5 206,-23.5 206,-23.5c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_24_G"
-                            android:translateX="206"
-                            android:translateY="50.5">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_24_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#e8eaed"
-                                android:fillType="nonZero"
-                                android:pathData=" M206 -32.5 C206,-32.5 206,32.5 206,32.5 C206,42.43 197.93,50.5 188,50.5 C188,50.5 -188,50.5 -188,50.5 C-197.93,50.5 -206,42.43 -206,32.5 C-206,32.5 -206,-32.5 -206,-32.5 C-206,-42.43 -197.93,-50.5 -188,-50.5 C-188,-50.5 188,-50.5 188,-50.5 C197.93,-50.5 206,-42.43 206,-32.5c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_23_G"
-                            android:translateX="54"
-                            android:translateY="157">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_23_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#9aa0a6"
-                                android:fillType="nonZero"
-                                android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_22_G"
-                            android:translateX="54"
-                            android:translateY="157">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_22_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#9aa0a6"
-                                android:fillType="nonZero"
-                                android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_21_G"
-                            android:translateX="148.5"
-                            android:translateY="148">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_21_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M46.5 -5 C46.5,-5 46.5,5 46.5,5 C46.5,7.21 44.71,9 42.5,9 C42.5,9 -42.5,9 -42.5,9 C-44.71,9 -46.5,7.21 -46.5,5 C-46.5,5 -46.5,-5 -46.5,-5 C-46.5,-7.21 -44.71,-9 -42.5,-9 C-42.5,-9 42.5,-9 42.5,-9 C44.71,-9 46.5,-7.21 46.5,-5c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_20_G"
-                            android:translateX="186"
-                            android:translateY="169">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_20_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M84 -4 C84,-4 84,4 84,4 C84,6.21 82.21,8 80,8 C80,8 -80,8 -80,8 C-82.21,8 -84,6.21 -84,4 C-84,4 -84,-4 -84,-4 C-84,-6.21 -82.21,-8 -80,-8 C-80,-8 80,-8 80,-8 C82.21,-8 84,-6.21 84,-4c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_19_G"
-                            android:translateX="54"
-                            android:translateY="245">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_19_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#9aa0a6"
-                                android:fillType="nonZero"
-                                android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_18_G"
-                            android:translateX="162"
-                            android:translateY="236">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_18_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M60 -5 C60,-5 60,5 60,5 C60,7.21 58.21,9 56,9 C56,9 -56,9 -56,9 C-58.21,9 -60,7.21 -60,5 C-60,5 -60,-5 -60,-5 C-60,-7.21 -58.21,-9 -56,-9 C-56,-9 56,-9 56,-9 C58.21,-9 60,-7.21 60,-5c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_17_G"
-                            android:translateX="171.5"
-                            android:translateY="257">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_17_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M69.5 -4 C69.5,-4 69.5,4 69.5,4 C69.5,6.21 67.71,8 65.5,8 C65.5,8 -65.5,8 -65.5,8 C-67.71,8 -69.5,6.21 -69.5,4 C-69.5,4 -69.5,-4 -69.5,-4 C-69.5,-6.21 -67.71,-8 -65.5,-8 C-65.5,-8 65.5,-8 65.5,-8 C67.71,-8 69.5,-6.21 69.5,-4c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_16_G"
-                            android:translateX="54"
-                            android:translateY="333">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_16_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#9aa0a6"
-                                android:fillType="nonZero"
-                                android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_15_G"
-                            android:translateX="158"
-                            android:translateY="324">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_15_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M56 -5 C56,-5 56,5 56,5 C56,7.21 54.21,9 52,9 C52,9 -52,9 -52,9 C-54.21,9 -56,7.21 -56,5 C-56,5 -56,-5 -56,-5 C-56,-7.21 -54.21,-9 -52,-9 C-52,-9 52,-9 52,-9 C54.21,-9 56,-7.21 56,-5c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_14_G"
-                            android:translateX="217.5"
-                            android:translateY="345">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_14_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M115.5 -4 C115.5,-4 115.5,4 115.5,4 C115.5,6.21 113.71,8 111.5,8 C111.5,8 -111.5,8 -111.5,8 C-113.71,8 -115.5,6.21 -115.5,4 C-115.5,4 -115.5,-4 -115.5,-4 C-115.5,-6.21 -113.71,-8 -111.5,-8 C-111.5,-8 111.5,-8 111.5,-8 C113.71,-8 115.5,-6.21 115.5,-4c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_13_G"
-                            android:translateX="54"
-                            android:translateY="421">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_13_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#9aa0a6"
-                                android:fillType="nonZero"
-                                android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_12_G"
-                            android:translateX="170"
-                            android:translateY="412">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_12_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M68 -5 C68,-5 68,5 68,5 C68,7.21 66.21,9 64,9 C64,9 -64,9 -64,9 C-66.21,9 -68,7.21 -68,5 C-68,5 -68,-5 -68,-5 C-68,-7.21 -66.21,-9 -64,-9 C-64,-9 64,-9 64,-9 C66.21,-9 68,-7.21 68,-5c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_11_G"
-                            android:translateX="198.5"
-                            android:translateY="433">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_11_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M96.5 -4 C96.5,-4 96.5,4 96.5,4 C96.5,6.21 94.71,8 92.5,8 C92.5,8 -92.5,8 -92.5,8 C-94.71,8 -96.5,6.21 -96.5,4 C-96.5,4 -96.5,-4 -96.5,-4 C-96.5,-6.21 -94.71,-8 -92.5,-8 C-92.5,-8 92.5,-8 92.5,-8 C94.71,-8 96.5,-6.21 96.5,-4c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_10_G"
-                            android:translateX="54"
-                            android:translateY="509">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_10_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#9aa0a6"
-                                android:fillType="nonZero"
-                                android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_9_G"
-                            android:translateX="135"
-                            android:translateY="500">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_9_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M33 -5 C33,-5 33,5 33,5 C33,7.21 31.21,9 29,9 C29,9 -29,9 -29,9 C-31.21,9 -33,7.21 -33,5 C-33,5 -33,-5 -33,-5 C-33,-7.21 -31.21,-9 -29,-9 C-29,-9 29,-9 29,-9 C31.21,-9 33,-7.21 33,-5c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_8_G"
-                            android:translateX="185.5"
-                            android:translateY="521">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_8_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M83.5 -4 C83.5,-4 83.5,4 83.5,4 C83.5,6.21 81.71,8 79.5,8 C79.5,8 -79.5,8 -79.5,8 C-81.71,8 -83.5,6.21 -83.5,4 C-83.5,4 -83.5,-4 -83.5,-4 C-83.5,-6.21 -81.71,-8 -79.5,-8 C-79.5,-8 79.5,-8 79.5,-8 C81.71,-8 83.5,-6.21 83.5,-4c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_7_G"
-                            android:translateX="54"
-                            android:translateY="597">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_7_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#9aa0a6"
-                                android:fillType="nonZero"
-                                android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_6_G"
-                            android:translateX="168.5"
-                            android:translateY="588">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_6_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M66.5 -5 C66.5,-5 66.5,5 66.5,5 C66.5,7.21 64.71,9 62.5,9 C62.5,9 -62.5,9 -62.5,9 C-64.71,9 -66.5,7.21 -66.5,5 C-66.5,5 -66.5,-5 -66.5,-5 C-66.5,-7.21 -64.71,-9 -62.5,-9 C-62.5,-9 62.5,-9 62.5,-9 C64.71,-9 66.5,-7.21 66.5,-5c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_5_G"
-                            android:translateX="198.5"
-                            android:translateY="609">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_5_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M96.5 -4 C96.5,-4 96.5,4 96.5,4 C96.5,6.21 94.71,8 92.5,8 C92.5,8 -92.5,8 -92.5,8 C-94.71,8 -96.5,6.21 -96.5,4 C-96.5,4 -96.5,-4 -96.5,-4 C-96.5,-6.21 -94.71,-8 -92.5,-8 C-92.5,-8 92.5,-8 92.5,-8 C94.71,-8 96.5,-6.21 96.5,-4c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_4_G"
-                            android:translateX="54"
-                            android:translateY="685">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_4_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#9aa0a6"
-                                android:fillType="nonZero"
-                                android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_3_G"
-                            android:translateX="162.5"
-                            android:translateY="676">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_3_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M60.5 -5 C60.5,-5 60.5,5 60.5,5 C60.5,7.21 58.71,9 56.5,9 C56.5,9 -56.5,9 -56.5,9 C-58.71,9 -60.5,7.21 -60.5,5 C-60.5,5 -60.5,-5 -60.5,-5 C-60.5,-7.21 -58.71,-9 -56.5,-9 C-56.5,-9 56.5,-9 56.5,-9 C58.71,-9 60.5,-7.21 60.5,-5c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_2_G"
-                            android:translateX="174"
-                            android:translateY="697">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_2_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M72 -4 C72,-4 72,4 72,4 C72,6.21 70.21,8 68,8 C68,8 -68,8 -68,8 C-70.21,8 -72,6.21 -72,4 C-72,4 -72,-4 -72,-4 C-72,-6.21 -70.21,-8 -68,-8 C-68,-8 68,-8 68,-8 C70.21,-8 72,-6.21 72,-4c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_1_G"
-                            android:translateX="313.5"
-                            android:translateY="798">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_1_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M74.5 0 C74.5,0 74.5,0 74.5,0 C74.5,15.45 61.95,28 46.5,28 C46.5,28 -46.5,28 -46.5,28 C-61.95,28 -74.5,15.45 -74.5,0 C-74.5,0 -74.5,0 -74.5,0 C-74.5,-15.45 -61.95,-28 -46.5,-28 C-46.5,-28 46.5,-28 46.5,-28 C61.95,-28 74.5,-15.45 74.5,0c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_0_G"
-                            android:translateX="205.5"
-                            android:translateY="61">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_0_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#f8f9fa"
-                                android:fillType="nonZero"
-                                android:pathData=" M171.5 -14 C171.5,-14 171.5,14 171.5,14 C171.5,16.21 169.71,18 167.5,18 C167.5,18 -167.5,18 -167.5,18 C-169.71,18 -171.5,16.21 -171.5,14 C-171.5,14 -171.5,-14 -171.5,-14 C-171.5,-16.21 -169.71,-18 -167.5,-18 C-167.5,-18 167.5,-18 167.5,-18 C169.71,-18 171.5,-16.21 171.5,-14c " />
-                        </group>
-                    </group>
-                </group>
-            </group>
-            <group android:name="time_group" />
-        </vector>
-    </aapt:attr>
-</animated-vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/mock_webpage_dark_mode.xml b/quickstep/res/drawable/mock_webpage_dark_mode.xml
deleted file mode 100644
index 93b22b7..0000000
--- a/quickstep/res/drawable/mock_webpage_dark_mode.xml
+++ /dev/null
@@ -1,251 +0,0 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:aapt="http://schemas.android.com/aapt">
-    <target android:name="time_group">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="83"
-                    android:propertyName="translateX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <aapt:attr name="android:drawable">
-        <vector
-            android:width="412dp"
-            android:height="892dp"
-            android:viewportHeight="892"
-            android:viewportWidth="412">
-            <group android:name="_R_G">
-                <group android:name="_R_G_L_0_G">
-                    <group android:name="_R_G_L_0_G_L_3_G">
-                        <group
-                            android:name="_R_G_L_0_G_L_3_G_L_11_G"
-                            android:scaleX="0.87473"
-                            android:scaleY="0.98643"
-                            android:translateX="206"
-                            android:translateY="472.769">
-                            <path
-                                android:name="_R_G_L_0_G_L_3_G_L_11_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#dadce0"
-                                android:fillType="nonZero"
-                                android:pathData=" M235.5 -407 C235.5,-407 235.5,407 235.5,407 C235.5,416.93 227.43,425 217.5,425 C217.5,425 -217.5,425 -217.5,425 C-227.43,425 -235.5,416.93 -235.5,407 C-235.5,407 -235.5,-407 -235.5,-407 C-235.5,-416.93 -227.43,-425 -217.5,-425 C-217.5,-425 217.5,-425 217.5,-425 C227.43,-425 235.5,-416.93 235.5,-407c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_3_G_L_10_G"
-                            android:translateX="182.5"
-                            android:translateY="831">
-                            <path
-                                android:name="_R_G_L_0_G_L_3_G_L_10_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M158.5 -3 C158.5,-3 158.5,3 158.5,3 C158.5,7.42 154.92,11 150.5,11 C150.5,11 -150.5,11 -150.5,11 C-154.92,11 -158.5,7.42 -158.5,3 C-158.5,3 -158.5,-3 -158.5,-3 C-158.5,-7.42 -154.92,-11 -150.5,-11 C-150.5,-11 150.5,-11 150.5,-11 C154.92,-11 158.5,-7.42 158.5,-3c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_3_G_L_9_G"
-                            android:translateX="186"
-                            android:translateY="801">
-                            <path
-                                android:name="_R_G_L_0_G_L_3_G_L_9_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M162 -3 C162,-3 162,3 162,3 C162,7.42 158.42,11 154,11 C154,11 -154,11 -154,11 C-158.42,11 -162,7.42 -162,3 C-162,3 -162,-3 -162,-3 C-162,-7.42 -158.42,-11 -154,-11 C-154,-11 154,-11 154,-11 C158.42,-11 162,-7.42 162,-3c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_3_G_L_8_G"
-                            android:translateX="119"
-                            android:translateY="755">
-                            <path
-                                android:name="_R_G_L_0_G_L_3_G_L_8_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M95 -3 C95,-3 95,3 95,3 C95,7.42 91.42,11 87,11 C87,11 -87,11 -87,11 C-91.42,11 -95,7.42 -95,3 C-95,3 -95,-3 -95,-3 C-95,-7.42 -91.42,-11 -87,-11 C-87,-11 87,-11 87,-11 C91.42,-11 95,-7.42 95,-3c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_3_G_L_7_G"
-                            android:translateX="182.5"
-                            android:translateY="725">
-                            <path
-                                android:name="_R_G_L_0_G_L_3_G_L_7_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M158.5 -3 C158.5,-3 158.5,3 158.5,3 C158.5,7.42 154.92,11 150.5,11 C150.5,11 -150.5,11 -150.5,11 C-154.92,11 -158.5,7.42 -158.5,3 C-158.5,3 -158.5,-3 -158.5,-3 C-158.5,-7.42 -154.92,-11 -150.5,-11 C-150.5,-11 150.5,-11 150.5,-11 C154.92,-11 158.5,-7.42 158.5,-3c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_3_G_L_6_G"
-                            android:translateX="197.5"
-                            android:translateY="695">
-                            <path
-                                android:name="_R_G_L_0_G_L_3_G_L_6_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M173.5 -3 C173.5,-3 173.5,3 173.5,3 C173.5,7.42 169.92,11 165.5,11 C165.5,11 -165.5,11 -165.5,11 C-169.92,11 -173.5,7.42 -173.5,3 C-173.5,3 -173.5,-3 -173.5,-3 C-173.5,-7.42 -169.92,-11 -165.5,-11 C-165.5,-11 165.5,-11 165.5,-11 C169.92,-11 173.5,-7.42 173.5,-3c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_3_G_L_5_G"
-                            android:translateX="192"
-                            android:translateY="665">
-                            <path
-                                android:name="_R_G_L_0_G_L_3_G_L_5_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M168 -3 C168,-3 168,3 168,3 C168,7.42 164.42,11 160,11 C160,11 -160,11 -160,11 C-164.42,11 -168,7.42 -168,3 C-168,3 -168,-3 -168,-3 C-168,-7.42 -164.42,-11 -160,-11 C-160,-11 160,-11 160,-11 C164.42,-11 168,-7.42 168,-3c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_3_G_L_4_G"
-                            android:translateX="105.5"
-                            android:translateY="360">
-                            <path
-                                android:name="_R_G_L_0_G_L_3_G_L_4_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M23.5 -2 C23.5,-2 23.5,2 23.5,2 C23.5,4.21 21.71,6 19.5,6 C19.5,6 -19.5,6 -19.5,6 C-21.71,6 -23.5,4.21 -23.5,2 C-23.5,2 -23.5,-2 -23.5,-2 C-23.5,-4.21 -21.71,-6 -19.5,-6 C-19.5,-6 19.5,-6 19.5,-6 C21.71,-6 23.5,-4.21 23.5,-2c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_3_G_L_3_G"
-                            android:translateX="47.5"
-                            android:translateY="360">
-                            <path
-                                android:name="_R_G_L_0_G_L_3_G_L_3_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M23.5 -2 C23.5,-2 23.5,2 23.5,2 C23.5,4.21 21.71,6 19.5,6 C19.5,6 -19.5,6 -19.5,6 C-21.71,6 -23.5,4.21 -23.5,2 C-23.5,2 -23.5,-2 -23.5,-2 C-23.5,-4.21 -21.71,-6 -19.5,-6 C-19.5,-6 19.5,-6 19.5,-6 C21.71,-6 23.5,-4.21 23.5,-2c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_3_G_L_2_G"
-                            android:translateX="142.5"
-                            android:translateY="328">
-                            <path
-                                android:name="_R_G_L_0_G_L_3_G_L_2_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M118.5 -10 C118.5,-10 118.5,10 118.5,10 C118.5,14.42 114.92,18 110.5,18 C110.5,18 -110.5,18 -110.5,18 C-114.92,18 -118.5,14.42 -118.5,10 C-118.5,10 -118.5,-10 -118.5,-10 C-118.5,-14.42 -114.92,-18 -110.5,-18 C-110.5,-18 110.5,-18 110.5,-18 C114.92,-18 118.5,-14.42 118.5,-10c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_3_G_L_1_G"
-                            android:translateX="186"
-                            android:translateY="284">
-                            <path
-                                android:name="_R_G_L_0_G_L_3_G_L_1_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M162 -10 C162,-10 162,10 162,10 C162,14.42 158.42,18 154,18 C154,18 -154,18 -154,18 C-158.42,18 -162,14.42 -162,10 C-162,10 -162,-10 -162,-10 C-162,-14.42 -158.42,-18 -154,-18 C-154,-18 154,-18 154,-18 C158.42,-18 162,-14.42 162,-10c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_3_G_L_0_G"
-                            android:translateX="155"
-                            android:translateY="240">
-                            <path
-                                android:name="_R_G_L_0_G_L_3_G_L_0_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M131 -10 C131,-10 131,10 131,10 C131,14.42 127.42,18 123,18 C123,18 -123,18 -123,18 C-127.42,18 -131,14.42 -131,10 C-131,10 -131,-10 -131,-10 C-131,-14.42 -127.42,-18 -123,-18 C-123,-18 123,-18 123,-18 C127.42,-18 131,-14.42 131,-10c " />
-                        </group>
-                    </group>
-                    <group
-                        android:name="_R_G_L_0_G_L_2_G"
-                        android:translateX="24"
-                        android:translateY="390">
-                        <group
-                            android:name="_R_G_L_0_G_L_2_G_L_0_G"
-                            android:translateX="182"
-                            android:translateY="120">
-                            <path
-                                android:name="_R_G_L_0_G_L_2_G_L_0_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M182 -98 C182,-98 182,98 182,98 C182,110.14 172.14,120 160,120 C160,120 -160,120 -160,120 C-172.14,120 -182,110.14 -182,98 C-182,98 -182,-98 -182,-98 C-182,-110.14 -172.14,-120 -160,-120 C-160,-120 160,-120 160,-120 C172.14,-120 182,-110.14 182,-98c " />
-                        </group>
-                    </group>
-                    <group android:name="_R_G_L_0_G_L_1_G">
-                        <group
-                            android:name="_R_G_L_0_G_L_1_G_L_2_G"
-                            android:translateX="206"
-                            android:translateY="145">
-                            <path
-                                android:name="_R_G_L_0_G_L_1_G_L_2_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#e8eaed"
-                                android:fillType="nonZero"
-                                android:pathData=" M206 -95.63 C206,-95.63 206,42.37 206,42.37 C206,43.47 205.1,44.37 204,44.37 C204,44.37 -204,44.37 -204,44.37 C-205.1,44.37 -206,43.47 -206,42.37 C-206,42.37 -206,-95.63 -206,-95.63 C-206,-96.73 -205.1,-97.63 -204,-97.63 C-204,-97.63 204,-97.63 204,-97.63 C205.1,-97.63 206,-96.73 206,-95.63c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_1_G_L_1_G"
-                            android:translateX="206"
-                            android:translateY="145">
-                            <path
-                                android:name="_R_G_L_0_G_L_1_G_L_1_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#80868b"
-                                android:fillType="nonZero"
-                                android:pathData=" M109 -14 C109,-14 109,14 109,14 C109,15.1 108.1,16 107,16 C107,16 -107,16 -107,16 C-108.1,16 -109,15.1 -109,14 C-109,14 -109,-14 -109,-14 C-109,-15.1 -108.1,-16 -107,-16 C-107,-16 107,-16 107,-16 C108.1,-16 109,-15.1 109,-14c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_1_G_L_0_G"
-                            android:translateX="46"
-                            android:translateY="145">
-                            <path
-                                android:name="_R_G_L_0_G_L_1_G_L_0_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#80868b"
-                                android:fillType="nonZero"
-                                android:pathData=" M22 -14 C22,-14 22,14 22,14 C22,18.42 18.42,22 14,22 C14,22 -14,22 -14,22 C-18.42,22 -22,18.42 -22,14 C-22,14 -22,-14 -22,-14 C-22,-18.42 -18.42,-22 -14,-22 C-14,-22 14,-22 14,-22 C18.42,-22 22,-18.42 22,-14c " />
-                        </group>
-                    </group>
-                    <group android:name="_R_G_L_0_G_L_0_G">
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_2_G"
-                            android:translateX="206"
-                            android:translateY="51">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_2_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#202124"
-                                android:fillType="nonZero"
-                                android:pathData=" M206 -0.27 C206,-0.27 206,49.73 206,49.73 C206,49.73 -206,49.73 -206,49.73 C-206,49.73 -206,-0.27 -206,-0.27 C-206,-0.27 206,-0.27 206,-0.27c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_1_G"
-                            android:translateX="206"
-                            android:translateY="50.5">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_1_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#202124"
-                                android:fillType="nonZero"
-                                android:pathData=" M206 -32.5 C206,-32.5 206,32.5 206,32.5 C206,42.43 197.93,50.5 188,50.5 C188,50.5 -188,50.5 -188,50.5 C-197.93,50.5 -206,42.43 -206,32.5 C-206,32.5 -206,-32.5 -206,-32.5 C-206,-42.43 -197.93,-50.5 -188,-50.5 C-188,-50.5 188,-50.5 188,-50.5 C197.93,-50.5 206,-42.43 206,-32.5c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_0_G_L_0_G"
-                            android:translateX="206"
-                            android:translateY="66.5">
-                            <path
-                                android:name="_R_G_L_0_G_L_0_G_L_0_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#3c4043"
-                                android:fillType="nonZero"
-                                android:pathData=" M190 0 C190,0 190,0 190,0 C190,10.21 181.71,18.5 171.5,18.5 C171.5,18.5 -171.5,18.5 -171.5,18.5 C-181.71,18.5 -190,10.21 -190,0 C-190,0 -190,0 -190,0 C-190,-10.21 -181.71,-18.5 -171.5,-18.5 C-171.5,-18.5 171.5,-18.5 171.5,-18.5 C181.71,-18.5 190,-10.21 190,0c " />
-                        </group>
-                    </group>
-                </group>
-            </group>
-            <group android:name="time_group" />
-        </vector>
-    </aapt:attr>
-</animated-vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/mock_webpage_light_mode.xml b/quickstep/res/drawable/mock_webpage_light_mode.xml
deleted file mode 100644
index 98abb92..0000000
--- a/quickstep/res/drawable/mock_webpage_light_mode.xml
+++ /dev/null
@@ -1,263 +0,0 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:aapt="http://schemas.android.com/aapt">
-    <target android:name="time_group">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="83"
-                    android:propertyName="translateX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <aapt:attr name="android:drawable">
-        <vector
-            android:width="412dp"
-            android:height="892dp"
-            android:viewportHeight="892"
-            android:viewportWidth="412">
-            <group android:name="_R_G">
-                <group android:name="_R_G_L_0_G">
-                    <group android:name="_R_G_L_0_G_L_4_G">
-                        <group
-                            android:name="_R_G_L_0_G_L_4_G_L_11_G"
-                            android:scaleX="0.87473"
-                            android:scaleY="0.98643"
-                            android:translateX="206"
-                            android:translateY="472.769">
-                            <path
-                                android:name="_R_G_L_0_G_L_4_G_L_11_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#dadce0"
-                                android:fillType="nonZero"
-                                android:pathData=" M235.5 -407 C235.5,-407 235.5,407 235.5,407 C235.5,416.93 227.43,425 217.5,425 C217.5,425 -217.5,425 -217.5,425 C-227.43,425 -235.5,416.93 -235.5,407 C-235.5,407 -235.5,-407 -235.5,-407 C-235.5,-416.93 -227.43,-425 -217.5,-425 C-217.5,-425 217.5,-425 217.5,-425 C227.43,-425 235.5,-416.93 235.5,-407c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_4_G_L_10_G"
-                            android:translateX="182.5"
-                            android:translateY="831">
-                            <path
-                                android:name="_R_G_L_0_G_L_4_G_L_10_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M158.5 -3 C158.5,-3 158.5,3 158.5,3 C158.5,7.42 154.92,11 150.5,11 C150.5,11 -150.5,11 -150.5,11 C-154.92,11 -158.5,7.42 -158.5,3 C-158.5,3 -158.5,-3 -158.5,-3 C-158.5,-7.42 -154.92,-11 -150.5,-11 C-150.5,-11 150.5,-11 150.5,-11 C154.92,-11 158.5,-7.42 158.5,-3c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_4_G_L_9_G"
-                            android:translateX="186"
-                            android:translateY="801">
-                            <path
-                                android:name="_R_G_L_0_G_L_4_G_L_9_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M162 -3 C162,-3 162,3 162,3 C162,7.42 158.42,11 154,11 C154,11 -154,11 -154,11 C-158.42,11 -162,7.42 -162,3 C-162,3 -162,-3 -162,-3 C-162,-7.42 -158.42,-11 -154,-11 C-154,-11 154,-11 154,-11 C158.42,-11 162,-7.42 162,-3c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_4_G_L_8_G"
-                            android:translateX="119"
-                            android:translateY="755">
-                            <path
-                                android:name="_R_G_L_0_G_L_4_G_L_8_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M95 -3 C95,-3 95,3 95,3 C95,7.42 91.42,11 87,11 C87,11 -87,11 -87,11 C-91.42,11 -95,7.42 -95,3 C-95,3 -95,-3 -95,-3 C-95,-7.42 -91.42,-11 -87,-11 C-87,-11 87,-11 87,-11 C91.42,-11 95,-7.42 95,-3c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_4_G_L_7_G"
-                            android:translateX="182.5"
-                            android:translateY="725">
-                            <path
-                                android:name="_R_G_L_0_G_L_4_G_L_7_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M158.5 -3 C158.5,-3 158.5,3 158.5,3 C158.5,7.42 154.92,11 150.5,11 C150.5,11 -150.5,11 -150.5,11 C-154.92,11 -158.5,7.42 -158.5,3 C-158.5,3 -158.5,-3 -158.5,-3 C-158.5,-7.42 -154.92,-11 -150.5,-11 C-150.5,-11 150.5,-11 150.5,-11 C154.92,-11 158.5,-7.42 158.5,-3c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_4_G_L_6_G"
-                            android:translateX="197.5"
-                            android:translateY="695">
-                            <path
-                                android:name="_R_G_L_0_G_L_4_G_L_6_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M173.5 -3 C173.5,-3 173.5,3 173.5,3 C173.5,7.42 169.92,11 165.5,11 C165.5,11 -165.5,11 -165.5,11 C-169.92,11 -173.5,7.42 -173.5,3 C-173.5,3 -173.5,-3 -173.5,-3 C-173.5,-7.42 -169.92,-11 -165.5,-11 C-165.5,-11 165.5,-11 165.5,-11 C169.92,-11 173.5,-7.42 173.5,-3c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_4_G_L_5_G"
-                            android:translateX="192"
-                            android:translateY="665">
-                            <path
-                                android:name="_R_G_L_0_G_L_4_G_L_5_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M168 -3 C168,-3 168,3 168,3 C168,7.42 164.42,11 160,11 C160,11 -160,11 -160,11 C-164.42,11 -168,7.42 -168,3 C-168,3 -168,-3 -168,-3 C-168,-7.42 -164.42,-11 -160,-11 C-160,-11 160,-11 160,-11 C164.42,-11 168,-7.42 168,-3c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_4_G_L_4_G"
-                            android:translateX="105.5"
-                            android:translateY="360">
-                            <path
-                                android:name="_R_G_L_0_G_L_4_G_L_4_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M23.5 -2 C23.5,-2 23.5,2 23.5,2 C23.5,4.21 21.71,6 19.5,6 C19.5,6 -19.5,6 -19.5,6 C-21.71,6 -23.5,4.21 -23.5,2 C-23.5,2 -23.5,-2 -23.5,-2 C-23.5,-4.21 -21.71,-6 -19.5,-6 C-19.5,-6 19.5,-6 19.5,-6 C21.71,-6 23.5,-4.21 23.5,-2c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_4_G_L_3_G"
-                            android:translateX="47.5"
-                            android:translateY="360">
-                            <path
-                                android:name="_R_G_L_0_G_L_4_G_L_3_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M23.5 -2 C23.5,-2 23.5,2 23.5,2 C23.5,4.21 21.71,6 19.5,6 C19.5,6 -19.5,6 -19.5,6 C-21.71,6 -23.5,4.21 -23.5,2 C-23.5,2 -23.5,-2 -23.5,-2 C-23.5,-4.21 -21.71,-6 -19.5,-6 C-19.5,-6 19.5,-6 19.5,-6 C21.71,-6 23.5,-4.21 23.5,-2c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_4_G_L_2_G"
-                            android:translateX="142.5"
-                            android:translateY="328">
-                            <path
-                                android:name="_R_G_L_0_G_L_4_G_L_2_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M118.5 -10 C118.5,-10 118.5,10 118.5,10 C118.5,14.42 114.92,18 110.5,18 C110.5,18 -110.5,18 -110.5,18 C-114.92,18 -118.5,14.42 -118.5,10 C-118.5,10 -118.5,-10 -118.5,-10 C-118.5,-14.42 -114.92,-18 -110.5,-18 C-110.5,-18 110.5,-18 110.5,-18 C114.92,-18 118.5,-14.42 118.5,-10c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_4_G_L_1_G"
-                            android:translateX="186"
-                            android:translateY="284">
-                            <path
-                                android:name="_R_G_L_0_G_L_4_G_L_1_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M162 -10 C162,-10 162,10 162,10 C162,14.42 158.42,18 154,18 C154,18 -154,18 -154,18 C-158.42,18 -162,14.42 -162,10 C-162,10 -162,-10 -162,-10 C-162,-14.42 -158.42,-18 -154,-18 C-154,-18 154,-18 154,-18 C158.42,-18 162,-14.42 162,-10c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_4_G_L_0_G"
-                            android:translateX="155"
-                            android:translateY="240">
-                            <path
-                                android:name="_R_G_L_0_G_L_4_G_L_0_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M131 -10 C131,-10 131,10 131,10 C131,14.42 127.42,18 123,18 C123,18 -123,18 -123,18 C-127.42,18 -131,14.42 -131,10 C-131,10 -131,-10 -131,-10 C-131,-14.42 -127.42,-18 -123,-18 C-123,-18 123,-18 123,-18 C127.42,-18 131,-14.42 131,-10c " />
-                        </group>
-                    </group>
-                    <group
-                        android:name="_R_G_L_0_G_L_3_G"
-                        android:translateX="24"
-                        android:translateY="390">
-                        <group
-                            android:name="_R_G_L_0_G_L_3_G_L_0_G"
-                            android:translateX="182"
-                            android:translateY="120">
-                            <path
-                                android:name="_R_G_L_0_G_L_3_G_L_0_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#bdc1c6"
-                                android:fillType="nonZero"
-                                android:pathData=" M182 -98 C182,-98 182,98 182,98 C182,110.14 172.14,120 160,120 C160,120 -160,120 -160,120 C-172.14,120 -182,110.14 -182,98 C-182,98 -182,-98 -182,-98 C-182,-110.14 -172.14,-120 -160,-120 C-160,-120 160,-120 160,-120 C172.14,-120 182,-110.14 182,-98c " />
-                        </group>
-                    </group>
-                    <group android:name="_R_G_L_0_G_L_2_G">
-                        <group
-                            android:name="_R_G_L_0_G_L_2_G_L_2_G"
-                            android:translateX="206"
-                            android:translateY="145">
-                            <path
-                                android:name="_R_G_L_0_G_L_2_G_L_2_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#e8eaed"
-                                android:fillType="nonZero"
-                                android:pathData=" M206 -95.63 C206,-95.63 206,42.37 206,42.37 C206,43.47 205.1,44.37 204,44.37 C204,44.37 -204,44.37 -204,44.37 C-205.1,44.37 -206,43.47 -206,42.37 C-206,42.37 -206,-95.63 -206,-95.63 C-206,-96.73 -205.1,-97.63 -204,-97.63 C-204,-97.63 204,-97.63 204,-97.63 C205.1,-97.63 206,-96.73 206,-95.63c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_2_G_L_1_G"
-                            android:translateX="206"
-                            android:translateY="145">
-                            <path
-                                android:name="_R_G_L_0_G_L_2_G_L_1_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#80868b"
-                                android:fillType="nonZero"
-                                android:pathData=" M109 -14 C109,-14 109,14 109,14 C109,15.1 108.1,16 107,16 C107,16 -107,16 -107,16 C-108.1,16 -109,15.1 -109,14 C-109,14 -109,-14 -109,-14 C-109,-15.1 -108.1,-16 -107,-16 C-107,-16 107,-16 107,-16 C108.1,-16 109,-15.1 109,-14c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_2_G_L_0_G"
-                            android:translateX="46"
-                            android:translateY="145">
-                            <path
-                                android:name="_R_G_L_0_G_L_2_G_L_0_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#80868b"
-                                android:fillType="nonZero"
-                                android:pathData=" M22 -14 C22,-14 22,14 22,14 C22,18.42 18.42,22 14,22 C14,22 -14,22 -14,22 C-18.42,22 -22,18.42 -22,14 C-22,14 -22,-14 -22,-14 C-22,-18.42 -18.42,-22 -14,-22 C-14,-22 14,-22 14,-22 C18.42,-22 22,-18.42 22,-14c " />
-                        </group>
-                    </group>
-                    <group android:name="_R_G_L_0_G_L_1_G">
-                        <group
-                            android:name="_R_G_L_0_G_L_1_G_L_2_G"
-                            android:translateX="206"
-                            android:translateY="51">
-                            <path
-                                android:name="_R_G_L_0_G_L_1_G_L_2_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#6e7175"
-                                android:fillType="nonZero"
-                                android:pathData=" M206 -0.27 C206,-0.27 206,49.73 206,49.73 C206,49.73 -206,49.73 -206,49.73 C-206,49.73 -206,-0.27 -206,-0.27 C-206,-0.27 206,-0.27 206,-0.27c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_1_G_L_1_G"
-                            android:translateX="206"
-                            android:translateY="50.5">
-                            <path
-                                android:name="_R_G_L_0_G_L_1_G_L_1_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#6e7175"
-                                android:fillType="nonZero"
-                                android:pathData=" M206 -32.5 C206,-32.5 206,32.5 206,32.5 C206,42.43 197.93,50.5 188,50.5 C188,50.5 -188,50.5 -188,50.5 C-197.93,50.5 -206,42.43 -206,32.5 C-206,32.5 -206,-32.5 -206,-32.5 C-206,-42.43 -197.93,-50.5 -188,-50.5 C-188,-50.5 188,-50.5 188,-50.5 C197.93,-50.5 206,-42.43 206,-32.5c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_L_1_G_L_0_G"
-                            android:translateX="206"
-                            android:translateY="66.5">
-                            <path
-                                android:name="_R_G_L_0_G_L_1_G_L_0_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#9a9a9a"
-                                android:fillType="nonZero"
-                                android:pathData=" M190 0 C190,0 190,0 190,0 C190,10.21 181.71,18.5 171.5,18.5 C171.5,18.5 -171.5,18.5 -171.5,18.5 C-181.71,18.5 -190,10.21 -190,0 C-190,0 -190,0 -190,0 C-190,-10.21 -181.71,-18.5 -171.5,-18.5 C-171.5,-18.5 171.5,-18.5 171.5,-18.5 C181.71,-18.5 190,-10.21 190,0c " />
-                        </group>
-                    </group>
-                    <group
-                        android:name="_R_G_L_0_G_L_0_G"
-                        android:scaleY="0"
-                        android:translateX="206"
-                        android:translateY="446">
-                        <path
-                            android:name="_R_G_L_0_G_L_0_G_D_0_P_0"
-                            android:fillAlpha="1"
-                            android:fillColor="#bac4d6"
-                            android:fillType="nonZero"
-                            android:pathData=" M206.06 -430.06 C206.06,-430.06 206,431 206,431 C206,446 189.75,446 189.79,446 C189.79,446 -189.98,446 -189.98,446 C-189.94,446 -206,446 -206,431 C-206,431 -206,-430 -206,-430 C-206,-446 -189.97,-446 -190.01,-446 C-190.01,-446 188.98,-446.06 188.98,-446.06 C188.94,-446.06 206,-446 206.06,-430.06c " />
-                    </group>
-                </group>
-            </group>
-            <group android:name="time_group" />
-        </vector>
-    </aapt:attr>
-</animated-vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/taskbar_edu_splitscreen.png b/quickstep/res/drawable/taskbar_edu_splitscreen.png
new file mode 100644
index 0000000..f9d2a63
--- /dev/null
+++ b/quickstep/res/drawable/taskbar_edu_splitscreen.png
Binary files differ
diff --git a/quickstep/res/drawable/taskbar_edu_stashing.png b/quickstep/res/drawable/taskbar_edu_stashing.png
new file mode 100644
index 0000000..f9d2a63
--- /dev/null
+++ b/quickstep/res/drawable/taskbar_edu_stashing.png
Binary files differ
diff --git a/quickstep/res/drawable/taskbar_edu_switch_apps.png b/quickstep/res/drawable/taskbar_edu_switch_apps.png
new file mode 100644
index 0000000..f9d2a63
--- /dev/null
+++ b/quickstep/res/drawable/taskbar_edu_switch_apps.png
Binary files differ
diff --git a/quickstep/res/layout/activity_allset.xml b/quickstep/res/layout/activity_allset.xml
index e79e57e..4fbb8a0 100644
--- a/quickstep/res/layout/activity_allset.xml
+++ b/quickstep/res/layout/activity_allset.xml
@@ -14,7 +14,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<androidx.constraintlayout.widget.ConstraintLayout 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="match_parent"
     android:paddingStart="@dimen/allset_page_margin_horizontal"
@@ -22,60 +23,72 @@
     android:layoutDirection="locale"
     android:textDirection="locale">
 
-    <LinearLayout
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/allset_title_icon_margin_top"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        android:src="@drawable/ic_all_set"/>
+
+    <TextView
+        android:id="@+id/title"
+        style="@style/TextAppearance.GestureTutorial.Feedback.Title"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_alignParentTop="true"
-        android:layout_gravity="start"
+        android:layout_marginTop="@dimen/allset_title_margin_top"
+        app:layout_constraintTop_toBottomOf="@id/icon"
+        app:layout_constraintStart_toStartOf="parent"
         android:gravity="start"
-        android:orientation="vertical">
+        android:text="@string/allset_title"/>
 
-        <ImageView
-            android:id="@+id/icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/allset_title_icon_margin_top"
-            android:src="@drawable/ic_all_set"/>
+    <TextView
+        android:id="@+id/subtitle"
+        style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/allset_subtitle_margin_top"
+        app:layout_constraintTop_toBottomOf="@id/title"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintWidth_max="@dimen/allset_subtitle_width_max"
+        android:gravity="start"
+        android:text="@string/allset_description"/>
 
-        <TextView
-            android:id="@+id/title"
-            style="@style/TextAppearance.GestureTutorial.Feedback.Title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/allset_title_margin_top"
-            android:gravity="start"
-            android:text="@string/allset_title"/>
-
-        <TextView
-            android:id="@+id/subtitle"
-            style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/allset_subtitle_margin_top"
-            android:gravity="start"
-            android:text="@string/allset_description"/>
-    </LinearLayout>
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/navigation_settings_guideline_bottom"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintGuide_percent="0.83" />
 
     <TextView
         android:id="@+id/navigation_settings"
         style="@style/TextAppearance.GestureTutorial.LinkText"
-        android:layout_width="match_parent"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_above="@+id/hint"
-        android:gravity="center"
-        android:layout_marginBottom="72dp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toBottomOf="@id/navigation_settings_guideline_bottom"
         android:minHeight="48dp"
         android:background="?android:attr/selectableItemBackground"
         android:text="@string/allset_navigation_settings" />
 
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/hint_guideline_bottom"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintGuide_percent="0.94" />
+
     <TextView
-        android:id="@id/hint"
+        android:id="@+id/hint"
         style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
         android:textSize="14sp"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/allset_hint_margin_bottom"
-        android:layout_alignParentBottom="true"
-        android:layout_centerHorizontal="true"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toBottomOf="@id/hint_guideline_bottom"
         android:text="@string/allset_hint"/>
-</RelativeLayout>
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/quickstep/res/layout/gesture_tutorial_dialog.xml b/quickstep/res/layout/gesture_tutorial_dialog.xml
index 59bf7b9..db6ec85 100644
--- a/quickstep/res/layout/gesture_tutorial_dialog.xml
+++ b/quickstep/res/layout/gesture_tutorial_dialog.xml
@@ -1,4 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
 <androidx.constraintlayout.widget.ConstraintLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
diff --git a/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation.xml b/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation.xml
new file mode 100644
index 0000000..34bd4e2
--- /dev/null
+++ b/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation.xml
@@ -0,0 +1,239 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout
+    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="match_parent">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/top_bar"
+        android:layout_width="match_parent"
+        android:layout_height="101dp"
+        android:background="@color/mock_conversation_top_bar"
+
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="43dp"
+            android:layout_marginBottom="22dp"
+            android:layout_marginStart="126dp"
+            android:layout_marginEnd="548dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_conversation_top_bar_item"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="43dp"
+            android:layout_marginBottom="22dp"
+            android:layout_marginStart="300dp"
+            android:layout_marginEnd="16dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_conversation_top_bar_item"
+            app:layout_constraintDimensionRatio="1:1"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toStartOf="@id/top_bar_button"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/top_bar_button"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="43dp"
+            android:layout_marginBottom="22dp"
+            android:layout_marginStart="300dp"
+            android:layout_marginEnd="126dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_conversation_top_bar_item"
+            app:layout_constraintDimensionRatio="1:1"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:background="@color/mock_conversation_background"
+        android:paddingBottom="80dp"
+
+        app:layout_constraintTop_toBottomOf="@id/top_bar"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:paddingBottom="@dimen/gesture_tutorial_message_input_margin_top"
+
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toTopOf="@id/message_bar"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent">
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/message_1"
+                android:layout_width="0dp"
+                android:layout_height="112dp"
+                android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
+                android:layout_marginStart="445dp"
+                android:layout_marginEnd="@dimen/gesture_tutorial_foldable_message_padding_start_end"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="18dp"
+                app:cardBackgroundColor="@color/mock_conversation_sent_message"
+                app:layout_constraintBottom_toTopOf="@id/reply_icon_1"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/reply_icon_1"
+                android:layout_width="@dimen/gesture_tutorial_message_icon_size"
+                android:layout_height="@dimen/gesture_tutorial_message_icon_size"
+                android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
+                android:layout_marginStart="@dimen/gesture_tutorial_foldable_message_padding_start_end"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_message_icon_corner_radius"
+                app:cardBackgroundColor="@color/mock_conversation_profile_icon"
+                app:layout_constraintDimensionRatio="1:1"
+                app:layout_constraintBottom_toTopOf="@id/message_2"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:layout_width="0dp"
+                android:layout_height="36dp"
+                android:layout_marginStart="17dp"
+                android:layout_marginEnd="441dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="18dp"
+                app:cardBackgroundColor="@color/mock_conversation_received_message"
+                app:layout_constraintTop_toTopOf="@id/reply_icon_1"
+                app:layout_constraintBottom_toBottomOf="@id/reply_icon_1"
+                app:layout_constraintStart_toEndOf="@id/reply_icon_1"
+                app:layout_constraintEnd_toEndOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/message_2"
+                android:layout_width="0dp"
+                android:layout_height="36dp"
+                android:layout_marginBottom="@dimen/gesture_tutorial_message_small_margin_bottom"
+                android:layout_marginStart="601dp"
+                android:layout_marginEnd="@dimen/gesture_tutorial_foldable_message_padding_start_end"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="18dp"
+                app:cardBackgroundColor="@color/mock_conversation_sent_message"
+                app:layout_constraintBottom_toTopOf="@id/message_3"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/message_3"
+                android:layout_width="0dp"
+                android:layout_height="74dp"
+                android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
+                android:layout_marginStart="445dp"
+                android:layout_marginEnd="@dimen/gesture_tutorial_foldable_message_padding_start_end"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="18dp"
+                app:cardBackgroundColor="@color/mock_conversation_sent_message"
+                app:layout_constraintBottom_toTopOf="@id/reply_icon_2"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/reply_icon_2"
+                android:layout_width="@dimen/gesture_tutorial_message_icon_size"
+                android:layout_height="@dimen/gesture_tutorial_message_icon_size"
+                android:layout_marginBottom="32dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_foldable_message_padding_start_end"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_message_icon_corner_radius"
+                app:cardBackgroundColor="@color/mock_conversation_profile_icon"
+                app:layout_constraintDimensionRatio="1:1"
+                app:layout_constraintBottom_toTopOf="@id/message_4"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:layout_width="0dp"
+                android:layout_height="36dp"
+                android:layout_marginStart="17dp"
+                android:layout_marginEnd="473dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="18dp"
+                app:cardBackgroundColor="@color/mock_conversation_received_message"
+                app:layout_constraintTop_toTopOf="@id/reply_icon_2"
+                app:layout_constraintBottom_toBottomOf="@id/reply_icon_2"
+                app:layout_constraintStart_toEndOf="@id/reply_icon_2"
+                app:layout_constraintEnd_toEndOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/message_4"
+                android:layout_width="0dp"
+                android:layout_height="74dp"
+                android:layout_marginStart="445dp"
+                android:layout_marginEnd="@dimen/gesture_tutorial_foldable_message_padding_start_end"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="18dp"
+                app:cardBackgroundColor="@color/mock_conversation_sent_message"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"/>
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/message_bar"
+            android:layout_width="0dp"
+            android:layout_height="44dp"
+            android:layout_marginTop="36dp"
+            android:layout_marginBottom="24dp"
+            android:layout_marginStart="134dp"
+            android:layout_marginEnd="126dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="164dp"
+            app:cardBackgroundColor="@color/mock_conversation_message_input"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation_list.xml b/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation_list.xml
new file mode 100644
index 0000000..0309cc3
--- /dev/null
+++ b/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation_list.xml
@@ -0,0 +1,396 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout
+    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="match_parent">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/top_bar"
+        android:layout_width="match_parent"
+        android:layout_height="101dp"
+        android:background="@color/mock_list_top_bar"
+
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="43dp"
+            android:layout_marginBottom="22dp"
+            android:layout_marginStart="126dp"
+            android:layout_marginEnd="126dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_list_top_bar_item"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:background="@color/mock_list_background"
+        android:paddingBottom="80dp"
+
+        app:layout_constraintTop_toBottomOf="@id/top_bar"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:paddingTop="@dimen/gesture_tutorial_conversation_list_padding_top"
+            android:paddingStart="126dp"
+
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toStartOf="@id/mock_button">
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_icon_1"
+                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+                app:cardBackgroundColor="@color/mock_list_profile_icon"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_1"
+                android:layout_width="0dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="270dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintVertical_chainStyle="packed"
+                app:layout_constraintTop_toTopOf="@id/conversation_icon_1"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_1"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toTopOf="@id/conversation_line_2"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_2"
+                android:layout_width="0dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="110dp"
+                android:layout_marginTop="4dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintTop_toBottomOf="@id/conversation_line_1"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_1"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_1"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_icon_2"
+                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_marginTop="32dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+                app:cardBackgroundColor="@color/mock_list_profile_icon"
+                app:layout_constraintTop_toBottomOf="@id/conversation_icon_1"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_3"
+                android:layout_width="0dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="243dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintVertical_chainStyle="packed"
+                app:layout_constraintTop_toTopOf="@id/conversation_icon_2"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_2"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toTopOf="@id/conversation_line_4"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_4"
+                android:layout_width="0dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="154dp"
+                android:layout_marginTop="4dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintTop_toBottomOf="@id/conversation_line_3"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_2"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_2"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_icon_3"
+                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_marginTop="32dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+                app:cardBackgroundColor="@color/mock_list_profile_icon"
+                app:layout_constraintTop_toBottomOf="@id/conversation_icon_2"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_5"
+                android:layout_width="0dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="251dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintVertical_chainStyle="packed"
+                app:layout_constraintTop_toTopOf="@id/conversation_icon_3"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_3"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toTopOf="@id/conversation_line_6"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_6"
+                android:layout_width="0dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="15dp"
+                android:layout_marginTop="4dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintTop_toBottomOf="@id/conversation_line_5"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_3"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_3"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_icon_4"
+                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_marginTop="32dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+                app:cardBackgroundColor="@color/mock_list_profile_icon"
+                app:layout_constraintTop_toBottomOf="@id/conversation_icon_3"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_7"
+                android:layout_width="0dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="227dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintVertical_chainStyle="packed"
+                app:layout_constraintTop_toTopOf="@id/conversation_icon_4"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_4"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toTopOf="@id/conversation_line_8"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_8"
+                android:layout_width="0dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="72dp"
+                android:layout_marginTop="4dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintTop_toBottomOf="@id/conversation_line_7"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_4"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_4"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_icon_5"
+                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_marginTop="32dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+                app:cardBackgroundColor="@color/mock_list_profile_icon"
+                app:layout_constraintTop_toBottomOf="@id/conversation_icon_4"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_9"
+                android:layout_width="0dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="297dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintVertical_chainStyle="packed"
+                app:layout_constraintTop_toTopOf="@id/conversation_icon_5"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_5"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toTopOf="@id/conversation_line_10"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_10"
+                android:layout_width="0dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="111dp"
+                android:layout_marginTop="4dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintTop_toBottomOf="@id/conversation_line_9"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_5"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_5"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_icon_6"
+                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_marginTop="32dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+                app:cardBackgroundColor="@color/mock_list_profile_icon"
+                app:layout_constraintTop_toBottomOf="@id/conversation_icon_5"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_11"
+                android:layout_width="0dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="230dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintVertical_chainStyle="packed"
+                app:layout_constraintTop_toTopOf="@id/conversation_icon_6"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_6"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toTopOf="@id/conversation_line_12"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_12"
+                android:layout_width="0dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="72dp"
+                android:layout_marginTop="4dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintTop_toBottomOf="@id/conversation_line_11"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_6"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_6"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_icon_7"
+                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_marginTop="32dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+                app:cardBackgroundColor="@color/mock_list_profile_icon"
+                app:layout_constraintTop_toBottomOf="@id/conversation_icon_6"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_13"
+                android:layout_width="0dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="242dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintVertical_chainStyle="packed"
+                app:layout_constraintTop_toTopOf="@id/conversation_icon_7"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_7"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toTopOf="@id/conversation_line_14"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_14"
+                android:layout_width="0dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="219dp"
+                android:layout_marginTop="4dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintTop_toBottomOf="@id/conversation_line_13"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_7"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_7"/>
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_button"
+            android:layout_width="149dp"
+            android:layout_height="56dp"
+            android:layout_marginEnd="126dp"
+            android:layout_marginBottom="24dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="164dp"
+            app:cardBackgroundColor="@color/mock_list_button"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout/gesture_tutorial_foldable_mock_hotseat.xml b/quickstep/res/layout/gesture_tutorial_foldable_mock_hotseat.xml
new file mode 100644
index 0000000..5612666
--- /dev/null
+++ b/quickstep/res/layout/gesture_tutorial_foldable_mock_hotseat.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout
+    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:paddingBottom="32dp"
+    android:paddingStart="170dp"
+    android:paddingEnd="170dp">
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_search_bar"
+        android:layout_width="0dp"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_search_height"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_search_corner_radius"
+        app:cardBackgroundColor="@color/mock_search_bar"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_1"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_marginTop="@dimen/gesture_tutorial_hotseat_icon_search_margin"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_1"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintHorizontal_chainStyle="spread_inside"
+        app:layout_constraintTop_toBottomOf="@id/hotseat_search_bar"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/hotseat_icon_2"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_2"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_marginTop="@dimen/gesture_tutorial_hotseat_icon_search_margin"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_2"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toBottomOf="@id/hotseat_search_bar"
+        app:layout_constraintStart_toEndOf="@id/hotseat_icon_1"
+        app:layout_constraintEnd_toStartOf="@id/hotseat_icon_3"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_3"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_marginTop="@dimen/gesture_tutorial_hotseat_icon_search_margin"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_3"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toBottomOf="@id/hotseat_search_bar"
+        app:layout_constraintStart_toEndOf="@id/hotseat_icon_2"
+        app:layout_constraintEnd_toStartOf="@id/hotseat_icon_4"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_4"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_marginTop="@dimen/gesture_tutorial_hotseat_icon_search_margin"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_1"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toBottomOf="@id/hotseat_search_bar"
+        app:layout_constraintStart_toEndOf="@id/hotseat_icon_3"
+        app:layout_constraintEnd_toStartOf="@id/hotseat_icon_5"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_5"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_marginTop="@dimen/gesture_tutorial_hotseat_icon_search_margin"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_4"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toBottomOf="@id/hotseat_search_bar"
+        app:layout_constraintStart_toEndOf="@id/hotseat_icon_4"
+        app:layout_constraintEnd_toStartOf="@id/hotseat_icon_6"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_6"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_marginTop="@dimen/gesture_tutorial_hotseat_icon_search_margin"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_2"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toBottomOf="@id/hotseat_search_bar"
+        app:layout_constraintStart_toEndOf="@id/hotseat_icon_5"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout/gesture_tutorial_foldable_mock_webpage.xml b/quickstep/res/layout/gesture_tutorial_foldable_mock_webpage.xml
new file mode 100644
index 0000000..67e9b02
--- /dev/null
+++ b/quickstep/res/layout/gesture_tutorial_foldable_mock_webpage.xml
@@ -0,0 +1,275 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout
+    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="match_parent"
+    android:background="@color/mock_webpage_background">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/url_bar"
+        android:layout_width="match_parent"
+        android:layout_height="101dp"
+        android:background="@color/mock_webpage_url_bar"
+
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="48dp"
+            android:layout_marginBottom="16dp"
+            android:layout_marginStart="100dp"
+            android:layout_marginEnd="100dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="100dp"
+            app:cardBackgroundColor="@color/mock_webpage_url_bar_item"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/top_bar"
+        android:layout_width="match_parent"
+        android:layout_height="88dp"
+        android:layout_marginStart="100dp"
+        android:layout_marginEnd="100dp"
+        android:background="@color/mock_webpage_top_bar"
+
+        app:layout_constraintTop_toBottomOf="@id/url_bar"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/top_bar_button"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="22dp"
+            android:layout_marginBottom="22dp"
+            android:layout_marginStart="24dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="8dp"
+            app:cardBackgroundColor="@color/mock_webpage_top_bar_item"
+            app:layout_constraintDimensionRatio="1:1"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="28dp"
+            android:layout_marginBottom="28dp"
+            android:layout_marginStart="97dp"
+            android:layout_marginEnd="325dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="2dp"
+            app:cardBackgroundColor="@color/mock_webpage_top_bar_item"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:background="@color/mock_webpage_background"
+        android:paddingTop="@dimen/gesture_tutorial_webpage_padding_top"
+        android:paddingStart="124dp"
+        android:paddingEnd="100dp"
+
+        app:layout_constraintTop_toBottomOf="@id/top_bar"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_1"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_large_line_height"
+            android:layout_marginEnd="126dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_2"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_large_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+            android:layout_marginEnd="64dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_1"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_3"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_large_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+            android:layout_marginEnd="151dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_2"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_button"
+            android:layout_width="47dp"
+            android:layout_height="12dp"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_3"
+            app:layout_constraintStart_toStartOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="47dp"
+            android:layout_height="12dp"
+            android:background="@color/mock_webpage_page_text"
+            android:layout_marginStart="11dp"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_3"
+            app:layout_constraintStart_toEndOf="@id/mock_button"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_block"
+            android:layout_width="0dp"
+            android:layout_height="240dp"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_large_margin_top"
+            android:layout_marginEnd="24dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_large_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_button"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_4"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_large_margin_top"
+            android:layout_marginEnd="52dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_block"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_5"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+            android:layout_marginEnd="41dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_4"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_6"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+            android:layout_marginEnd="71dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_5"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_7"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+            android:layout_marginEnd="198dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_6"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_8"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_large_margin_top"
+            android:layout_marginEnd="64dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_7"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+            android:layout_marginEnd="71dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_8"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout/gesture_tutorial_fragment.xml b/quickstep/res/layout/gesture_tutorial_fragment.xml
index cdda43c..41d0a1d 100644
--- a/quickstep/res/layout/gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/gesture_tutorial_fragment.xml
@@ -25,13 +25,12 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
-        <ImageView
+        <FrameLayout
             android:id="@+id/gesture_tutorial_fake_hotseat_view"
-            android:layout_width="wrap_content"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_centerHorizontal="true"
-            android:layout_alignParentBottom="true"
-            android:layout_marginBottom="70dp"/>
+            android:layout_alignParentBottom="true"/>
 
     </RelativeLayout>
 
@@ -41,15 +40,56 @@
         android:layout_height="20dp"
         android:visibility="invisible" />
 
-    <View
+    <com.android.quickstep.interaction.AnimatedTaskView
         android:id="@+id/gesture_tutorial_fake_previous_task_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:scaleX="0.98"
         android:scaleY="0.98"
-        android:visibility="invisible" />
+        android:visibility="invisible">
 
-    <View
+        <View
+            android:id="@+id/full_task_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="invisible"
+
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/top_task_view"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:visibility="invisible"
+            android:layout_marginBottom="@dimen/gesture_tutorial_multi_row_task_view_spacing"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_small_task_view_corner_radius"
+            app:layout_constraintVertical_chainStyle="spread"
+            app:layout_constraintTop_toTopOf="@id/full_task_view"
+            app:layout_constraintBottom_toTopOf="@id/bottom_task_view"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/bottom_task_view"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:visibility="invisible"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_small_task_view_corner_radius"
+            app:layout_constraintTop_toBottomOf="@id/top_task_view"
+            app:layout_constraintBottom_toBottomOf="@id/full_task_view"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+    </com.android.quickstep.interaction.AnimatedTaskView>
+
+    <FrameLayout
         android:id="@+id/gesture_tutorial_fake_task_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
@@ -62,7 +102,7 @@
         android:background="@drawable/gesture_tutorial_ripple"/>
 
     <ImageView
-        android:id="@+id/gesture_tutorial_feedback_video"
+        android:id="@+id/gesture_tutorial_edge_gesture_video"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_alignParentTop="true"
@@ -73,14 +113,12 @@
         android:visibility="gone"/>
 
     <ImageView
-        android:id="@+id/gesture_tutorial_gesture_video"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_alignParentTop="true"
-        android:layout_alignParentBottom="true"
-        android:layout_alignParentStart="true"
-        android:layout_alignParentEnd="true"
-        android:scaleType="fitXY"
+        android:id="@+id/gesture_tutorial_finger_dot"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/gesture_tutorial_finger_dot"
+        android:layout_centerHorizontal="true"
+        android:layout_centerVertical="true"
         android:visibility="gone"/>
 
     <androidx.constraintlayout.widget.ConstraintLayout
@@ -89,8 +127,6 @@
         android:layout_height="wrap_content"
         android:layout_alignParentTop="true"
         android:layout_centerHorizontal="true"
-        android:layout_marginStart="@dimen/gesture_tutorial_feedback_margin_start_end"
-        android:layout_marginEnd="@dimen/gesture_tutorial_feedback_margin_start_end"
         android:layout_marginTop="24dp"
         android:paddingTop="24dp"
         android:paddingBottom="16dp"
diff --git a/quickstep/res/layout/gesture_tutorial_mock_conversation.xml b/quickstep/res/layout/gesture_tutorial_mock_conversation.xml
new file mode 100644
index 0000000..e8d5d79
--- /dev/null
+++ b/quickstep/res/layout/gesture_tutorial_mock_conversation.xml
@@ -0,0 +1,238 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout
+    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="match_parent">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/top_bar"
+        android:layout_width="match_parent"
+        android:layout_height="101dp"
+        android:background="@color/mock_conversation_top_bar"
+
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="43dp"
+            android:layout_marginBottom="22dp"
+            android:layout_marginStart="34dp"
+            android:layout_marginEnd="211dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_conversation_top_bar_item"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="43dp"
+            android:layout_marginBottom="22dp"
+            android:layout_marginStart="300dp"
+            android:layout_marginEnd="16dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_conversation_top_bar_item"
+            app:layout_constraintDimensionRatio="1:1"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toStartOf="@id/top_bar_button"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/top_bar_button"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="43dp"
+            android:layout_marginBottom="22dp"
+            android:layout_marginStart="300dp"
+            android:layout_marginEnd="24dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_conversation_top_bar_item"
+            app:layout_constraintDimensionRatio="1:1"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:background="@color/mock_conversation_background"
+        android:paddingBottom="66dp"
+
+        app:layout_constraintTop_toBottomOf="@id/top_bar"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:paddingBottom="@dimen/gesture_tutorial_message_input_margin_top"
+
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toTopOf="@id/message_bar"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent">
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/message_1"
+                android:layout_width="0dp"
+                android:layout_height="112dp"
+                android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
+                android:layout_marginStart="124dp"
+                android:layout_marginEnd="@dimen/gesture_tutorial_message_padding_end"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="18dp"
+                app:cardBackgroundColor="@color/mock_conversation_sent_message"
+                app:layout_constraintBottom_toTopOf="@id/reply_icon_1"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/reply_icon_1"
+                android:layout_width="@dimen/gesture_tutorial_message_icon_size"
+                android:layout_height="@dimen/gesture_tutorial_message_icon_size"
+                android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
+                android:layout_marginStart="@dimen/gesture_tutorial_message_padding_start"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_message_icon_corner_radius"
+                app:cardBackgroundColor="@color/mock_conversation_profile_icon"
+                app:layout_constraintDimensionRatio="1:1"
+                app:layout_constraintBottom_toTopOf="@id/message_2"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:layout_width="0dp"
+                android:layout_height="36dp"
+                android:layout_marginStart="17dp"
+                android:layout_marginEnd="112dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="18dp"
+                app:cardBackgroundColor="@color/mock_conversation_received_message"
+                app:layout_constraintTop_toTopOf="@id/reply_icon_1"
+                app:layout_constraintBottom_toBottomOf="@id/reply_icon_1"
+                app:layout_constraintStart_toEndOf="@id/reply_icon_1"
+                app:layout_constraintEnd_toEndOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/message_2"
+                android:layout_width="0dp"
+                android:layout_height="36dp"
+                android:layout_marginBottom="@dimen/gesture_tutorial_message_small_margin_bottom"
+                android:layout_marginStart="280dp"
+                android:layout_marginEnd="@dimen/gesture_tutorial_message_padding_end"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="18dp"
+                app:cardBackgroundColor="@color/mock_conversation_sent_message"
+                app:layout_constraintBottom_toTopOf="@id/message_3"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/message_3"
+                android:layout_width="0dp"
+                android:layout_height="74dp"
+                android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
+                android:layout_marginStart="124dp"
+                android:layout_marginEnd="@dimen/gesture_tutorial_message_padding_end"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="18dp"
+                app:cardBackgroundColor="@color/mock_conversation_sent_message"
+                app:layout_constraintBottom_toTopOf="@id/reply_icon_2"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/reply_icon_2"
+                android:layout_width="@dimen/gesture_tutorial_message_icon_size"
+                android:layout_height="@dimen/gesture_tutorial_message_icon_size"
+                android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
+                android:layout_marginStart="@dimen/gesture_tutorial_message_padding_start"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_message_icon_corner_radius"
+                app:cardBackgroundColor="@color/mock_conversation_profile_icon"
+                app:layout_constraintDimensionRatio="1:1"
+                app:layout_constraintBottom_toTopOf="@id/message_4"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:layout_width="0dp"
+                android:layout_height="36dp"
+                android:layout_marginStart="17dp"
+                android:layout_marginEnd="144dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="18dp"
+                app:cardBackgroundColor="@color/mock_conversation_received_message"
+                app:layout_constraintTop_toTopOf="@id/reply_icon_2"
+                app:layout_constraintBottom_toBottomOf="@id/reply_icon_2"
+                app:layout_constraintStart_toEndOf="@id/reply_icon_2"
+                app:layout_constraintEnd_toEndOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/message_4"
+                android:layout_width="0dp"
+                android:layout_height="74dp"
+                android:layout_marginStart="124dp"
+                android:layout_marginEnd="@dimen/gesture_tutorial_message_padding_end"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="18dp"
+                app:cardBackgroundColor="@color/mock_conversation_sent_message"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"/>
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/message_bar"
+            android:layout_width="0dp"
+            android:layout_height="44dp"
+            android:layout_marginTop="36dp"
+            android:layout_marginStart="34dp"
+            android:layout_marginEnd="24dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="164dp"
+            app:cardBackgroundColor="@color/mock_conversation_message_input"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout/gesture_tutorial_mock_conversation_list.xml b/quickstep/res/layout/gesture_tutorial_mock_conversation_list.xml
new file mode 100644
index 0000000..364ad6d
--- /dev/null
+++ b/quickstep/res/layout/gesture_tutorial_mock_conversation_list.xml
@@ -0,0 +1,394 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout
+    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="match_parent">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/top_bar"
+        android:layout_width="match_parent"
+        android:layout_height="101dp"
+        android:background="@color/mock_list_top_bar"
+
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="43dp"
+            android:layout_marginBottom="22dp"
+            android:layout_marginStart="34dp"
+            android:layout_marginEnd="35dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_list_top_bar_item"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:background="@color/mock_list_background"
+        android:paddingBottom="66dp"
+
+        app:layout_constraintTop_toBottomOf="@id/top_bar"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:paddingTop="@dimen/gesture_tutorial_conversation_list_padding_top"
+            android:paddingStart="26dp"
+            android:paddingBottom="14dp"
+
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toTopOf="@id/mock_button">
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_icon_1"
+                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+                app:cardBackgroundColor="@color/mock_list_profile_icon"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_1"
+                android:layout_width="0dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="217dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintVertical_chainStyle="packed"
+                app:layout_constraintTop_toTopOf="@id/conversation_icon_1"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_1"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toTopOf="@id/conversation_line_2"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_2"
+                android:layout_width="0dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="142dp"
+                android:layout_marginTop="4dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintTop_toBottomOf="@id/conversation_line_1"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_1"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_1"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_icon_2"
+                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_marginTop="32dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+                app:cardBackgroundColor="@color/mock_list_profile_icon"
+                app:layout_constraintTop_toBottomOf="@id/conversation_icon_1"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_3"
+                android:layout_width="0dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="190dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintVertical_chainStyle="packed"
+                app:layout_constraintTop_toTopOf="@id/conversation_icon_2"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_2"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toTopOf="@id/conversation_line_4"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_4"
+                android:layout_width="0dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="171dp"
+                android:layout_marginTop="4dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintTop_toBottomOf="@id/conversation_line_3"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_2"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_2"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_icon_3"
+                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_marginTop="32dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+                app:cardBackgroundColor="@color/mock_list_profile_icon"
+                app:layout_constraintTop_toBottomOf="@id/conversation_icon_2"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_5"
+                android:layout_width="0dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="198dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintVertical_chainStyle="packed"
+                app:layout_constraintTop_toTopOf="@id/conversation_icon_3"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_3"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toTopOf="@id/conversation_line_6"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_6"
+                android:layout_width="0dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="79dp"
+                android:layout_marginTop="4dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintTop_toBottomOf="@id/conversation_line_5"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_3"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_3"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_icon_4"
+                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_marginTop="32dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+                app:cardBackgroundColor="@color/mock_list_profile_icon"
+                app:layout_constraintTop_toBottomOf="@id/conversation_icon_3"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_7"
+                android:layout_width="0dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="174dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintVertical_chainStyle="packed"
+                app:layout_constraintTop_toTopOf="@id/conversation_icon_4"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_4"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toTopOf="@id/conversation_line_8"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_8"
+                android:layout_width="0dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="117dp"
+                android:layout_marginTop="4dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintTop_toBottomOf="@id/conversation_line_7"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_4"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_4"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_icon_5"
+                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_marginTop="32dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+                app:cardBackgroundColor="@color/mock_list_profile_icon"
+                app:layout_constraintTop_toBottomOf="@id/conversation_icon_4"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_9"
+                android:layout_width="0dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="244dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintVertical_chainStyle="packed"
+                app:layout_constraintTop_toTopOf="@id/conversation_icon_5"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_5"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toTopOf="@id/conversation_line_10"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_10"
+                android:layout_width="0dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="143dp"
+                android:layout_marginTop="4dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintTop_toBottomOf="@id/conversation_line_9"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_5"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_5"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_icon_6"
+                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_marginTop="32dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+                app:cardBackgroundColor="@color/mock_list_profile_icon"
+                app:layout_constraintTop_toBottomOf="@id/conversation_icon_5"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_11"
+                android:layout_width="0dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="177dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintVertical_chainStyle="packed"
+                app:layout_constraintTop_toTopOf="@id/conversation_icon_6"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_6"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toTopOf="@id/conversation_line_12"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_12"
+                android:layout_width="0dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="117dp"
+                android:layout_marginTop="4dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintTop_toBottomOf="@id/conversation_line_11"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_6"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_6"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_icon_7"
+                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_marginTop="32dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+                app:cardBackgroundColor="@color/mock_list_profile_icon"
+                app:layout_constraintTop_toBottomOf="@id/conversation_icon_6"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_13"
+                android:layout_width="0dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="189dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintVertical_chainStyle="packed"
+                app:layout_constraintTop_toTopOf="@id/conversation_icon_7"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_7"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toTopOf="@id/conversation_line_14"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/conversation_line_14"
+                android:layout_width="0dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="166dp"
+                android:layout_marginTop="4dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="4dp"
+                app:cardBackgroundColor="@color/mock_list_preview_message"
+                app:layout_constraintTop_toBottomOf="@id/conversation_line_13"
+                app:layout_constraintStart_toEndOf="@id/conversation_icon_7"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_7"/>
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_button"
+            android:layout_width="149dp"
+            android:layout_height="56dp"
+            android:layout_marginEnd="24dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="164dp"
+            app:cardBackgroundColor="@color/mock_list_button"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout/gesture_tutorial_mock_hotseat.xml b/quickstep/res/layout/gesture_tutorial_mock_hotseat.xml
new file mode 100644
index 0000000..b3e86cf
--- /dev/null
+++ b/quickstep/res/layout/gesture_tutorial_mock_hotseat.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout
+    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:paddingBottom="70dp"
+    android:paddingStart="26dp"
+    android:paddingEnd="26dp">
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_1"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_1"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintHorizontal_chainStyle="spread_inside"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/hotseat_icon_2"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_2"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_2"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toEndOf="@id/hotseat_icon_1"
+        app:layout_constraintEnd_toStartOf="@id/hotseat_icon_3"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_3"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_3"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toEndOf="@id/hotseat_icon_2"
+        app:layout_constraintEnd_toStartOf="@id/hotseat_icon_4"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_4"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_4"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toEndOf="@id/hotseat_icon_3"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+    <androidx.cardview.widget.CardView
+        android:layout_width="0dp"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_search_height"
+        android:layout_marginTop="@dimen/gesture_tutorial_hotseat_icon_search_margin"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_search_corner_radius"
+        app:cardBackgroundColor="@color/mock_search_bar"
+        app:layout_constraintTop_toBottomOf="@id/hotseat_icon_1"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout/gesture_tutorial_mock_webpage.xml b/quickstep/res/layout/gesture_tutorial_mock_webpage.xml
new file mode 100644
index 0000000..bb20968
--- /dev/null
+++ b/quickstep/res/layout/gesture_tutorial_mock_webpage.xml
@@ -0,0 +1,271 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout
+    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="match_parent">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/url_bar"
+        android:layout_width="match_parent"
+        android:layout_height="101dp"
+        android:background="@color/mock_webpage_url_bar"
+
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="48dp"
+            android:layout_marginBottom="16dp"
+            android:layout_marginStart="16dp"
+            android:layout_marginEnd="16dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="100dp"
+            app:cardBackgroundColor="@color/mock_webpage_url_bar_item"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/top_bar"
+        android:layout_width="match_parent"
+        android:layout_height="88dp"
+        android:background="@color/mock_webpage_top_bar"
+
+        app:layout_constraintTop_toBottomOf="@id/url_bar"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/top_bar_button"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="22dp"
+            android:layout_marginBottom="22dp"
+            android:layout_marginStart="24dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="8dp"
+            app:cardBackgroundColor="@color/mock_webpage_top_bar_item"
+            app:layout_constraintDimensionRatio="1:1"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="28dp"
+            android:layout_marginBottom="28dp"
+            android:layout_marginStart="97dp"
+            android:layout_marginEnd="97dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="2dp"
+            app:cardBackgroundColor="@color/mock_webpage_top_bar_item"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:background="@color/mock_webpage_background"
+        android:paddingTop="@dimen/gesture_tutorial_webpage_padding_top"
+        android:paddingStart="24dp"
+
+        app:layout_constraintTop_toBottomOf="@id/top_bar"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_1"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_large_line_height"
+            android:layout_marginEnd="126dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_2"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_large_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+            android:layout_marginEnd="64dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_1"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_3"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_large_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+            android:layout_marginEnd="151dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_2"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_button"
+            android:layout_width="47dp"
+            android:layout_height="12dp"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_3"
+            app:layout_constraintStart_toStartOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="47dp"
+            android:layout_height="12dp"
+            android:background="@color/mock_webpage_page_text"
+            android:layout_marginStart="11dp"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_3"
+            app:layout_constraintStart_toEndOf="@id/mock_button"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_block"
+            android:layout_width="0dp"
+            android:layout_height="240dp"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_large_margin_top"
+            android:layout_marginEnd="24dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_large_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_button"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_4"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_large_margin_top"
+            android:layout_marginEnd="52dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_block"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_5"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+            android:layout_marginEnd="41dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_4"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_6"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+            android:layout_marginEnd="71dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_5"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_7"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+            android:layout_marginEnd="198dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_6"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_8"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_large_margin_top"
+            android:layout_marginEnd="64dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_7"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+            android:layout_marginEnd="71dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_8"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout/overview_actions_container.xml b/quickstep/res/layout/overview_actions_container.xml
index 68680d3..dd8afc2 100644
--- a/quickstep/res/layout/overview_actions_container.xml
+++ b/quickstep/res/layout/overview_actions_container.xml
@@ -14,11 +14,10 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<!-- NOTE! don't add dimensions for margins / gravity to root view in this file, they need to be
-     loaded at runtime. -->
 <com.android.quickstep.views.OverviewActionsView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content">
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_horizontal|bottom">
 
     <LinearLayout
         android:id="@+id/action_buttons"
@@ -48,16 +47,23 @@
             android:layout_weight="1" />
 
         <Button
-            android:id="@+id/action_share"
+            android:id="@+id/action_split"
             style="@style/OverviewActionButton"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:drawableStart="@drawable/ic_share"
-            android:text="@string/action_share"
+            android:drawableStart="@drawable/ic_split_screen"
+            android:text="@string/action_split"
             android:theme="@style/ThemeControlHighlightWorkspaceColor"
             android:visibility="gone" />
 
         <Space
+            android:id="@+id/action_split_space"
+            android:layout_width="0dp"
+            android:layout_height="1dp"
+            android:layout_weight="1"
+            android:visibility="gone" />
+
+        <Space
             android:id="@+id/oav_three_button_space"
             android:layout_width="0dp"
             android:layout_height="1dp"
diff --git a/quickstep/res/layout/overview_clear_all_button.xml b/quickstep/res/layout/overview_clear_all_button.xml
index 1ee726e..1dea57e 100644
--- a/quickstep/res/layout/overview_clear_all_button.xml
+++ b/quickstep/res/layout/overview_clear_all_button.xml
@@ -16,7 +16,7 @@
 -->
 <com.android.quickstep.views.ClearAllButton
     xmlns:android="http://schemas.android.com/apk/res/android"
-    style="@android:style/Widget.DeviceDefault.Button.Borderless"
+    style="@style/OverviewClearAllButton"
     android:id="@+id/clear_all"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
diff --git a/quickstep/res/layout/task_grouped.xml b/quickstep/res/layout/task_grouped.xml
new file mode 100644
index 0000000..cd5bcbd
--- /dev/null
+++ b/quickstep/res/layout/task_grouped.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+     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.
+-->
+<!-- NOTE! don't add dimensions for margins / paddings / sizes that change per orientation to this
+     file, they need to be loaded at runtime. -->
+
+<!-- DOUBLE NOTE! Don't deviate IDs from task.xml since this layout acts as a "subclass" (read as
+     "bad code"). How can we use the view pool in RecentsView to use task.xml layout with using
+     GroupedTaskView.java class? Is that possible (while still keeping code in separate class) ? -->
+
+<com.android.quickstep.views.GroupedTaskView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clipChildren="false"
+    android:defaultFocusHighlightEnabled="false"
+    android:focusable="true">
+
+    <com.android.quickstep.views.TaskThumbnailView
+        android:id="@+id/snapshot"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
+    <com.android.quickstep.views.TaskThumbnailView
+        android:id="@+id/bottomright_snapshot"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
+    <com.android.quickstep.views.IconView
+        android:id="@+id/icon"
+        android:layout_width="@dimen/task_thumbnail_icon_size"
+        android:layout_height="@dimen/task_thumbnail_icon_size"
+        android:focusable="false"
+        android:importantForAccessibility="no"/>
+
+    <com.android.quickstep.views.IconView
+        android:id="@+id/bottomRight_icon"
+        android:layout_width="@dimen/task_thumbnail_icon_size"
+        android:layout_height="@dimen/task_thumbnail_icon_size"
+        android:focusable="false"
+        android:importantForAccessibility="no"/>
+</com.android.quickstep.views.GroupedTaskView>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar.xml b/quickstep/res/layout/taskbar.xml
index e680233..3b1d217 100644
--- a/quickstep/res/layout/taskbar.xml
+++ b/quickstep/res/layout/taskbar.xml
@@ -13,12 +13,13 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
 <com.android.launcher3.taskbar.TaskbarDragLayer
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/taskbar_container"
     android:layout_width="wrap_content"
-    android:layout_height="wrap_content">
+    android:layout_height="wrap_content"
+    android:clipChildren="false">
 
     <com.android.launcher3.taskbar.TaskbarView
         android:id="@+id/taskbar_view"
@@ -26,30 +27,59 @@
         android:layout_height="wrap_content"
         android:gravity="center"
         android:forceHasOverlappingRendering="false"
+        android:layout_gravity="bottom"
+        android:clipChildren="false" />
+
+    <com.android.launcher3.taskbar.TaskbarScrimView
+        android:id="@+id/taskbar_scrim"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
+    <FrameLayout
+        android:id="@+id/navbuttons_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
         android:layout_gravity="bottom" >
 
-        <LinearLayout
-            android:id="@+id/system_button_layout"
+        <FrameLayout
+            android:id="@+id/start_contextual_buttons"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_height="match_parent"
             android:paddingLeft="@dimen/taskbar_nav_buttons_spacing"
             android:paddingRight="@dimen/taskbar_nav_buttons_spacing"
-            android:forceHasOverlappingRendering="false"
-            android:gravity="center" />
+            android:paddingTop="@dimen/taskbar_contextual_padding_top"
+            android:gravity="center_vertical"
+            android:layout_gravity="start"/>
 
         <LinearLayout
-            android:id="@+id/hotseat_icons_layout"
+            android:id="@+id/end_nav_buttons"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:forceHasOverlappingRendering="false"
-            android:gravity="center" />
+            android:layout_height="match_parent"
+            android:orientation="horizontal"
+            android:paddingLeft="@dimen/taskbar_nav_buttons_spacing"
+            android:paddingRight="@dimen/taskbar_nav_buttons_spacing"
+            android:layout_marginEnd="@dimen/taskbar_contextual_button_margin"
+            android:gravity="center_vertical"
+            android:layout_gravity="end"/>
 
-    </com.android.launcher3.taskbar.TaskbarView>
+        <FrameLayout
+            android:id="@+id/end_contextual_buttons"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:paddingLeft="@dimen/taskbar_nav_buttons_spacing"
+            android:paddingRight="@dimen/taskbar_nav_buttons_spacing"
+            android:paddingTop="@dimen/taskbar_contextual_padding_top"
+            android:gravity="center_vertical"
+            android:layout_gravity="end"/>
+    </FrameLayout>
 
-    <com.android.launcher3.taskbar.ImeBarView
-        android:id="@+id/ime_bar_view"
-        android:layout_width="wrap_content"
+    <com.android.launcher3.taskbar.StashedHandleView
+        android:id="@+id/stashed_handle"
+        tools:comment1="The actual size and shape will be set as a ViewOutlineProvider at runtime"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:visibility="gone"/>
+        android:background="@color/taskbar_stashed_handle_dark_color"
+        android:clipToOutline="true"
+        android:layout_gravity="bottom"/>
 
 </com.android.launcher3.taskbar.TaskbarDragLayer>
\ No newline at end of file
diff --git a/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml b/quickstep/res/layout/taskbar_contextual_button.xml
similarity index 62%
copy from quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml
copy to quickstep/res/layout/taskbar_contextual_button.xml
index 9c95497..4ffb8d8 100644
--- a/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml
+++ b/quickstep/res/layout/taskbar_contextual_button.xml
@@ -1,17 +1,21 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2020 The Android Open Source Project
+<!-- 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.
 -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-  <solid android:color="@color/gesture_tutorial_fake_previous_task_view_color" />
-</shape>
+<ImageView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/taskbar_nav_buttons_size"
+    android:layout_height="@dimen/taskbar_nav_buttons_size"
+    android:background="@drawable/taskbar_icon_click_feedback_roundrect"
+    android:scaleType="center"/>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_edu.xml b/quickstep/res/layout/taskbar_edu.xml
new file mode 100644
index 0000000..3796ff9
--- /dev/null
+++ b/quickstep/res/layout/taskbar_edu.xml
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<com.android.launcher3.taskbar.TaskbarEduView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_gravity="bottom"
+    android:gravity="bottom"
+    android:orientation="vertical"
+    android:layout_marginHorizontal="108dp">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/edu_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@drawable/bg_rounded_corner_bottom_sheet"
+        android:gravity="center_horizontal"
+        android:paddingHorizontal="36dp"
+        android:paddingTop="64dp">
+
+        <com.android.launcher3.taskbar.TaskbarEduPagedView
+            android:id="@+id/content"
+            android:clipToPadding="false"
+            android:layout_width="match_parent"
+            android:layout_height="378dp"
+            app:layout_constraintTop_toTopOf="parent"
+            launcher:pageIndicator="@+id/content_page_indicator">
+
+            <LinearLayout
+                android:id="@+id/page_switch_apps"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:gravity="center_horizontal">
+
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    style="@style/TextAppearance.TaskbarEdu.Title"
+                    android:text="@string/taskbar_edu_switch_apps"/>
+
+                <ImageView
+                    android:layout_width="322dp"
+                    android:layout_height="282dp"
+                    android:layout_marginTop="16dp"
+                    android:src="@drawable/taskbar_edu_switch_apps"/>
+            </LinearLayout>
+
+            <LinearLayout
+                android:id="@+id/page_splitscreen"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:gravity="center_horizontal">
+
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    style="@style/TextAppearance.TaskbarEdu.Title"
+                    android:text="@string/taskbar_edu_splitscreen"/>
+
+                <ImageView
+                    android:layout_width="322dp"
+                    android:layout_height="282dp"
+                    android:layout_marginTop="16dp"
+                    android:src="@drawable/taskbar_edu_splitscreen"/>
+            </LinearLayout>
+
+            <LinearLayout
+                android:id="@+id/page_stashing"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:gravity="center_horizontal">
+
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    style="@style/TextAppearance.TaskbarEdu.Title"
+                    android:text="@string/taskbar_edu_stashing"/>
+
+                <ImageView
+                    android:layout_width="322dp"
+                    android:layout_height="282dp"
+                    android:layout_marginTop="16dp"
+                    android:src="@drawable/taskbar_edu_stashing"/>
+            </LinearLayout>
+        </com.android.launcher3.taskbar.TaskbarEduPagedView>
+
+        <Button
+            android:id="@+id/edu_start_button"
+            android:layout_width="wrap_content"
+            android:layout_height="36dp"
+            android:layout_marginBottom="92dp"
+            android:layout_marginTop="32dp"
+            app:layout_constraintTop_toBottomOf="@id/content"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            android:text="@string/taskbar_edu_close"
+            style="@style/TaskbarEdu.Button.Close"
+            android:textColor="?android:attr/textColorPrimary"/>
+
+        <com.android.launcher3.pageindicators.PageIndicatorDots
+            android:id="@+id/content_page_indicator"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:layout_constraintTop_toTopOf="@id/edu_start_button"
+            app:layout_constraintBottom_toBottomOf="@id/edu_start_button"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            android:elevation="1dp" />
+
+        <Button
+            android:id="@+id/edu_end_button"
+            android:layout_width="wrap_content"
+            android:layout_height="0dp"
+            app:layout_constraintTop_toTopOf="@id/edu_start_button"
+            app:layout_constraintBottom_toBottomOf="@id/edu_start_button"
+            app:layout_constraintEnd_toEndOf="parent"
+            android:text="@string/taskbar_edu_next"
+            style="@style/TaskbarEdu.Button.Next"
+            android:textColor="?androidprv:attr/textColorOnAccent"/>
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</com.android.launcher3.taskbar.TaskbarEduView>
\ No newline at end of file
diff --git a/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml b/quickstep/res/layout/taskbar_nav_button.xml
similarity index 62%
copy from quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml
copy to quickstep/res/layout/taskbar_nav_button.xml
index 9c95497..4ffb8d8 100644
--- a/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml
+++ b/quickstep/res/layout/taskbar_nav_button.xml
@@ -1,17 +1,21 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2020 The Android Open Source Project
+<!-- 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.
 -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-  <solid android:color="@color/gesture_tutorial_fake_previous_task_view_color" />
-</shape>
+<ImageView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/taskbar_nav_buttons_size"
+    android:layout_height="@dimen/taskbar_nav_buttons_size"
+    android:background="@drawable/taskbar_icon_click_feedback_roundrect"
+    android:scaleType="center"/>
\ No newline at end of file
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index 433ef86..459d32a 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minuut"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> oor vandag"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Programvoorstelle"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Alle programme"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Jou voorspelde programme"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Kry programvoorstelle in die onderste ry van jou tuisskerm"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Kry programvoorstelle op jou tuisskerm se gunstelingery"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Stelselnavigasie-instellings"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Deel"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Skermkiekie"</string>
+    <string name="action_split" msgid="2098009717623550676">"Verdeel"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Probeer ander program om verdeelde skerm te gebruik"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Jou organisasie laat nie hierdie program toe nie"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Slaan navigasietutoriaal oor?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Jy kan dit later in die <xliff:g id="NAME">%1$s</xliff:g>-program kry"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Kanselleer"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Slaan oor"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Taakbalkopvoeding het verskyn"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Taakbalkopvoeding is toegemaak"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Raak en hou om die taakbalk te versteek"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Volgende"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Terug"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Maak toe"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Klaar"</string>
 </resources>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index 838dd60..7961b28 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 ደቂቃ"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"ዛሬ <xliff:g id="TIME">%1$s</xliff:g> ቀርቷል"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"የመተግበሪያ አስተያየቶች"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"ሁሉም መተግበሪያዎች"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"የእርስዎ የሚገመቱ መተግበሪያዎች"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"በመነሻ ገጽዎ ታችኛው ረድፍ ላይ የመተግበሪያ አስተያየት ጥቆማዎችን ያግኙ"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"በመነሻ ማያ ገጽዎ የተወዳጆች ረድፍ ላይ የመተግበሪያ አስተያየት ጥቆማዎችን ያግኙ"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"የስርዓት አሰሳ ቅንብሮች"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"አጋራ"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ቅጽበታዊ ገጽ እይታ"</string>
+    <string name="action_split" msgid="2098009717623550676">"ክፈል"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"የተከፈለ ማያን ለመጠቀም ሌላ መተግበሪያ መታ ያድርጉ"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"ይህ ድርጊት በመተግበሪያው ወይም በእርስዎ ድርጅት አይፈቀድም"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"የአሰሳ አጋዥ ሥልጠናን ይዝለሉ?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"ይህን በኋላ በ<xliff:g id="NAME">%1$s</xliff:g> መተግበሪያው ውስጥ ማግኘት ይችላሉ"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"ይቅር"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"ዝለል"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"የተግባር አሞሌ ትምህርት ይታያል"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"የተግባር አሞሌ ትምህርት ተዘግቷል"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"የተግባር አሞሌውን ለመደበቅ ነክተው ይያዙት"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"ቀጣይ"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"ተመለስ"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"ዝጋ"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"ተጠናቅቋል"</string>
 </resources>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index 3f26977..a293124 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"أقل من دقيقة"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"يتبقى اليوم <xliff:g id="TIME">%1$s</xliff:g>."</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"التطبيقات المقترحة"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"جميع التطبيقات"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"تطبيقاتك المتوقّعة"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"رؤية التطبيقات المقترحة في الصف السفلي من الشاشة الرئيسية"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"رؤية التطبيقات المقترحة في صف التطبيقات المفضّلة في الشاشة الرئيسية"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"إعدادات التنقّل داخل النظام"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"مشاركة"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"لقطة شاشة"</string>
+    <string name="action_split" msgid="2098009717623550676">"تقسيم"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"انقر على تطبيق آخر لاستخدام وضع تقسيم الشاشة."</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"لا يسمح التطبيق أو لا تسمح مؤسستك بهذا الإجراء."</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"هل تريد تخطي الدليل التوجيهي؟"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"يمكنك العثور على هذا الدليل التوجيهي لاحقًا في التطبيق \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"إلغاء"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"التخطي"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ظهرت لوحة تعليم استخدام شريط المهام."</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"تم إغلاق لوحة تعليم استخدام شريط المهام."</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"انقر مع الاستمرار لإخفاء شريط المهام."</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"الشاشة التالية"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"رجوع"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"إغلاق"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"تم"</string>
 </resources>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index aa114fa..cc4347a 100644
--- a/quickstep/res/values-as/strings.xml
+++ b/quickstep/res/values-as/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; ১ মিনিট"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"আজি <xliff:g id="TIME">%1$s</xliff:g> বাকী আছ"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"এপ চাজেশ্বন"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"সকলো এপ্"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"আপোনাৰ প্ৰয়োজন হ\'ব পৰা এপ্"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"আপোনাৰ গৃহ স্ক্ৰীনৰ একেবাৰে তলৰ শাৰীটোত এপৰ পৰামর্শসমূহ পাওক"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"আপোনাৰ গৃহ স্ক্ৰীনৰ প্ৰিয় সমলৰ শাৰীটোত এপৰ পৰামর্শসমূহ পাওক"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ছিষ্টেম নেভিগেশ্বনৰ ছেটিং"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"শ্বেয়াৰ কৰক"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"স্ক্ৰীনশ্বট"</string>
+    <string name="action_split" msgid="2098009717623550676">"বিভাজন কৰক"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰিবলৈ অন্য এটা এপত টিপক"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"এপ্‌টোৱে অথবা আপোনাৰ প্ৰতিষ্ঠানে এই কাৰ্যটোৰ অনুমতি নিদিয়ে"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"নেভিগেশ্বনৰ টিউট’ৰিয়েল এৰিব বিচাৰে নেকি?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"আপুনি এয়া পাছত <xliff:g id="NAME">%1$s</xliff:g> এপ্‌টোত বিচাৰিব পাৰিব"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"বাতিল কৰক"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"এৰি যাওক"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"টাস্কবাৰৰ শিক্ষাৰ পেনেলটো প্ৰদর্শিত হৈছে"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"টাস্কবাৰৰ শিক্ষাৰ পেনেলটো বন্ধ হৈছে"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"টাস্কবাৰডাল লুকুৱাবলৈ স্পৰ্শ কৰি ধৰি ৰাখক"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"পৰৱৰ্তী"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"উভতি যাওক"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"বন্ধ কৰক"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"হ’ল"</string>
 </resources>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index d01bf3f..e288b5d 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 dəq"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Bu gün <xliff:g id="TIME">%1$s</xliff:g> qaldı"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Tətbiq təklifləri"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Bütün tətbiqlər"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Təklif edilən tətbiqlər"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Ana ekranın aşağı sırasında tətbiq təklifləri alın"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Ana ekranın sevimlilər sırasında tətbiq təklifləri alın"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sistem naviqasiya ayarları"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Paylaşın"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Skrinşot"</string>
+    <string name="action_split" msgid="2098009717623550676">"Ayırın"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Bölmə ekranını istifadə etmək üçün başqa tətbiqə toxunun"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Bu əməliyyata tətbiq və ya təşkilatınız tərəfindən icazə verilmir"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Naviqasiya dərsliyi ötürülsün?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Bunu sonra <xliff:g id="NAME">%1$s</xliff:g> tətbiqində tapa bilərsiniz"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Ləğv edin"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Ötürün"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Tapşırıq panelindəki təlim bölməsi görünür"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Tapşırıq panelindəki təlim bölməsi bağlanıb"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Tapşırıq panelini toxunub saxlamaqla gizlədin"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Sonra"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Geri"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Bağlayın"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Hazırdır"</string>
 </resources>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index bde409f..3ebaa40 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Još <xliff:g id="TIME">%1$s</xliff:g> danas"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Predlozi aplikacija"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Sve aplikacije"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Predviđene aplikacije"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Dobijajte predloge aplikacija u donjem redu početnog ekrana"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Dobijajte predloge aplikacija u redu sa omiljenim stavkama na početnom ekranu"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Podešavanja kretanja kroz sistem"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Deli"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Snimak ekrana"</string>
+    <string name="action_split" msgid="2098009717623550676">"Podeli"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Dodirnite drugu aplikaciju za podeljeni ekran"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Aplikacija ili organizacija ne dozvoljavaju ovu radnju"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Želite da preskočite vodič za kretanje?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Možete da pronađete ovo kasnije u aplikaciji <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Otkaži"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Preskoči"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Edukativno okno iz trake zadataka se pojavilo"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Edukativno okno iz trake zadataka je zatvoreno"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Dodirnite i zadržite za skrivanje trake zadataka"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Dalje"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Nazad"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Zatvori"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Gotovo"</string>
 </resources>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index 8e57bd8..bf21d81 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 хв"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Сёння засталося <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Прапановы праграм"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Усе праграмы"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Вашы праграмы з падказак"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Атрымлівайце прапановы праграм у ніжнім радку на Галоўным экране."</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Атрымлівайце прапановы праграм у пераліку абраных на Галоўным экране"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Налады навігацыі ў сістэме"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Абагуліць"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Здымак экрана"</string>
+    <string name="action_split" msgid="2098009717623550676">"Падзелены экран"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Для падзеленага экрана націсніце на іншую праграму"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Гэта дзеянне не дазволена праграмай ці вашай арганізацыяй"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Прапусціць дапаможнік па навігацыі?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Знайсці дапаможнік можна ў праграме \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Скасаваць"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Прапусціць"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"З\'явілася панэль навучання на панэлі задач"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Панэль навучання на панэлі задач закрыта"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Націсніце і ўтрымлівайце, каб схаваць панэль задач"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Далей"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Назад"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Закрыць"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Гатова"</string>
 </resources>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index 7305d9d..659dcb6 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 мин"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Оставащо време днес: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Предложения за приложения"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Всички приложения"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Предвидени приложения"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Получавайте предложения за приложения на най-долния ред на началния си екран"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Получаване на предложения за приложения в реда с любими на началния екран"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Настройки за навигиране в системата"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Споделяне"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Екранна снимка"</string>
+    <string name="action_split" msgid="2098009717623550676">"Разделяне на екрана"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Докоснете друго прил., за да ползвате разд. екран"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Това действие не е разрешено от приложението или организацията ви"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Пропускане на урока за навигиране?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Урокът е налице в приложението <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Отказ"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Пропускане"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Показва се урокът за лентата на задачите"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Урокът за лентата на задачите бе затворен"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Докоснете и задръжте, за да скриете лентата на задачите"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Напред"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Назад"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Затваряне"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Готово"</string>
 </resources>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index 1d0b45f..cc8e186 100644
--- a/quickstep/res/values-bn/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; ১ মি."</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"আজকে <xliff:g id="TIME">%1$s</xliff:g> বাকি আছে"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"অ্যাপের সাজেশন"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"সব অ্যাপ"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"আপনার প্রয়োজন হতে পারে এমন অ্যাপ"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"আপনার হোম স্ক্রিনের নিচে সারিতে অ্যাপ সাজেশন পান"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"হোম স্ক্রিনের \'ফেভারিট রো\' বিকল্পের জন্য অ্যাপ সাজেশন পান"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"সিস্টেম নেভিগেশন সেটিংস"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"শেয়ার করুন"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"স্ক্রিনশট নিন"</string>
+    <string name="action_split" msgid="2098009717623550676">"স্প্লিট"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"স্প্লিটস্ক্রিন ব্যবহার করতে অন্য অ্যাপে ট্যাপ করুন"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"এই অ্যাপ বা আপনার প্রতিষ্ঠান এই অ্যাকশনটি পারফর্ম করার অনুমতি দেয়নি"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"নেভিগেশন টিউটোরিয়াল এড়িয়ে যেতে চান?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"আপনি <xliff:g id="NAME">%1$s</xliff:g> অ্যাপে পরে এটি খুঁজে পাবেন"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"বাতিল করুন"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"এড়িয়ে যান"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"টাস্কবার এডুকেশন দেখানো হয়েছে"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"টাস্কবার এডুকেশন বন্ধ করা আছে"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"টাস্কবার লুকানোর জন্য টাচ করে ধরে থাকুন"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"পরবর্তী"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"ফিরুন"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"বন্ধ করুন"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"হয়ে গেছে"</string>
 </resources>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index acd7b10..bf2517f 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Preostalo vrijeme: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Prijedlozi aplikacija"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Sve aplikacije"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Predviđene aplikacije"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Primajte prijedloge aplikacija u donjem redu početnog ekrana"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Primajte prijedloge aplikacija u redu omiljenih stavki početnog ekrana"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Postavke navigiranja sistemom"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Dijeli"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Snimak ekrana"</string>
+    <string name="action_split" msgid="2098009717623550676">"Podijeli"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Dodirnite drugu apl. da koristite podijeljeni ekran"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Ovu radnju ne dozvoljava aplikacija ili vaša organizacija"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Preskočiti vodič za navigiranje?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"To možete pronaći kasnije u aplikaciji <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Otkaži"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Preskoči"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Edukacija o programskoj traci je prikazana"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Edukacija o programskoj traci je zatvorena"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Dodirnite i držite da sakrijete programsku traku"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Naprijed"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Nazad"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Zatvori"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Gotovo"</string>
 </resources>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index a0d6aee..1b1ed61 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minut"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"temps restant avui: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Suggeriments d\'aplicacions"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Totes les aplicacions"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Prediccions d\'aplicacions"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Obtén suggeriments d\'aplicacions a la fila inferior de la pantalla d\'inici"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Obtén suggeriments d\'aplicacions a la fila Preferides de la teva pantalla d\'inici"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Configuració de navegació del sistema"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Comparteix"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Captura de pantalla"</string>
+    <string name="action_split" msgid="2098009717623550676">"Divideix"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Toca una altra aplicació per dividir la pantalla"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"L\'aplicació o la teva organització no permeten aquesta acció"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Vols ometre el tutorial de navegació?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Pots trobar-lo més tard a l\'aplicació <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancel·la"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Omet"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Ha aparegut el tauler educatiu de la barra de tasques"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"S\'ha tancat el tauler educatiu de la barra de tasques"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Mantén premut per amagar la barra de tasques"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Següent"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Enrere"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Tanca"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Fet"</string>
 </resources>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index b2d092e..ae2e13f 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minuta"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"dnes zbývá: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Návrhy aplikací"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Všechny aplikace"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Návrhy aplikací pro vás"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Nechte si ve spodním řádku na ploše zobrazovat návrhy aplikací"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Nechte si na řádku oblíbených na ploše zobrazovat návrhy aplikací"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Nastavení navigace v systému"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Sdílet"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Snímek obrazovky"</string>
+    <string name="action_split" msgid="2098009717623550676">"Rozdělit"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Klepnutím na jinou aplikaci rozdělíte obrazovku"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Aplikace nebo organizace zakazuje tuto akci"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Přeskočit výukový program k navigaci?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Program později najdete v aplikaci <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Zrušit"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Přeskočit"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Zobrazila se výuka k hlavnímu panelu"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Výuka k hlavnímu panelu byla zavřena"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Hlavní panel můžete skrýt podržením"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Další"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Zpět"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Zavřít"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Hotovo"</string>
 </resources>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index f2e2df0..26de0c2 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> tilbage i dag"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Appforslag"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Alle apps"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Dine foreslåede apps"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Få appforslag på den nederste række af din startskærm"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Få appforslag i rækken med favoritter på din startskærm"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Indstillinger for systemnavigation"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Del"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
+    <string name="action_split" msgid="2098009717623550676">"Opdel"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Tryk på en anden app for at bruge opdelt skærm"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Appen eller din organisation tillader ikke denne handling"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Vil du springe selvstudiet for navigation over?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Du kan finde dette senere i appen <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Annuller"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Spring over"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Vejledningen om proceslinjen blev åbnet"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Vejledningen om proceslinjen blev lukket"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Du kan skjule proceslinjen ved at holde fingeren nede"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Næste"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Tilbage"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Luk"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Luk"</string>
 </resources>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index 4a023e3..494f846 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 Min."</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Heute noch <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"App-Vorschläge"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Alle Apps"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"App-Vorschläge für dich"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Lass dir in der unteren Reihe auf deinem Startbildschirm Vorschläge für Apps anzeigen"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Lass dir in der Favoritenleiste auf dem Startbildschirm App-Vorschläge anzeigen"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Einstellungen der Systemsteuerung"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Teilen"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
+    <string name="action_split" msgid="2098009717623550676">"Teilen"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Für „Bildschirm teilen“ auf weitere App tippen"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Die App oder deine Organisation lässt diese Aktion nicht zu"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Navigationstutorial überspringen?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Du findest es später auch in der <xliff:g id="NAME">%1$s</xliff:g> App"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Abbrechen"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Überspringen"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Anleitung für Taskleiste eingeblendet"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Anleitung für Taskleiste geschlossen"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Gedrückt halten, um die Taskleiste auszublenden"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Weiter"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Zurück"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Schließen"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Fertig"</string>
 </resources>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index 7243e03..a266ff8 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 λ."</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Απομένουν <xliff:g id="TIME">%1$s</xliff:g> σήμερα"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Προτεινόμενες εφαρμογές"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Όλες οι εφαρμογές"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Προβλέψεις εφαρμογών"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Δείτε τις προτεινόμενες εφαρμογές στην κάτω σειρά της αρχικής οθόνης"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Δείτε τις προτεινόμενες εφαρμογές στη σειρά Αγαπημένα της αρχικής οθόνης."</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Ρυθμίσεις πλοήγησης συστήματος"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Κοινοποίηση"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Στιγμιότυπο οθόνης"</string>
+    <string name="action_split" msgid="2098009717623550676">"Διαχωρισμός"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Πατήστε άλλη εφαρμογή για χρήση διαχωρισμού οθόνης"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Αυτή η ενέργεια δεν επιτρέπεται από την εφαρμογή ή τον οργανισμό σας."</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Παράβλεψη οδηγού πλοήγησης;"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Βρείτε τον αργότερα στην εφαρμογή <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Ακύρωση"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Παράβλεψη"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Η εκπαίδευση για τη γραμμή εργασιών εμφανίστηκε"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Η εκπαίδευση για τη γραμμή εργασιών έκλεισε"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Αγγίξτε παρατεταμένα για απόκρυψη της γραμμής εργασιών."</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Επόμενο"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Πίσω"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Κλείσιμο"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Τέλος"</string>
 </resources>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index 7b42aac..b787fdf 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minute"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> left today"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"App suggestions"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"All apps"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Your predicted apps"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Get app suggestions on the bottom row of your home screen"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Get app suggestions on the favourites row of your home screen"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"System navigation settings"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Share"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
+    <string name="action_split" msgid="2098009717623550676">"Split"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Tap another app to use split-screen"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organisation"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Skip navigation tutorial?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"You can find this later in the <xliff:g id="NAME">%1$s</xliff:g> app"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancel"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Skip"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Taskbar education appeared"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Taskbar education closed"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Touch &amp; hold to hide the taskbar"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Next"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Back"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Close"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Done"</string>
 </resources>
diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml
index 7b42aac..b787fdf 100644
--- a/quickstep/res/values-en-rCA/strings.xml
+++ b/quickstep/res/values-en-rCA/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minute"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> left today"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"App suggestions"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"All apps"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Your predicted apps"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Get app suggestions on the bottom row of your home screen"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Get app suggestions on the favourites row of your home screen"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"System navigation settings"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Share"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
+    <string name="action_split" msgid="2098009717623550676">"Split"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Tap another app to use split-screen"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organisation"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Skip navigation tutorial?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"You can find this later in the <xliff:g id="NAME">%1$s</xliff:g> app"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancel"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Skip"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Taskbar education appeared"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Taskbar education closed"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Touch &amp; hold to hide the taskbar"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Next"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Back"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Close"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Done"</string>
 </resources>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index 7b42aac..b787fdf 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minute"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> left today"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"App suggestions"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"All apps"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Your predicted apps"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Get app suggestions on the bottom row of your home screen"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Get app suggestions on the favourites row of your home screen"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"System navigation settings"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Share"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
+    <string name="action_split" msgid="2098009717623550676">"Split"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Tap another app to use split-screen"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organisation"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Skip navigation tutorial?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"You can find this later in the <xliff:g id="NAME">%1$s</xliff:g> app"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancel"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Skip"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Taskbar education appeared"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Taskbar education closed"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Touch &amp; hold to hide the taskbar"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Next"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Back"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Close"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Done"</string>
 </resources>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index 7b42aac..b787fdf 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minute"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> left today"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"App suggestions"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"All apps"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Your predicted apps"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Get app suggestions on the bottom row of your home screen"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Get app suggestions on the favourites row of your home screen"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"System navigation settings"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Share"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
+    <string name="action_split" msgid="2098009717623550676">"Split"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Tap another app to use split-screen"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organisation"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Skip navigation tutorial?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"You can find this later in the <xliff:g id="NAME">%1$s</xliff:g> app"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancel"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Skip"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Taskbar education appeared"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Taskbar education closed"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Touch &amp; hold to hide the taskbar"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Next"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Back"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Close"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Done"</string>
 </resources>
diff --git a/quickstep/res/values-en-rXC/strings.xml b/quickstep/res/values-en-rXC/strings.xml
index 35ec6bb..c92af05 100644
--- a/quickstep/res/values-en-rXC/strings.xml
+++ b/quickstep/res/values-en-rXC/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‏‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‎‎‎‏‏‎‏‎‏‎‎‎‎&lt; 1 minute‎‏‎‎‏‎"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‎‏‏‎‎‎‎‎‎‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‏‏‎‎‏‎‎‎‎‎‎‏‎‎‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="TIME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ left today‎‏‎‎‏‎"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‎‎‏‎‎‏‎‏‏‏‎‎‎‏‏‏‎‏‏‎‎‎‎‎‏‎‏‎‎‎‎‏‏‏‎‏‎‎‎‎‎‎App suggestions‎‏‎‎‏‎"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‏‎‎‎‎‏‎‎‎‎‏‏‏‎‏‏‏‎‏‏‏‏‏‎‎‎‏‏‎‎‏‏‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎‏‏‏‎All apps‎‏‎‎‏‎"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‏‎‏‏‎‎‎‎‎‏‎‏‏‏‏‏‎‏‏‎‏‏‏‎‎‏‎‏‏‏‏‎‎‎‏‏‎‏‏‏‏‎‎‏‏‏‏‏‏‏‎‏‎‎Your predicted apps‎‏‎‎‏‎"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‎‎‏‎‎‏‎‏‏‏‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‏‎‎‏‎‏‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‏‏‏‎‎‎Get app suggestions on the bottom row of your Home screen‎‏‎‎‏‎"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‏‏‏‏‎‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‏‎‎‎‎‏‎‏‏‏‏‏‎‏‏‎Get app suggestions on favorites row of your Home screen‎‏‎‎‏‎"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎‏‎‏‏‎‎‎‎‎‏‎‎‎‎‏‎‎‎‎‏‏‎‏‎‏‎‎‎‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‎‏‎‎‏‏‎"<annotation id="link">"‎‏‎‎‏‏‏‎System navigation settings‎‏‎‎‏‏‎"</annotation>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="action_share" msgid="2648470652637092375">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‎‎‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‎‏‏‎‎‎‎‎‎‎‏‏‏‏‎‏‏‎‎‎‏‏‎‏‎‎‎‎‏‎‏‏‏‎Share‎‏‎‎‏‎"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‎‏‎‏‎‏‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‏‎‎‏‏‎‎‏‏‎‎‏‎‎‎‎‏‏‏‏‎‎‎‏‎‏‎Screenshot‎‏‎‎‏‎"</string>
+    <string name="action_split" msgid="2098009717623550676">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‏‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‏‏‏‎‎‏‎‎‏‎‎‎‏‎‎‏‏‏‏‎‎‏‎‏‏‎‏‎‏‎‎‎Split‎‏‎‎‏‎"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‏‏‎‎‎‎‎‎‎‎‎‏‏‎‎‎‏‎‎‏‎‏‏‎‎‏‏‎‏‎‏‎‎‎‏‎‏‎‏‏‎‏‏‎‏‎‎‎‏‏‎‎‎‎‎Tap another app to use splitscreen‎‏‎‎‏‎"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‏‏‏‏‎‎‎‏‏‎‎‎‎‏‎‎‏‏‎‎‏‏‏‎‏‎‏‏‏‎‎‎‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‎‏‎‏‎‎This action isn\'t allowed by the app or your organization‎‏‎‎‏‎"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‎‏‎‎‏‏‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‎‎‎‎‎‏‎‎‏‏‎‎‏‎‎‏‏‏‎‏‎‎Skip navigation tutorial?‎‏‎‎‏‎"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‏‎‎‏‏‏‎‎‏‏‎‏‎‏‏‎‏‏‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‏‏‎‎You can find this later in the ‎‏‎‎‏‏‎<xliff:g id="NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ app‎‏‎‎‏‎"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‎‎‎‎‏‏‎‏‎‎‏‎‎‎‏‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‎Cancel‎‏‎‎‏‎"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‎‏‏‏‎‏‎‏‏‏‏‎‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‎‏‎‎‏‏‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‏‎‎‏‏‎‎‎‎Skip‎‏‎‎‏‎"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‏‎‎‏‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‎‎‏‎‏‎‏‎‎‎‎‎‎‏‏‎‎‎‎‎‎‏‏‎‎‏‎Taskbar education appeared‎‏‎‎‏‎"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‏‏‎‎‏‏‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‎‏‎‏‎‏‏‏‏‎‎‏‏‏‏‏‎‎Taskbar education closed‎‏‎‎‏‎"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‎‏‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‎‎‎‏‏‏‎‎‏‎‏‏‎‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‏‎‎‏‏‏‏‏‏‎Touch &amp; hold to hide the taskbar‎‏‎‎‏‎"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‏‏‎‏‎‏‏‏‎‎‏‎‏‏‎‎‏‎‏‏‎‏‎‏‎‎‏‎‎‏‎‎‏‏‎‏‎‎‏‎‎‏‎‎‎‎‏‎Next‎‏‎‎‏‎"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‎‏‎‎‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‏‏‏‏‎‎‏‎‎‎‏‏‎‏‎‎‏‏‎‎Back‎‏‎‎‏‎"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‎‏‎‏‎‏‏‎‏‎‏‏‎‏‎‏‏‎‏‏‏‏‎‎‏‎‎‏‎‏‎‎‎‏‏‏‎‎‎‎‎‏‏‎‎‎‎‏‎Close‎‏‎‎‏‎"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‎‎‏‏‎‏‏‎‎‎‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‏‎‏‏‏‎‎‎‏‏‎‎‎‎‏‏‏‏‎‎‏‎Done‎‏‎‎‏‎"</string>
 </resources>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index e61fd91..118df33 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minuto"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Tiempo restante: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Sugerencias de aplicaciones"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Todas las apps"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Predicción de tus apps"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Obtén sugerencias de aplicaciones en la fila inferior de la pantalla principal"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Obtén sugerencias de apps en la fila de favoritos de la pantalla principal"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Configuración de navegación del sistema"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Compartir"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Captura de pantalla"</string>
+    <string name="action_split" msgid="2098009717623550676">"Pantalla dividida"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Presiona otra app para usar la pantalla dividida"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"La app o tu organización no permiten realizar esta acción"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"¿Omitir el instructivo de navegación?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Puedes encontrarlo en la app de <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancelar"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Omitir"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Se abrió la barra de herramientas Educación"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Se cerró la barra de herramientas Educación"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Mantén presionado para ocultar la barra de tareas"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Siguiente"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Atrás"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Cerrar"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Listo"</string>
 </resources>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index 8e8829e..5b2cee9 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt;1 minuto"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"tiempo restante: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Sugerencias de aplicaciones"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Todas las aplicaciones"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Predicción de aplicaciones"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Obtén sugerencias de aplicaciones en la fila inferior de la pantalla de inicio"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Recibe sugerencias de aplicaciones en la fila de aplicaciones favoritas de la pantalla de inicio"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Ajustes de navegación del sistema"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Compartir"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Hacer captura"</string>
+    <string name="action_split" msgid="2098009717623550676">"Dividir"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Toca otra aplicación para usar la pantalla dividida"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"No puedes hacerlo porque la aplicación o tu organización no lo permiten"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"¿Saltar tutorial de navegación?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Puedes consultarlo en otro momento en la aplicación <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancelar"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Saltar"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Ha aparecido una nota sobre la barra de tareas"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Nota sobre la barra de tareas cerrada"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Mantén pulsada la barra de tareas para ocultarla"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Siguiente"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Atrás"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Cerrar"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Hecho"</string>
 </resources>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index 74b7528..7b2b3da 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minut"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Tääna jäänud <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Rakenduste soovitused"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Kõik rakendused"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Teie ennustatud rakendused"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Hankige avakuva alumisel real rakenduste soovitusi"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Hankige avakuva lemmikute reale rakenduste soovitusi"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Süsteemi navigeerimisseaded"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Jaga"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Ekraanipilt"</string>
+    <string name="action_split" msgid="2098009717623550676">"Eralda"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Jagatud kuva kasutamiseks puudutage muud rakendust"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Rakendus või teie organisatsioon on selle toimingu keelanud"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Kas jätta navigeerimise õpetused vahele?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Leiate selle hiljem rakendusest <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Tühista"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Jäta vahele"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Tegumiriba juhised kuvati"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Tegumiriba juhised on suletud"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Tegumiriba peitmiseks puudutage pikalt"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Järgmine"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Tagasi"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Sule"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Valmis"</string>
 </resources>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index 12a0dbc..8e26e20 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> gelditzen dira gaur"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Aplikazioen iradokizunak"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Aplikazio guztiak"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Iradokitako aplikazioak"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Jaso aplikazioen iradokizunak hasierako pantailaren beheko errenkadan"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Jaso aplikazioen iradokizunak hasierako pantailako gogokoen errenkadan"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sisteman nabigatzeko ezarpenak"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Partekatu"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Atera pantaila-argazki bat"</string>
+    <string name="action_split" msgid="2098009717623550676">"Zatitu"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Sakatu beste aplikazio bat pantaila zatitzeko"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Aplikazioak edo erakundeak ez du eman ekintza hori gauzatzeko baimena"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Nabigazio-tutoriala saltatu nahi duzu?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"<xliff:g id="NAME">%1$s</xliff:g> aplikazioan dago eskuragarri tutoriala"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Utzi"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Saltatu"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Agertu egin da zereginen barraren tutoriala"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Itxi egin da zereginen barraren tutoriala"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Zereginen barra ezkutatzeko, eduki ezazu sakatuta"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Hurrengoa"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Atzera"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Itxi"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Eginda"</string>
 </resources>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index ae260ed..c0a805c 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; ۱ دقیقه"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> باقی‌مانده برای امروز"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"پیشنهادهای برنامه"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"همه برنامه‌ها"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"برنامه‌های پیش‌بینی‌شده"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"دریافت پیشنهادهای برنامه در ردیف پایین صفحه اصلی"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"دریافت «پیشنهاد برنامه» در ردیف موارد دلخواه صفحه اصلی"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"تنظیمات پیمایش سیستم"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"هم‌رسانی"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"نماگرفت"</string>
+    <string name="action_split" msgid="2098009717623550676">"دونیمه"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"برای استفاده از صفحهٔ دونیمه، روی برنامه دیگری ضربه بزنید"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"برنامه یا سازمان شما اجازه نمی‌دهد این کنش انجام شود."</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"آموزش گام‌به‌گام پیمایش رد شود؟"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"می‌توانید آن را بعداً در برنامه <xliff:g id="NAME">%1$s</xliff:g> پیدا کنید"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"لغو"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"رد شدن"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"پانل آموزشی نوار وظیفه نمایان شد"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"پانل آموزشی نوار وظیفه بسته شد"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"برای پنهان کردن نوار وظیفه، لمس کنید و نگه دارید"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"بعدی"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"برگشت"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"بستن"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"تمام"</string>
 </resources>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index 8762feb..e6f6ab1 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> jäljellä tänään"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Sovellusehdotukset"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Kaikki sovellukset"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Sovellusennusteet"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Näytä sovellusehdotuksia aloitusnäytön alimmalla rivillä"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Näytä sovellusehdotuksia aloitusnäytön Suosikit-rivillä"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Järjestelmän navigointiasetukset"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Jaa"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Kuvakaappaus"</string>
+    <string name="action_split" msgid="2098009717623550676">"Jaa"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Avaa jaettu näyttö napauttamalla toista sovellusta"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Sovellus tai organisaatio ei salli tätä toimintoa"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Ohitetaanko navigointiohje?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Löydät tämän myöhemmin sovelluksesta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Peru"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Ohita"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Tehtäväpalkin ohje näkyvissä"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Tehtäväpalkin ohje suljettu"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Piilota tehtäväpalkki koskettamalla pitkään"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Seuraava"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Takaisin"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Sulje"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Valmis"</string>
 </resources>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index a3c9393..3b611c4 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Il reste <xliff:g id="TIME">%1$s</xliff:g> aujourd\'hui"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Suggestions d\'applications"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Toutes les applications"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Vos prédictions d\'applications"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Obtenir des suggestions d\'applications dans la rangée du bas de votre écran d\'accueil"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Retrouvez des suggestions d\'applications dans la rangée des favoris de votre écran d\'accueil"</string>
@@ -79,9 +78,18 @@
     <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>
     <string name="action_screenshot" msgid="8171125848358142917">"Capture d\'écran"</string>
+    <string name="action_split" msgid="2098009717623550676">"Séparé"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Touchez une autre appli pour partager l\'écran"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"L\'application ou votre organisation n\'autorise pas cette action"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Ignorer le tutoriel sur la navigation?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Vous trouverez le tutoriel dans l\'application <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Annuler"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Ignorer"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"La barre des tâches éducatives s\'est affichée"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"La barre des tâches éducatives est fermée"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Maintenez le doigt sur la barre des tâches pour la masquer"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Suivant"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Retour"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Fermer"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"OK"</string>
 </resources>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index 5164792..6f7149f 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Encore <xliff:g id="TIME">%1$s</xliff:g> aujourd\'hui"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Applications suggérées"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Toutes les applications"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Applications prévues pour vous"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Retrouvez vos applications favorites au bas de votre écran d\'accueil"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Retrouvez des suggestions d\'applications dans la zone des favoris de votre écran d\'accueil"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Paramètres de navigation système"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Partager"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Capture d\'écran"</string>
+    <string name="action_split" msgid="2098009717623550676">"Partager"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Appuyez sur autre appli pour utiliser écran partagé"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Cette action n\'est pas autorisée par l\'application ou par votre organisation"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Ignorer le tutoriel de navigation ?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Vous le retrouverez dans l\'appli <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Annuler"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Passer"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Infos sur la barre des tâches affichées"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Infos sur la barre des tâches fermées"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Appuyez de manière prolongée pour masquer barre des tâches"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Suivant"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Retour"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Fermer"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"OK"</string>
 </resources>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index baa87e7..db28634 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt;1 min"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Tempo restante hoxe <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Suxestións de aplicacións"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Todas as aplicacións"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Previsión das túas aplicacións"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Recibe suxestións de aplicacións na fila inferior da pantalla de inicio"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Recibe suxestións de aplicacións na fila de Favoritos da pantalla de inicio"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Configuración da navegación do sistema"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Compartir"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Facer captura"</string>
+    <string name="action_split" msgid="2098009717623550676">"Dividir"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Para usar a pantalla dividida, toca outra app"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"A aplicación ou a túa organización non permite realizar esta acción"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Queres omitir o titorial de navegación?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Podes atopar isto máis tarde na aplicación <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancelar"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Omitir"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Panel de información de barra de tarefas aberto"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Panel de información de barra de tarefas pechado"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Mantén premida a barra de tarefas para ocultala"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Seguinte"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Atrás"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Pechar"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Feito"</string>
 </resources>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index 33e8617..cc998b4 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 મિનિટ"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> આજે બાકી"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"ઍપ સૂચનો"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"બધી ઍપ"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"તમારી પૂર્વાનુમાનિત ઍપ"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"તમારી હોમ સ્ક્રીનની નીચલી પંક્તિમાં ઍપના સુઝાવો મેળવો"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"તમારી હોમ સ્ક્રીનની મનપસંદ પંક્તિમાં ઍપના સુઝાવો મેળવો"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"સિસ્ટમના નૅવિગેશન સેટિંગ"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"શેર કરો"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"સ્ક્રીનશૉટ"</string>
+    <string name="action_split" msgid="2098009717623550676">"વિભાજિત કરો"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"સ્પલિટસ્ક્રીનના વપરાશ માટે, કોઈ અન્ય ઍપ પર ટૅપ કરો"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"ઍપ કે તમારી સંસ્થા દ્વારા આ ક્રિયા કરવાની મંજૂરી નથી"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"નૅવિગેશન ટ્યૂટૉરિઅલ છોડી દઈએ?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"તમે આને પછીથી <xliff:g id="NAME">%1$s</xliff:g> ઍપમાં જોઈ શકો છો"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"રદ કરો"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"છોડો"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ટાસ્કબારનું શિક્ષણ આપતી પૅનલ દેખાય છે"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ટાસ્કબારનું શિક્ષણ આપતી પૅનલ બંધ થઈ છે"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"ટાસ્કબાર છુપાવવા, તેને ટચ કરીને થોડીવાર દબાવી રાખો"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"આગળ"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"પાછળ"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"બંધ કરો"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"થઈ ગયું"</string>
 </resources>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index 50ebb27..fd06fca 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt;1 मिनट"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"आज <xliff:g id="TIME">%1$s</xliff:g> और चलेगा"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"सुझाए गए ऐप्लिकेशन"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"सभी ऐप्लिकेशन"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"आपके काम के ऐप्लिकेशन"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"अपने होम स्क्रीन की सबसे नीचे वाली पंक्ति में ऐप्लिकेशन के सुझाव पाएं"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"अपने होम स्क्रीन की सबसे नीचे वाली पंक्ति में पसंदीदा ऐप्लिकेशन के सुझाव पाएं"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"सिस्टम नेविगेशन सेटिंग"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"शेयर करें"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"स्क्रीनशॉट लें"</string>
+    <string name="action_split" msgid="2098009717623550676">"स्प्लिट स्क्रीन मोड"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"स्प्लिट स्क्रीन मोड के लिए, दूसरे ऐप पर टैप करें"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"ऐप्लिकेशन या आपका संगठन इस कार्रवाई की अनुमति नहीं देता"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"नेविगेशन ट्यूटोरियल छोड़ना चाहते हैं?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"आप बाद में <xliff:g id="NAME">%1$s</xliff:g> ऐप्लिकेशन पर इसे देख सकते हैं"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"अभी नहीं"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"अभी नहीं"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"टास्कबार ट्यूटोरियल दिखाया गया"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"टास्कबार ट्यूटोरियल बंद किया गया"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"टास्कबार को छिपाने के लिए, उसे दबाकर रखें"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"आगे बढ़ें"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"वापस जाएं"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"बंद करें"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"हो गया"</string>
 </resources>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index 50efb74..2b31943 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Još <xliff:g id="TIME">%1$s</xliff:g> danas"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Prijedlozi aplikacija"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Sve aplikacije"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Vaše predviđene aplikacije"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Primajte prijedloge aplikacija u donjem retku početnog zaslona"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Primajte prijedloge aplikacija u retku omiljenih na početnom zaslonu"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Postavke navigacije sustavom"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Podijeli"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Snimka zaslona"</string>
+    <string name="action_split" msgid="2098009717623550676">"Podijeli"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Dodirnite drugu aplikaciju za podijeljeni zaslon"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Aplikacija ili vaša organizacija ne dopuštaju ovu radnju"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Želite li preskočiti vodič za kretanje?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Kasnije ga možete pronaći u aplikaciji <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Odustani"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Preskoči"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Upute za programsku traku su se pojavile"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Upute za programsku traku su zatvorene"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Dodirnite i zadržite da biste sakrili programsku traku"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Dalje"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Natrag"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Zatvori"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Gotovo"</string>
 </resources>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index e9bd2bf..06cfb71 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 perc"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Ma még <xliff:g id="TIME">%1$s</xliff:g> van hátra"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Alkalmazásjavaslatok"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Az összes alkalmazás"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Várható alkalmazások"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Alkalmazásjavaslatokat kaphat a kezdőképernyő alsó sorában"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Alkalmazásjavaslatokat kaphat a kezdőképernyőn megjelenő kedvencek sorában"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Rendszer-navigációs beállítások"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Megosztás"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Képernyőkép"</string>
+    <string name="action_split" msgid="2098009717623550676">"Felosztás"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Koppintson másik appra a képernyőmegosztáshoz"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Az alkalmazás vagy az Ön szervezete nem engedélyezi ezt a műveletet"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Kihagyja a navigáció bemutatóját?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Ezt később megtalálhatja a(z) <xliff:g id="NAME">%1$s</xliff:g> alkalmazásban"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Mégse"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Kihagyás"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Az eszköztár használatát ismertető panel megjelent"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Az eszköztár használatát ismertető panel bezárult"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Nyomva tartással elrejthető az eszköztár"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Tovább"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Vissza"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Bezárás"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Kész"</string>
 </resources>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index e2c0af3..18a2ddd 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 ր"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Այսօր մնացել է՝ <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Առաջարկվող հավելվածներ"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Բոլոր հավելվածները"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Ձեր կանխատեսված հավելվածները"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Ստացեք հավելվածների առաջարկներ հիմնական էկրանի ներքևում"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Ստացեք հավելվածների առաջարկներ հիմնական էկրանի «Ընտրանի» տողում"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Նավիգացիայի համակարգային կարգավորումներ"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Կիսվել"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Սքրինշոթ անել"</string>
+    <string name="action_split" msgid="2098009717623550676">"Տրոհել"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Հպեք այլ հավելվածի՝ էկրանը տրոհելու համար"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Այս գործողությունն արգելված է հավելվածի կամ ձեր կազմակերպության կողմից"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Բաց թողնե՞լ նավիգացիայի ուղեցույցը"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Հետագայում սա կարող եք գտնել «<xliff:g id="NAME">%1$s</xliff:g>» հավելվածում"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Չեղարկել"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Բաց թողնել"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Խնդրագոտու «Կրթություն» վահանակը բացվեց"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Խնդրագոտու «Կրթություն» վահանակը փակվեց"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Հպեք և պահեք՝ խնդրագոտին թաքցնելու համար"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Առաջ"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Հետ"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Փակել"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Պատրաստ է"</string>
 </resources>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index 3e24fcb..5854104 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 menit"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> tersisa hari ini"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Saran aplikasi"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Semua aplikasi"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Aplikasi yang diprediksi"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Dapatkan saran aplikasi di baris paling bawah Layar utama"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Dapatkan saran aplikasi di baris favorit Layar utama"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Setelan navigasi sistem"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Bagikan"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
+    <string name="action_split" msgid="2098009717623550676">"Pisahkan"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Ketuk aplikasi lain untuk menggunakan layar terpisah"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Tindakan ini tidak diizinkan oleh aplikasi atau organisasi Anda"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Lewati tutorial navigasi?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Anda dapat menemukan tutorial ini di lain waktu di aplikasi <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Batal"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Lewati"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Edukasi taskbar ditampilkan"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Edukasi taskbar ditutup"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Sentuh lama untuk menyembunyikan taskbar"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Berikutnya"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Kembali"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Tutup"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Selesai"</string>
 </resources>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index eeebc71..c5a47b1 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 mín."</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> eftir í dag"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Tillögur að forritum"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Öll forrit"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Spáð forrit"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Fáðu tillögur að forritum í neðstu röð heimaskjásins"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Fáðu tillögur að forritum á eftirlætissvæði heimaskjásins"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Stillingar kerfisstjórnunar"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Deila"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Skjámynd"</string>
+    <string name="action_split" msgid="2098009717623550676">"Skipta"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Ýttu á annað forrit til að nota skjáskiptingu"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Forritið eða fyrirtækið leyfir ekki þessa aðgerð"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Sleppa flettileiðsögn?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Þú getur fundið þetta síðar í forritinu <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Hætta við"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Sleppa"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Leiðsögn verkefnastiku sýnileg"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Leiðsögn verkefnastiku lokað"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Haltu inni til að fela verkefnastikuna"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Áfram"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Til baka"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Loka"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Lokið"</string>
 </resources>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index 5a33a38..2a5b87d 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Rimanente oggi: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"App suggerite"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Tutte le app"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"App previste per te"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Visualizza app suggerite nella riga inferiore della schermata Home"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Visualizza app suggerite nella riga dei Preferiti della schermata Home"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Impostazioni Navigazione del sistema"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Condividi"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
+    <string name="action_split" msgid="2098009717623550676">"Dividi"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Tocca un\'altra app per usare lo schermo diviso"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Questa azione non è consentita dall\'app o dall\'organizzazione"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Saltare il tutorial di navigazione?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Puoi trovarlo in un secondo momento nell\'app <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Annulla"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Salta"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Riquadro Formazione barra delle applicazioni visualizzato"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Riquadro Formazione barra delle applicazioni chiuso"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Tocca e tieni premuto per nascondere barra applicazioni"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Avanti"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Indietro"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Chiudi"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Fine"</string>
 </resources>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index 2677690..467f5b9 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"‏&lt; דקה"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"הזמן שנותר להיום: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"הצעות לאפליקציות"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"כל האפליקציות"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"האפליקציות החזויות שלך"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"קבלת הצעות לאפליקציות בשורה התחתונה של מסך הבית"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"קבלת הצעות לאפליקציות בשורת המועדפות של מסך הבית"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"הגדרות הניווט של המערכת"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"שיתוף"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"צילום מסך"</string>
+    <string name="action_split" msgid="2098009717623550676">"פיצול"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"צריך להקיש על אפליקציה אחרת כדי להשתמש במסך מפוצל"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"האפליקציה או הארגון שלך אינם מתירים את הפעולה הזאת"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"לדלג על המדריך לניווט?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"ניתן למצוא את המדריך מאוחר יותר באפליקציה <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"ביטול"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"דילוג"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"חלונית ההסברים על שורת המשימות מופיעה"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"חלונית ההסברים על שורת המשימות נסגרה"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"כדי להסתיר את שורת המשימות, לוחצים לחיצה ארוכה"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"הבא"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"חזרה"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"סגירה"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"סיום"</string>
 </resources>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index 9c2adef..6b73a9b 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"1 分未満"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"今日はあと <xliff:g id="TIME">%1$s</xliff:g>です"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"アプリの候補"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"すべてのアプリ"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"予測されたアプリ"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"ホーム画面の一番下にアプリの候補を表示できます"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"ホーム画面のお気に入りの行でアプリの候補を利用できます"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"システム ナビゲーションの設定"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"共有"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"スクリーンショット"</string>
+    <string name="action_split" msgid="2098009717623550676">"分割"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"分割画面を使用するには、他のアプリをタップします"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"この操作はアプリまたは組織で許可されていません"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"操作チュートリアルをスキップしますか?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"チュートリアルは後から <xliff:g id="NAME">%1$s</xliff:g> アプリで確認できます"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"キャンセル"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"スキップ"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"タスクバーの説明を開きました"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"タスクバーの説明を閉じました"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"タスクバーを長押しすると非表示になります"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"次へ"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"戻る"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"閉じる"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"完了"</string>
 </resources>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index d9420c8..22ead5c 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 წუთი"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"დღეს დარჩენილია <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"აპის შემოთავაზებები"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"ყველა აპი"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"თქვენი პროგნოზირებული აპები"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"მიიღეთ აპის შეთავაზებები მთავარი ეკრანის ქვედა რიგში"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"მიიღეთ აპების შემოთავაზებები მთავარი ეკრანის რჩეულების მწკრივში"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"სისტემის ნავიგაციის პარამეტრები"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"გაზიარება"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ეკრანის ანაბეჭდი"</string>
+    <string name="action_split" msgid="2098009717623550676">"გაყოფა"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"შეეხეთ სხვა აპს ეკრანის გასაყოფად"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"ეს მოქმედება არ არის დაშვებული აპის ან თქვენი ორგანიზაციის მიერ"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"გსურთ, გამოტოვოთ ნავიგაციის სახელმძღვანელო?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"ამის მოგვიანებით პოვნა <xliff:g id="NAME">%1$s</xliff:g> აპში შეგიძლიათ"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"გაუქმება"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"გამოტოვება"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ამოცანების ზოლის სასწავლო არე გამოჩნდა"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ამოცანების ზოლის სასწავლო არე დაიხურა"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"ხანგრძლივად შეეხეთ ამოცანების ზოლის დასამალად"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"შემდეგი"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"უკან"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"დახურვა"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"მზადაა"</string>
 </resources>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index d762bd8..cd86af4 100644
--- a/quickstep/res/values-kk/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 мин"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Бүгін <xliff:g id="TIME">%1$s</xliff:g> қалды"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Ұсынылған қолданбалар"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Барлық қолданба"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Ұсынылатын қолданбалар"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Негізгі экранның төменгі жолында қолданбаларды ұсыну"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Ұсынылған қолданбалар негізгі экранда таңдаулылар арасында көрсетілетін болады"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Навигацияның жүйелік параметрлері"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Бөлісу"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</string>
+    <string name="action_split" msgid="2098009717623550676">"Бөлу"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Экранды бөлу режимін пайдалану үшін басқа қолданбаны түртіңіз."</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Бұл әрекетке қолданба не ұйым рұқсат етпейді."</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Қимылдар оқулығын өткізіп жіберу керек пе?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Мұны кейін <xliff:g id="NAME">%1$s</xliff:g> қолданбасынан таба аласыз."</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Бас тарту"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Өткізіп жіберу"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Тапсырмалар тақтасы бойынша нұсқаулық ашылды."</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Тапсырмалар тақтасы бойынша нұсқаулық жабылды."</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Тапсырмалар тақтасын жасыру үшін басып тұрыңыз."</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Келесі"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Артқа"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Жабу"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Дайын"</string>
 </resources>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 2700b83..1e677c2 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 នាទី"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"នៅសល់ <xliff:g id="TIME">%1$s</xliff:g> ទៀត​នៅថ្ងៃនេះ"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"ការណែនាំកម្មវិធី"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"កម្មវិធី​ទាំងអស់"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"កម្មវិធី​ដែលបាន​ព្យាករ​របស់អ្នក"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"ទទួលបាន​ការណែនាំកម្មវិធី​នៅជួរខាងក្រោម​នៃអេក្រង់ដើម​របស់អ្នក"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"ទទួលបាន​ការណែនាំកម្មវិធី​នៅលើ​ជួរដេកសំណព្វ​នៃអេក្រង់ដើម​របស់អ្នក"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ការកំណត់​ការរុករក​ប្រព័ន្ធ"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"ចែករំលែក"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"រូបថតអេក្រង់"</string>
+    <string name="action_split" msgid="2098009717623550676">"បំបែក"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"ចុចកម្មវិធី​ផ្សេងទៀត ដើម្បីប្រើមុខងារ​បំបែកអេក្រង់"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"សកម្មភាពនេះ​មិនត្រូវបានអនុញ្ញាតដោយកម្មវិធី​ ឬ​ស្ថាប័ន​របស់អ្នកទេ"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"រំលង​មេរៀន​អំពី​ការរុករក​ឬ?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"អ្នកអាចស្វែងរកមេរៀននេះនៅពេលក្រោយក្នុងកម្មវិធី <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"បោះបង់"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"រំលង"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ការបង្រៀនអំពីរបារកិច្ចការបានបង្ហាញ"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ការបង្រៀនអំពីរបារកិច្ចការត្រូវបានបិទ"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"ចុចឱ្យជាប់ ដើម្បីលាក់របារកិច្ចការ"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"បន្ទាប់"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"ថយក្រោយ"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"បិទ"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"រួចរាល់"</string>
 </resources>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index 9b981c2..fa3f1f6 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 ನಿ"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"ಇಂದು <xliff:g id="TIME">%1$s</xliff:g> ಸಮಯ ಉಳಿದಿದೆ"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"ಆ್ಯಪ್ ಸಲಹೆಗಳು"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳು"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"ನಿಮ್ಮ ಮುನ್ಸೂಚಿತ ಆ್ಯಪ್‌ಗಳು"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್‌ನ ಕೆಳಭಾಗದ ಸಾಲಿನಲ್ಲಿ ಆ್ಯಪ್ ಸಲಹೆಗಳನ್ನು ಪಡೆಯಿರಿ"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್‌ನ ಮೆಚ್ಚಿನವುಗಳ ಸಾಲಿನಲ್ಲಿ ಆ್ಯಪ್ ಸಲಹೆಗಳನ್ನು ಪಡೆಯಿರಿ"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ಸಿಸ್ಟಂ ನ್ಯಾವಿಗೇಶನ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"ಹಂಚಿಕೊಳ್ಳಿ"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್"</string>
+    <string name="action_split" msgid="2098009717623550676">"ವಿಭಜಿಸಿ"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಬಳಸಲು ಬೇರೊಂದು ಆ್ಯಪ್ ಮೇಲೆ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"ಆ್ಯಪ್ ಅಥವಾ ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ಈ ಕ್ರಿಯೆಯನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"ನ್ಯಾವಿಗೇಷನ್ ಟ್ಯುಟೋರಿಯಲ್ ಸ್ಕಿಪ್ ಮಾಡಿ?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"<xliff:g id="NAME">%1$s</xliff:g> ಆ್ಯಪ್‌ನಲ್ಲಿ ಇದನ್ನು ನಂತರ ಕಾಣಬಹುದು"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"ರದ್ದುಮಾಡಿ"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"ಸ್ಕಿಪ್ ಮಾಡಿ"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ಟಾಸ್ಕ್‌ಬಾರ್ ಶಿಕ್ಷಣ ಕಾಣಿಸಿಕೊಂಡಿದೆ"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ಟಾಸ್ಕ್‌ಬಾರ್ ಶಿಕ್ಷಣ ಮುಚ್ಚಿದೆ"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"ಟಾಸ್ಕ್‌ಬಾರ್ ಅನ್ನು ಮರೆಮಾಡಲು ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹೋಲ್ಡ್ ಮಾಡಿ"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"ಮುಂದೆ"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"ಹಿಂದೆ"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"ಮುಚ್ಚಿರಿ"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"ಮುಗಿದಿದೆ"</string>
 </resources>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index ba564a7..085f194 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1분"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"오늘 <xliff:g id="TIME">%1$s</xliff:g> 남음"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"앱 제안"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"모든 앱"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"추천 앱"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"홈 화면 하단에서 앱 제안 보기"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"홈 화면의 즐겨찾기 행에서 앱 제안 보기"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"시스템 탐색 설정"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"공유"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"스크린샷"</string>
+    <string name="action_split" msgid="2098009717623550676">"분할"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"다른 앱을 탭하여 화면 분할 사용"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"이 작업은 앱 또는 조직에서 허용되지 않습니다."</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"이동 방법 튜토리얼을 건너뛰시겠습니까?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"이 튜토리얼은 <xliff:g id="NAME">%1$s</xliff:g> 앱에서 다시 볼 수 있습니다"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"취소"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"건너뛰기"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"작업 표시줄 튜토리얼 패널 표시됨"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"작업 표시줄 튜토리얼 패널 닫힘"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"작업 표시줄을 숨기려면 길게 터치하세요."</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"다음"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"뒤로"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"닫기"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"완료"</string>
 </resources>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index 8be5c53..ba23770 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 мүнөт"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Бүгүн <xliff:g id="TIME">%1$s</xliff:g> мүнөт калды"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Сунушталган колдонмолор"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Бардык колдонмолор"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Божомолдонгон колдонмолоруңуз"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Сунушталган колдонмолор башкы экрандын ылдый жагында көрүнөт."</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Сунушталган колдонмолор башкы экрандагы тандалмалардын катарында көрүнөт."</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Өтүү аракетинин тутумдук жөндөөлөрү"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Бөлүшүү"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</string>
+    <string name="action_split" msgid="2098009717623550676">"Бөлүү"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Экранды бөлүү үчүн башка колдонмону таптап коюңуз"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Бул аракетти аткарууга колдонмо же ишканаңыз тыюу салган"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Жаңсоолор үйрөткүчүн өткөрүп жибересизби?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Аны кийин <xliff:g id="NAME">%1$s</xliff:g> колдонмосунан табасыз"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Жокко чыгаруу"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Өткрп жиберүү"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Тапшырмалар тактасынын окутуу панели көрсөтүлдү"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Тапшырмалар тактасынын окутуу панели жабылды"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Тапшырмалар тактасын жашыруу үчүн коё бербей басып туруңуз"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Кийинки"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Артка"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Жабуу"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Бүттү"</string>
 </resources>
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index 915578b..796de14 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 ນາທີ"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"ເຫຼືອ <xliff:g id="TIME">%1$s</xliff:g> ມື້ນີ້"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"ການແນະນຳແອັບ"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"ແອັບທັງໝົດ"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"ແອັບທີ່ຄາດເດົາໄວ້ແລ້ວຂອງທ່ານ"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"ຮັບການແນະນຳແອັບຢູ່ແຖວລຸ່ມສຸດຂອງໜ້າຈໍຫຼັກທ່ານ"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"ຮັບການແນະນຳແອັບຢູ່ແຖວລາຍການທີ່ມັກຂອງໜ້າຈໍຫຼັກຂອງທ່ານ"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ການຕັ້ງຄ່າການນຳທາງລະບົບ"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"ແບ່ງປັນ"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ຮູບໜ້າຈໍ"</string>
+    <string name="action_split" msgid="2098009717623550676">"ແບ່ງ"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"ແຕະແອັບອື່ນເພື່ອໃຊ້ການແຍກໜ້າຈໍ"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"ແອັບ ຫຼື ອົງການຂອງທ່ານບໍ່ອະນຸຍາດໃຫ້ໃຊ້ຄຳສັ່ງນີ້"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"ຂ້າມການສອນການນຳໃຊ້ການນຳທາງບໍ?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"ທ່ານສາມາດຊອກສ່ວນນີ້ພາຍຫຼັງໄດ້ໃນແອັບ <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"ຍົກເລີກ"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"ຂ້າມ"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ສະແດງການສຶກສາແຖບໜ້າວຽກແລ້ວ"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ປິດການສຶກສາແຖບໜ້າວຽກແລ້ວ"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"ແຕະຄ້າງໄວ້ເພື່ອເຊື່ອງແຖບໜ້າວຽກ"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"ຕໍ່ໄປ"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"ກັບຄືນ"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"ປິດ"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"ແລ້ວໆ"</string>
 </resources>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index b0c679c..e20a780 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min."</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Šiandien liko: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Programų pasiūlymai"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Visos programos"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Numatomos programos"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Gaukite programų pasiūlymų apatinėje pagrindinio ekrano eilutėje"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Gaukite programų pasiūlymų pagrindinio ekrano eilutėje „Mėgstamiausios“"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sistemos naršymo nustatymai"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Bendrinti"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Ekrano kopija"</string>
+    <string name="action_split" msgid="2098009717623550676">"Išskaidymo režimas"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Pal. kitą progr., kad gal. naud. išsk. ekr. rež."</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Jūsų organizacijoje arba naudojant šią programą neleidžiama atlikti šio veiksmo"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Praleisti naršymo mokymo programą?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Tai galėsite rasti vėliau programoje „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Atšaukti"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Praleisti"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Užduočių juostos patarimai rodomi"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Užduočių juostos patarimai uždaryti"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Palieskite ir palaikykite, kad paslėptumėte užduočių juostą"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Kitas"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Atgal"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Uždaryti"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Atlikta"</string>
 </resources>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index df1c3e9..148102f 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt;1 minūte"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Šodien atlicis: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Ieteicamās lietotnes"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Visas lietotnes"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Jūsu prognozētās lietotnes"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Sākuma ekrāna apakšējā rindā tiks rādītas ieteicamās lietotnes"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Saņemiet lietotņu ieteikumus izlases rindā sākuma ekrānā"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sistēmas navigācijas iestatījumi"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Kopīgot"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Veikt ekrānuzņēmumu"</string>
+    <string name="action_split" msgid="2098009717623550676">"Sadalīt"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Piesk. citai lietotnei, lai izm. ekrāna sadalīšanu"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Lietotne vai jūsu organizācija neatļauj veikt šo darbību."</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Vai izlaist navigācijas mācības?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Varēsiet to vēlāk atrast lietotnē <xliff:g id="NAME">%1$s</xliff:g>."</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Atcelt"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Izlaist"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Tika atvērta uzdevumjoslas apmācība"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Tika aizvērta uzdevumjoslas apmācība"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Pieskarieties un turiet, lai paslēptu uzdevumjoslu."</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Tālāk"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Atpakaļ"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Aizvērt"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Gatavs"</string>
 </resources>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index 98307da..e1bc7a6 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 минута"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Уште <xliff:g id="TIME">%1$s</xliff:g> за денес"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Предлози за апликации"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Сите апликации"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Вашите предвидени апликации"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Добивајте предлози за апликации на долниот ред од почетниот екран"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Добивајте предлози за апликации во редот со омилени на почетниот екран"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Поставки за системска навигација"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Сподели"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Слика од екранот"</string>
+    <string name="action_split" msgid="2098009717623550676">"Раздели"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Допрете друга апликација за да користите поделен екран"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Апликацијата или вашата организација не го дозволува дејствово"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Да се прескокне упатството за навигација?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Ова може да го најдете подоцна во апликацијата <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Откажи"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Прескокни"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Се појави лентата за задачи за образование"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Затворена е лентата за задачи за образование"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Допрете и задржете за да се сокрие лентата за задачи"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Следна"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Назад"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Затвори"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Готово"</string>
 </resources>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index 52be3ac..7342c3c 100644
--- a/quickstep/res/values-ml/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 മിനിറ്റ്"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"ഇന്ന് <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"ആപ്പ് നിർദ്ദേശങ്ങൾ"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"എല്ലാ ആപ്പുകളും"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"നിങ്ങളുടെ പ്രവചിക്കപ്പെട്ട ആപ്പുകൾ"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"നിങ്ങളുടെ ഹോം സ്‌ക്രീനിന്റെ താഴത്തെ നിരയിൽ ആപ്പ് നിർദ്ദേശങ്ങൾ നേടുക"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"നിങ്ങളുടെ ഹോം സ്‌ക്രീനിന്റെ \'പ്രിയപ്പെട്ടവ\' വരിയിൽ ആപ്പ് നിർദ്ദേശങ്ങൾ നേടുക"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"സിസ്‌റ്റം നാവിഗേഷൻ ക്രമീകരണം"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"പങ്കിടുക"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"സ്ക്രീൻഷോട്ട്"</string>
+    <string name="action_split" msgid="2098009717623550676">"വിഭജിക്കുക"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"സ്പ്ലിറ്റ് സ്ക്രീനിനായി മറ്റൊരു ആപ്പ് ടാപ്പുചെയ്യൂ"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"ഈ നടപടി എടുക്കുന്നത് ആപ്പോ നിങ്ങളുടെ സ്ഥാപനമോ അനുവദിക്കുന്നില്ല"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"നാവിഗേഷൻ ട്യൂട്ടോറിയൽ ഒഴിവാക്കണോ?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"<xliff:g id="NAME">%1$s</xliff:g> ആപ്പിൽ നിങ്ങൾക്ക് ഇത് പിന്നീട് കാണാനാകും"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"റദ്ദാക്കുക"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"ഒഴിവാക്കുക"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ടാസ്ക്ക്ബാർ വിവര പാനൽ ദൃശ്യമായി"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ടാസ്ക്ക്ബാർ വിവര പാനൽ അടച്ചു"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"ടാസ്ക്ക്ബാർ മറയ്ക്കാൻ സ്‌പർശിച്ച് പിടിക്കുക"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"അടുത്തത്"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"മടങ്ങുക"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"അടയ്ക്കുക"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"പൂർത്തിയായി"</string>
 </resources>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index 79fbca6..78ddb56 100644
--- a/quickstep/res/values-mn/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 минут"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Өнөөдөр <xliff:g id="TIME">%1$s</xliff:g> үлдсэн"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Санал болгож буй аппууд"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Бүх апп"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Таны таамагласан аппууд"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Үндсэн нүүрнийхээ доод мөрд санал болгож буй аппуудыг аваарай"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Үндсэн нүүрний дуртай мөрнөөсөө санал болгож буй аппуудыг аваарай"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Системийн навигацын тохиргоо"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Хуваалцах"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Дэлгэцийн агшин дарах"</string>
+    <string name="action_split" msgid="2098009717623550676">"Хуваах"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Дэлгэц хуваахыг ашиглах бол өөр аппыг товшино уу"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Энэ үйлдлийг апп эсвэл танай байгууллага зөвшөөрдөггүй"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Навигацын практик хичээлийг алгасах уу?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Та үүнийг дараа нь <xliff:g id="NAME">%1$s</xliff:g> аппаас олох боломжтой"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Цуцлах"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Алгасах"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Боловсролын ажлын талбар гарч ирсэн"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Боловсролын ажлын талбарыг хаасан"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Ажлын талбарыг нуухын тулд хүрээд удаан дарна уу"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Дараах"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Буцах"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Хаах"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Дууссан"</string>
 </resources>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index 176dc3a..df94447 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"१मिहून कमी"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"आज <xliff:g id="TIME">%1$s</xliff:g>शिल्लक आहे"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"अ‍ॅप सूचना"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"सर्व अ‍ॅप्स"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"तुमची पूर्वानुमानीत अ‍ॅप्स"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"तुमच्या होम स्क्रीनच्या तळाशी अ‍ॅप सूचना मिळवा"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"तुमच्या होम स्क्रीनच्या पसंतीच्या पंक्तीवर अ‍ॅप सूचना मिळवा"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"सिस्टम नेव्हिगेशन सेटिंग्ज"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"शेअर करा"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"स्क्रीनशॉट"</string>
+    <string name="action_split" msgid="2098009717623550676">"स्प्लिट"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"स्प्लिटस्क्रीन वापरण्यासाठी दुसऱ्या ॲपवर टॅप करा"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"अ‍ॅप किंवा तुमच्या संस्थेद्वारे ही क्रिया करण्याची अनुमती नाही"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"नेव्हिगेशन ट्यूटोरियल वगळायचे आहे का?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"तुम्हाला हे नंतर <xliff:g id="NAME">%1$s</xliff:g> ॲपमध्ये मिळेल"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"रद्द करा"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"वगळा"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"टास्कबारशी संबंधित माहिती देणारे पॅनल उघडले आहे"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"टास्कबारशी संबंधित माहिती देणारे पॅनल बंद केले आहे"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"टास्कबार लपवण्यासाठी स्पर्श करा आणि धरून ठेवा"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"पुढे"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"मागे जा"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"बंद करा"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"पूर्ण झाले"</string>
 </resources>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index 1e2d43d..ba7a15e 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minit"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> lagi hari ini"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Cadangan apl"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Semua apl"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Apl ramalan anda"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Dapatkan cadangan apl di baris bawah Skrin utama anda"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Dapatkan cadangan apl di baris kegemaran Skrin utama anda"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Tetapan navigasi sistem"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Kongsi"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Tangkapan skrin"</string>
+    <string name="action_split" msgid="2098009717623550676">"Pisah"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Ketik apl lain untuk menggunakan skrin pisah"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Tindakan ini tidak dibenarkan oleh apl atau organisasi anda"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Langkau tutorial navigasi?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Anda boleh mendapatkan tutorial ini kemudian dalam apl <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Batal"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Langkau"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Pendidikan bar tugas muncul"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Pendidikan bar tugas ditutup"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Sentuh &amp; tahan untuk menyembunyikan bar tugas"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Seterusnya"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Kembali"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Tutup"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Selesai"</string>
 </resources>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index b07bbfa..17cd498 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; ၁ မိနစ်"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"ယနေ့ <xliff:g id="TIME">%1$s</xliff:g> ခု ကျန်သည်"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"အက်ပ်အကြံပြုချက်များ"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"အက်ပ်အားလုံး"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"သင်၏ ခန့်မှန်းအက်ပ်များ"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"သင်၏ \'ပင်မစာမျက်နှာ\' အောက်ခြေအတန်းတွင် အက်ပ်အကြံပြုချက်များ ရယူခြင်း"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"သင်၏ \'ပင်မစာမျက်နှာ\' ၏ အနှစ်သက်ဆုံးများအတန်းတွင် အက်ပ်အကြံပြုချက်များ ရယူခြင်း"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"စနစ် လမ်းညွှန် ဆက်တင်များ"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"မျှဝေရန်"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ဖန်သားပြင်ဓာတ်ပုံ"</string>
+    <string name="action_split" msgid="2098009717623550676">"ခွဲထုတ်ရန်"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"မျက်နှာပြင်ခွဲ၍ပြသရန် အက်ပ်နောက်တစ်ခုကို တို့ပါ"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"ဤလုပ်ဆောင်ချက်ကို အက်ပ် သို့မဟုတ် သင်၏အဖွဲ့အစည်းက ခွင့်မပြုပါ"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"လမ်းညွှန်ခြင်း ရှင်းလင်းပို့ချချက်ကို ကျော်မလား။"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"၎င်းကို နောက်မှ <xliff:g id="NAME">%1$s</xliff:g> အက်ပ်တွင် ရှာနိုင်သည်"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"မလုပ်တော့"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"ကျော်ရန်"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ပညာရေး လုပ်ဆောင်စရာဘား ပြထားသည်"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ပညာရေး လုပ်ဆောင်စရာဘား ပိတ်ထားသည်"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"လုပ်ဆောင်စရာဘားကို ဖျောက်ရန် ထိပြီးဖိထားနိုင်သည်"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"ရှေ့သို့"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"နောက်သို့"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"ပိတ်ရန်"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"ပြီးပြီ"</string>
 </resources>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index 16988c5..6e43cfb 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minutt"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> gjenstår i dag"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Appanbefalinger"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Alle apper"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Forslag til apper"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Få appforslag i den nederste raden på startskjermen"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Få appforslag i favoritter-raden på startskjermen"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Innstillinger for systemnavigasjon"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Del"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Skjermdump"</string>
+    <string name="action_split" msgid="2098009717623550676">"Del opp"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Trykk på en annen app for å bruke delt skjerm"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Appen eller organisasjonen din tillater ikke denne handlingen"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Vil du hoppe over navigeringsveiledning?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Du kan finne dette i <xliff:g id="NAME">%1$s</xliff:g>-appen senere"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Avbryt"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Hopp over"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Opplæringen for oppgavelinjen vises"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Opplæringen for oppgavelinjen er lukket"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Trykk og hold for å skjule oppgavelinjen"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Neste"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Tilbake"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Lukk"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Ferdig"</string>
 </resources>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index 91cae47..f827bcc 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; १ मिनेट"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"आज: <xliff:g id="TIME">%1$s</xliff:g> बाँकी"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"एपसम्बन्धी सुझावहरू"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"सबै एपहरू"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"तपाईंलाई चाहिने एपहरू"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"आफ्नो होम स्क्रिनको पुछारको रोमा एपसम्बन्धी सिफारिस प्राप्त गर्नुहोस्"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"आफ्नो होम स्क्रिनको मन पर्ने नामक पङ्क्तिमा एपसम्बन्धी सिफारिस प्राप्त गर्नुहोस्"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"सिस्टम नेभिगेसनसम्बन्धी सेटिङ"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"सेयर गर्नुहोस्"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"स्क्रिनसट"</string>
+    <string name="action_split" msgid="2098009717623550676">"स्प्लिट गर्नुहोस्"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"स्प्लिटक्रिन प्रयोग गर्न अर्को एपमा ट्याप गर्नुहोस्"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"यो एप वा तपाईंको सङ्गठनले यो कारबाही गर्ने अनुमति दिँदैन"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"नेभिगेसन ट्युटोरियल स्किप गर्ने हो?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"तपाईं पछि <xliff:g id="NAME">%1$s</xliff:g> नामक एपमा गई यो ट्युटोरियल भेट्टाउन सक्नुहुन्छ"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"रद्द गर्नुहोस्"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"स्किप गर्नु…"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"टास्कबार एजुकेसन देखिएको छ"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"टास्कबार एजुकेसन बन्द गरिएको छ"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"टास्कबार लुकाउन टास्कबार थिचिराख्नुहोस्"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"अर्को"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"पछाडि"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"बन्द गर्नुहोस्"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"सम्पन्न भयो"</string>
 </resources>
diff --git a/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml b/quickstep/res/values-night/colors.xml
similarity index 62%
copy from quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml
copy to quickstep/res/values-night/colors.xml
index 9c95497..c3b2536 100644
--- a/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml
+++ b/quickstep/res/values-night/colors.xml
@@ -1,17 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2020 The Android Open Source Project
+<!-- 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.
 -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-  <solid android:color="@color/gesture_tutorial_fake_previous_task_view_color" />
-</shape>
+<resources>
+
+    <color name="gesture_tutorial_back_arrow_color">#99000000</color>
+
+    <color name="gesture_tutorial_fake_wallpaper_color">#000000</color> <!-- Black -->
+
+    <color name="mock_webpage_url_bar">#202124</color>
+    <color name="mock_webpage_url_bar_item">#3c4043</color>
+
+</resources>
\ No newline at end of file
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index 96da966..81cda4d 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minuut"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Nog <xliff:g id="TIME">%1$s</xliff:g> vandaag"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"App-suggesties"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Alle apps"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Je voorspelde apps"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"App-suggesties ontvangen op de onderste rij van je startscherm"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"App-suggesties ontvangen op de rij met favorieten op het startscherm"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Navigatie-instellingen van systeem"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Delen"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
+    <string name="action_split" msgid="2098009717623550676">"Splitsen"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Tik op nog een app om je scherm te splitsen"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Deze actie wordt niet toegestaan door de app of je organisatie"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Navigatietutorial overslaan?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Je vindt dit later terug in de app <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Annuleren"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Overslaan"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Uitleg van taakbalk geopend"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Uitleg van taakbalk gesloten"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Tik en houd vast om de taakbalk te verbergen"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Volgende"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Terug"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Sluiten"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Klaar"</string>
 </resources>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index 9488021..3535427 100644
--- a/quickstep/res/values-or/strings.xml
+++ b/quickstep/res/values-or/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 ମିନିଟ୍"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"ଆଜି <xliff:g id="TIME">%1$s</xliff:g> ବାକି ଅଛି"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକ"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"ସମସ୍ତ ଆପ୍ସ"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"ଆପଣ ପୂର୍ବାନୁମାନ କରିଥିବା ଆପ୍ସ"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"ଆପଣଙ୍କ ମୂଳ ସ୍କ୍ରିନର ତଳ ଧାଡ଼ିରେ ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକ ପାଆନ୍ତୁ"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"ଆପଣଙ୍କ ମୂଳ ସ୍କ୍ରିନର ପସନ୍ଦର ଧାଡ଼ିରେ ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକ ପାଆନ୍ତୁ"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ସିଷ୍ଟମ୍ ନାଭିଗେସନ୍ ସେଟିଂସ୍"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"ସେୟାର୍ କରନ୍ତୁ"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ସ୍କ୍ରିନସଟ୍"</string>
+    <string name="action_split" msgid="2098009717623550676">"ସ୍ପ୍ଲିଟ୍"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"ସ୍ପ୍ଲିଟସ୍କ୍ରିନ ବ୍ୟବହାର କରିବାକୁ ଅନ୍ୟ ଏକ ଆପରେ ଟାପ କର"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"ଆପ୍ କିମ୍ବା ଆପଣଙ୍କ ସଂସ୍ଥା ଦ୍ୱାରା ଏହି କାର୍ଯ୍ୟକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"ନାଭିଗେସନ୍ ଟ୍ୟୁଟୋରିଆଲକୁ ବାଦ୍ ଦେବେ?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"ଆପଣ ପରେ ଏହାକୁ <xliff:g id="NAME">%1$s</xliff:g> ଆପରେ ପାଇପାରିବେ"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"ବାତିଲ୍ କରନ୍ତୁ"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"ବାଦ୍ ଦିଅନ୍ତୁ"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ଟାସ୍କବାର୍ ଶିକ୍ଷା ଦେଖାଯାଇଛି"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ଟାସ୍କବାର୍ ଶିକ୍ଷା ବନ୍ଦ ହୋଇଯାଇଛି"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"ଟାସ୍କବାରକୁ ଲୁଚାଇବା ପାଇଁ ସ୍ପର୍ଶ କରି ଧରି ରଖନ୍ତୁ"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"ପରବର୍ତ୍ତୀ"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"ପଛକୁ ଫେରନ୍ତୁ"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"ବନ୍ଦ କରନ୍ତୁ"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"ହୋଇଗଲା"</string>
 </resources>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index 89b2119..5f070ce 100644
--- a/quickstep/res/values-pa/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 ਮਿੰਟ"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"ਅੱਜ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"ਐਪ ਸੁਝਾਅ"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"ਸਾਰੀਆਂ ਐਪਾਂ"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"ਤੁਹਾਡੀਆਂ ਪੂਰਵ ਅਨੁਮਾਨਿਤ ਐਪਾਂ"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"ਆਪਣੀ ਹੋਮ ਸਕ੍ਰੀਨ ਦੀ ਹੇਠਲੀ ਕਤਾਰ \'ਤੇ ਐਪ ਸੁਝਾਅ ਪ੍ਰਾਪਤ ਕਰੋ"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"ਆਪਣੀ ਹੋਮ ਸਕ੍ਰੀਨ ਦੀ ਮਨਪਸੰਦ ਕਤਾਰ \'ਤੇ ਐਪ ਸੁਝਾਅ ਹਾਸਲ ਕਰੋ"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ਸਿਸਟਮ ਨੈਵੀਗੇਸ਼ਨ ਸੈਟਿੰਗਾਂ"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"ਸਾਂਝਾ ਕਰੋ"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ਸਕ੍ਰੀਨਸ਼ਾਟ"</string>
+    <string name="action_split" msgid="2098009717623550676">"ਸਪਲਿਟ"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਵਰਤਣ ਲਈ ਕਿਸੇ ਹੋਰ ਐਪ \'ਤੇ ਟੈਪ ਕਰੋ"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"ਐਪ ਜਾਂ ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਇਸ ਕਾਰਵਾਈ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"ਕੀ ਨੈਵੀਗੇਸ਼ਨ ਟਿਊਟੋਰੀਅਲ ਨੂੰ ਛੱਡਣਾ ਹੈ?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"ਤੁਸੀਂ ਇਸਨੂੰ ਬਾਅਦ ਵਿੱਚ <xliff:g id="NAME">%1$s</xliff:g> ਐਪ ਵਿੱਚ ਲੱਭ ਸਕਦੇ ਹੋ"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"ਰੱਦ ਕਰੋ"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"ਛੱਡੋ"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ਟਾਸਕਵਾਰ ਸਿੱਖਿਆ ਪੈਨਲ ਦਿਖਾਇਆ ਗਿਆ"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ਟਾਸਕਵਾਰ ਸਿੱਖਿਆ ਪੈਨਲ ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"ਟਾਸਕਬਾਰ ਨੂੰ ਲੁਕਾਉਣ ਲਈ ਸਪਰਸ਼ ਕਰਕੇ ਰੱਖੋ"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"ਅੱਗੇ"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"ਪਿੱਛੇ"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"ਬੰਦ ਕਰੋ"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"ਹੋ ਗਿਆ"</string>
 </resources>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index 893db91..601aa4c 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&gt; 1 min"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Na dziś zostało <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Sugestie aplikacji"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Wszystkie aplikacje"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Przewidywane aplikacje"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Otrzymuj sugestie aplikacji w dolnym wierszu ekranu głównego"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Otrzymuj sugestie aplikacji w wierszu ulubionych na ekranie głównym"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Ustawienia nawigacji w systemie"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Udostępnij"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Zrzut ekranu"</string>
+    <string name="action_split" msgid="2098009717623550676">"Podziel"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Kliknij drugą aplikację, aby podzielić ekran"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Nie możesz wykonać tego działania, bo nie zezwala na to aplikacja lub Twoja organizacja"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Pominąć samouczek nawigacji?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Znajdziesz to później w aplikacji <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Anuluj"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Pomiń"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Wskazówki na temat paska zadań zostały wyświetlone"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Wskazówki na temat paska zadań zostały zamknięte"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Naciśnij i przytrzymaj, aby ukryć pasek zadań"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Dalej"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Wstecz"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Zamknij"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Gotowe"</string>
 </resources>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index c1cd2bb..3f03537 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minuto"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Resta(m) <xliff:g id="TIME">%1$s</xliff:g> hoje."</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Sugestões de apps"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Todas as apps"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"As suas apps previstas"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Obtenha sugestões de apps na última fila do ecrã principal"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Obtenha sugestões de apps na fila dos favoritos do ecrã principal"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Definições de navegação do sistema"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Partilhar"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Fazer captura de ecrã"</string>
+    <string name="action_split" msgid="2098009717623550676">"Dividir"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Toque noutra app para utilizar o ecrã dividido"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Esta ação não é permitida pela app ou a sua entidade."</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Ignorar o tutorial de navegação?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Pode encontrar isto mais tarde na app <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancelar"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Ignorar"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Informação da barra de tarefas apresentada"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Informação da barra de tarefas fechada"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Toque sem soltar para ocultar a barra de tarefas"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Seguinte"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Anterior"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Fechar"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Concluir"</string>
 </resources>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index 422d13a..d725576 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> restante(s) hoje"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Sugestões de apps"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Todos os apps"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Suas predições de apps"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Receba sugestões de apps na linha inferior da tela inicial"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Receba sugestões de apps na linha \"Favoritos\" da tela inicial"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Configurações de navegação do sistema"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Compartilhar"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Capturar tela"</string>
+    <string name="action_split" msgid="2098009717623550676">"Dividir"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Toque em outro app para dividir a tela"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Essa ação não é permitida pelo app ou pela organização"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Pular o tutorial de navegação?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Veja o tutorial mais tarde no app <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancelar"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Pular"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"As dicas sobre a barra de tarefas foram abertas"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"As dicas sobre a barra de tarefas foram fechadas"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Mantenha a barra de tarefas pressionada para ocultá-la"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Próxima"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Voltar"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Fechar"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Concluído"</string>
 </resources>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index a5e97e0..27fd511 100644
--- a/quickstep/res/values-ro/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minut"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Au mai rămas <xliff:g id="TIME">%1$s</xliff:g> astăzi"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Sugestii de aplicații"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Toate aplicațiile"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Aplicațiile estimate"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Primiți sugestii de aplicații în rândul de jos al ecranului de pornire"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Primiți sugestii de aplicații în rândul de preferințe al ecranului de pornire"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Setările de navigare ale sistemului"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Distribuiți"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Captură de ecran"</string>
+    <string name="action_split" msgid="2098009717623550676">"Împărțit"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Atingeți altă aplicație pentru a folosi ecranul împărțit"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Această acțiune nu este permisă de aplicație sau de organizația dvs."</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Omiteți tutorialul de navigare?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Îl puteți găsi mai târziu în aplicația <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Anulați"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Omiteți"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Panoul cu informații despre bara de activități s-a afișat"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Panoul cu informații despre bara de activități s-a închis"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Atingeți lung oricând pentru a ascunde bara de activități"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Înainte"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Înapoi"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Închideți"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Gata"</string>
 </resources>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index 7c4d485..ab8b8cf 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 мин."</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Осталось сегодня: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Рекомендуемые приложения"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Все приложения"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Ваши рекомендуемые приложения"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Рекомендуемые приложения будут появляться в нижнем ряду на главном экране"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Рекомендуемые приложения будут появляться в разделе избранных на главном экране"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Системные настройки навигации"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Поделиться"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</string>
+    <string name="action_split" msgid="2098009717623550676">"Разделить"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Для разделения экрана нажмите на другое приложение."</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Это действие заблокировано приложением или организацией."</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Пропустить руководство по жестам?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Его можно найти в приложении \"<xliff:g id="NAME">%1$s</xliff:g>\"."</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Отмена"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Пропустить"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Обучение по работе с панелью задач показано"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Обучение по работе с панелью задач скрыто"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Чтобы скрыть панель задач, коснитесь ее и удерживайте."</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Далее"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Назад"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Закрыть"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Готово"</string>
 </resources>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index 5f322e6..f286b66 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 විනාඩියක්"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"අද <xliff:g id="TIME">%1$s</xliff:g>ක් ඉතුරුයි"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"යෙදුම් යෝජනා"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"සියලු යෙදුම්"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"ඔබේ පුරෝකථන කළ යෙදුම්"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"ඔබගේ මුල් තිරයේ පහළ පේළියේ යෙදුම් යෝජනා ලබා ගන්න"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"ඔබේ මුල් තිරයේ ප්‍රියතම පේළියේ යෙදුම් යෝජනා ලබා ගන්න"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"පද්ධති සංචාලන සැකසීම්"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"බෙදා ගන්න"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"තිර රුව"</string>
+    <string name="action_split" msgid="2098009717623550676">"බෙදන්න"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"බෙදුම් තිරය භාවිත කිරීමට තවත් යෙදුමක් තට්ටු කරන්න"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"මෙම ක්‍රියාව යෙදුම හෝ ඔබේ සංවිධානය මගින් ඉඩ නොදේ"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"නිබන්ධනය සංචාලනය මඟ හරින්නද?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"ඔබට මෙය පසුව <xliff:g id="NAME">%1$s</xliff:g> යෙදුම තුළ සොයා ගත හැකිය"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"අවලංගු කරන්න"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"මඟ හරින්න"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"කාර්ය තීරු අධ්‍යාපනය දිස් විය"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"කාර්ය තීරු අධ්‍යාපනය වසා ඇත"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"කාර්ය තීරුව සැඟවීමට ස්පර්ශ කර අල්ලා ගෙන සිටින්න"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"ඊළඟ"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"ආපසු"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"වසන්න"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"නිමයි"</string>
 </resources>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index 799e941..ce71b88 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"Menej ako 1 minúta"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Dnes ešte zostáva: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Návrhy aplikácií"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Všetky aplikácie"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Vaše predpovedané aplikácie"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Nechajte si v spodnom riadku na ploche zobrazovať návrhy aplikácií"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Nechajte si na ploche na riadku obľúbených zobrazovať návrhy aplikácií"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Nastavenia navigácie systémom"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Zdieľať"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Snímka obrazovky"</string>
+    <string name="action_split" msgid="2098009717623550676">"Rozdeliť"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Rozdel. obrazovku spustíte klepnutím na inú aplik."</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Aplikácia alebo vaša organizácia túto akciu nepovoľuje"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Chcete preskočiť návod na navigáciu?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Tento návod nájdete v aplikácii <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Zrušiť"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Preskočiť"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Zobrazila sa výuka k hlavnému panelu"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Výuka k hlavnému panelu bola zatvorená"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Panel úloh skryjete pridržaním"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Ďalej"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Späť"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Zavrieť"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Hotovo"</string>
 </resources>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index 7e40277..13299f1 100644
--- a/quickstep/res/values-sl/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Danes je ostalo še <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Predlagane aplikacije"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Vse aplikacije"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Predvidene aplikacije"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Oglejte si predlagane aplikacije v spodnji vrstici začetnega zaslona"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Prejemajte predloge aplikacij v vrstici s priljubljenimi na začetnem zaslonu"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Nastavitve krmarjenja po sistemu"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Deli"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Posnetek zaslona"</string>
+    <string name="action_split" msgid="2098009717623550676">"Razdeli"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Za uporabo razdeljenega zaslona se dotaknite še ene aplikacije."</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Aplikacija ali vaša organizacija ne dovoljuje tega dejanja"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Želite preskočiti vadnico za krmarjenje?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"To lahko pozneje najdete v aplikaciji <xliff:g id="NAME">%1$s</xliff:g>."</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Prekliči"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Preskoči"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Poučni nasveti o opravilni vrstici so prikazani."</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Poučni nasveti o opravilni vrstici so zaprti."</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Pridržite, če želite opravilno vrstico skriti."</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Naprej"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Nazaj"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Zapri"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Končano"</string>
 </resources>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index 86528d2..771eb13 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minutë"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> të mbetura sot"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Aplikacionet e sugjeruara"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Të gjitha aplikacionet"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Aplikacionet e tua të parashikuara"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Merr aplikacione të sugjeruara në rreshtin e poshtëm të ekranit tënd bazë"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Merr aplikacione të sugjeruara në rreshtin e të preferuarave të ekranit tënd bazë"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Cilësimet e navigimit të sistemit"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Ndaj"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Pamja e ekranit"</string>
+    <string name="action_split" msgid="2098009717623550676">"Ndaj"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Trokit aplikacion tjetër e përdor ekranin e ndarë"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Ky veprim nuk lejohet nga aplikacioni ose organizata jote"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Të kapërcehet udhëzuesi i navigimit?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Këtë mund ta gjesh më vonë tek aplikacioni <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Anulo"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Kapërce"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Edukimi i shiritit të detyrave u shfaq"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Edukimi nga shiriti i detyrave u mbyll"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Preke dhe mbaje prekur për ta fshehur shiritin e detyrave"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Para"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Pas"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Mbyll"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"U krye"</string>
 </resources>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index f2ffa0a..73d3bfa 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 мин"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Још <xliff:g id="TIME">%1$s</xliff:g> данас"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Предлози апликација"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Све апликације"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Предвиђене апликације"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Добијајте предлоге апликација у доњем реду почетног екрана"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Добијајте предлоге апликација у реду са омиљеним ставкама на почетном екрану"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Подешавања кретања кроз систем"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Дели"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Снимак екрана"</string>
+    <string name="action_split" msgid="2098009717623550676">"Подели"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Додирните другу апликацију за подељени екран"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Апликација или организација не дозвољавају ову радњу"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Желите да прескочите водич за кретање?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Можете да пронађете ово касније у апликацији <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Откажи"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Прескочи"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Едукативно окно из траке задатака се појавило"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Едукативно окно из траке задатака је затворено"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Додирните и задржите за скривање траке задатака"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Даље"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Назад"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Затвори"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Готово"</string>
 </resources>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index 931e458..c741a39 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> kvar i dag"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Appförslag"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Alla appar"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Föreslagna appar"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Få appförslag på den nedersta raden på startskärmen"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Få appförslag på raden Favoriter på startskärmen"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Systemnavigeringsinställningar"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Dela"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Skärmbild"</string>
+    <string name="action_split" msgid="2098009717623550676">"Delat"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Tryck på en annan app för att använda delad skärm"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Appen eller organisationen tillåter inte den här åtgärden"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Vill du hoppa över självstudierna?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Du hittar det här igen i <xliff:g id="NAME">%1$s</xliff:g>-appen"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Avbryt"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Hoppa över"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Information om aktivitetsfältet visades"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Information om aktivitetsfältet stängdes"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Tryck länge för att dölja aktivitetsfältet"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Nästa"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Tillbaka"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Stäng"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Klar"</string>
 </resources>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index 22aa3ef..8302751 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; dak 1"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Umebakisha <xliff:g id="TIME">%1$s</xliff:g> leo"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Mapendekezo ya programu"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Programu zote"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Programu zako zinazopendekezwa"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Pata mapendekezo ya programu kwenye sehemu ya chini ya Skrini yako ya kwanza"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Pata mapendekezo ya programu katika safu ya vipendwa ya Skrini yako ya kwanza"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Mipangilio ya usogezaji kwenye mfumo"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Shiriki"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Picha ya skrini"</string>
+    <string name="action_split" msgid="2098009717623550676">"Iliyogawanywa"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Gusa programu nyingine ili utumie skrini iliyogawanywa"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Kitendo hiki hakiruhusiwi na programu au shirika lako"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Ungependa kuruka mafunzo ya usogezaji?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Utapata mafunzo haya baadaye katika programu ya <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Ghairi"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Ruka"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Paneli ya elimu kwenye upau wa shughuli inaonyeshwa"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Paneli ya elimu kwenye upau wa shughuli imefungwa"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Gusa na ushikilie ili ufiche upau wa shughuli"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Endelea"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Nyuma"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Funga"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Imemaliza"</string>
 </resources>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index e7cc31d..0de21e8 100644
--- a/quickstep/res/values-ta/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 நி"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"இன்று <xliff:g id="TIME">%1$s</xliff:g> மீதமுள்ளது"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"ஆப்ஸ் பரிந்துரைகள்"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"அனைத்து ஆப்ஸும்"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"நீங்கள் கணித்த ஆப்ஸ்"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"முகப்புத் திரையின் கடைசி வரிசையில் ஆப்ஸ் பரிந்துரைகளைப் பெறலாம்"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"உங்கள் முகப்புத் திரையின் \'பிடித்தவை\' வரிசையில் ஆப்ஸ் பரிந்துரைகளைப் பெறலாம்"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"சிஸ்டம் வழிசெலுத்தல் அமைப்புகள்"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"பகிர்"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ஸ்கிரீன்ஷாட்"</string>
+    <string name="action_split" msgid="2098009717623550676">"பிரி"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"ஸ்பிளிட் ஸ்கிரீனுக்கு மற்றொரு ஆப்ஸைத் தட்டவும்"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"ஆப்ஸோ உங்கள் நிறுவனமோ இந்த செயலை அனுமதிப்பதில்லை"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"வழிகாட்டுதல் பயிற்சியைத் தவிர்க்கவா?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"<xliff:g id="NAME">%1$s</xliff:g> ஆப்ஸில் பிறகு இதைக் கண்டறியலாம்"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"ரத்துசெய்"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"தவிர்"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"பணிப்பட்டியை எவ்வாறு பயன்படுத்துவது என்பது பற்றிய பலகம் காட்டப்படுகிறது"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"பணிப்பட்டியை எவ்வாறு பயன்படுத்துவது என்பது பற்றிய பலகம் மூடப்பட்டது"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"பணிப்பட்டியை மறைக்கத் தொட்டுப் பிடிக்கவும்"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"அடுத்து"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"பின்செல்"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"மூடுக"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"முடிந்தது"</string>
 </resources>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index ef8f3ba..0f42271 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 నిమిషం"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"నేటికి <xliff:g id="TIME">%1$s</xliff:g> మిగిలి ఉంది"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"యాప్ సలహాలు"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"అన్ని యాప్‌లు"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"మీ సూచించబడిన యాప్‌లు"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"మీ మొదటి స్క్రీన్‌ దిగువ వరుసలో యాప్ సలహాలను పొందండి"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"మీ హోమ్ స్క్రీన్‌లోని ఇష్టమైన వాటి వరుసలో యాప్ సూచ‌న‌లు పొందండి"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"సిస్టమ్ నావిగేషన్ సెట్టింగ్‌లు"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"షేర్ చేయండి"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"స్క్రీన్‌షాట్"</string>
+    <string name="action_split" msgid="2098009717623550676">"స్ప్లిట్ చేయండి"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"స్క్రీన్ విభజనను ఉపయోగించడానికి మరొక యాప్ నొక్కండి"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"ఈ చర్యను యాప్ గానీ, మీ సంస్థ గానీ అనుమతించవు"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"నావిగేషన్ ట్యుటోరియల్‌ను స్కిప్ చేయాలా?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"<xliff:g id="NAME">%1$s</xliff:g> యాప్‌లో మీరు తర్వాత కనుగొనవచ్చు"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"రద్దు చేయి"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"స్కిప్ చేయి"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"టాస్క్‌బార్ శిక్షణకు సంబంధించిన ప్యానెల్ కనిపించింది"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"టాస్క్‌బార్ శిక్షణకు సంబంధించిన ప్యానెల్ మూసివేయబడింది"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"టాస్క్‌బార్‌ను దాచడానికి తాకి, నొక్కి ఉంచండి"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"తర్వాత"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"వెనుకకు"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"మూసివేయండి"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"పూర్తయింది"</string>
 </resources>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index 52b2878..435c774 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt;1 นาที"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"วันนี้เหลืออีก <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"คำแนะนำเกี่ยวกับแอป"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"แอปทั้งหมด"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"แอปที่คาดการณ์ไว้"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"ดูแอปแนะนำที่แถวล่างของหน้าจอหลัก"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"รับคำแนะนำเกี่ยวกับแอปในแถวรายการโปรดของหน้าจอหลัก"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"การตั้งค่าการนำทางของระบบ"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"แชร์"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ภาพหน้าจอ"</string>
+    <string name="action_split" msgid="2098009717623550676">"แยก"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"แตะที่แอปอื่นเพื่อใช้แบ่งหน้าจอ"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"แอปหรือองค์กรของคุณไม่อนุญาตการดำเนินการนี้"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"ข้ามบทแนะนำการนำทางไหม"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"คุณดูบทแนะนำนี้ได้ภายหลังในแอป \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"ยกเลิก"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"ข้าม"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"แถบงาน Education ปรากฎขึ้น"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ปิดแถบงาน Education แล้ว"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"แตะค้างไว้เพื่อซ่อนแถบงาน"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"ถัดไป"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"กลับ"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"ปิด"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"เสร็จ"</string>
 </resources>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index 412449b..3ed75a9 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> na lang ngayon"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Mga iminumungkahing app"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Lahat ng app"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Iyong mga nahulaang app"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Makakuha ng mga suhestyon sa app sa ibabang row ng iyong Home screen"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Makakuha ng mga iminumungkahing app sa row ng mga paborito ng iyong Home screen"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Mga setting ng navigation ng system"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Ibahagi"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
+    <string name="action_split" msgid="2098009717623550676">"Split"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Mag-tap ng ibang app para gamitin ang splitscreen"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Hindi pinapayagan ng app o ng iyong organisasyon ang pagkilos na ito"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Laktawan ang tutorial sa pag-navigate?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Makikita mo ito sa <xliff:g id="NAME">%1$s</xliff:g> app sa ibang pagkakataon"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Kanselahin"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Laktawan"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Lumabas ang edukasyon sa taskbar"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Sarado ang edukasyon sa taskbar"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Pindutin nang matagal para itago ang taskbar"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Susunod"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Bumalik"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Isara"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Tapos na"</string>
 </resources>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index 58d78e0..910affe 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 dk."</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Bugün <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Önerilen uygulamalar"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Tüm uygulamalar"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Tahmin edilen uygulamalarınız"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Ana ekranınızın alt satırında uygulama önerileri alın"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Ana ekranınızın favoriler satırında uygulama önerileri alın"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sistem gezinme ayarları"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Paylaş"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Ekran görüntüsü"</string>
+    <string name="action_split" msgid="2098009717623550676">"Böl"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Bölünmüş ekran için başka bir uygulamaya dokunun"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Uygulamanız veya kuruluşunuz bu işleme izin vermiyor"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Gezinme eğitimi atlansın mı?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Bunu daha sonra <xliff:g id="NAME">%1$s</xliff:g> uygulamasında bulabilirsiniz"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"İptal"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Atla"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Görev çubuğu eğitimi görüntülendi"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Görev çubuğu eğitimi kapatıldı"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Görev çubuğunu gizlemek için basılı tutun"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"İleri"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Geri"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Kapat"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Bitti"</string>
 </resources>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 544cec7..466da29 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 хв"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Сьогодні залишилося <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Рекомендовані додатки"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Усі додатки"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Передбачені додатки"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Рекомендовані додатки з\'являтимуться в нижньому рядку головного екрана"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Рекомендовані додатки з\'являтимуться в рядку \"Вибране\" на головному екрані"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Системні налаштування навігації"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Поділитися"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Знімок екрана"</string>
+    <string name="action_split" msgid="2098009717623550676">"Розділити"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Щоб розділити екран, виберіть ще один додаток"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Ця дія заборонена додатком або адміністратором організації"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Пропустити посібник із навігації?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Ви знайдете його пізніше в додатку <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Скасувати"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Пропустити"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Панель завдань Education відкрито"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Панель завдань Education закрито"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Натисніть і втримуйте панель завдань, щоб сховати її"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Далі"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Назад"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Закрити"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Готово"</string>
 </resources>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index 2e8bde9..e0e9beb 100644
--- a/quickstep/res/values-ur/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"‏&lt; 1 منٹ"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"آج <xliff:g id="TIME">%1$s</xliff:g> بچا ہے"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"ایپس کی تجاویز"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"تمام ایپس"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"آپ کی پیشن گوئی کردہ ایپس"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"اپنی ہوم اسکرین کی نچلی قطار پر ایپ کی تجاویز حاصل کریں"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"اپنی ہوم اسکرین کی پسندیدہ قطار پر ایپ کی تجاویز حاصل کریں"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"سسٹم نیویگیشن کی ترتیبات"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"اشتراک کریں"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"اسکرین شاٹ"</string>
+    <string name="action_split" msgid="2098009717623550676">"اسپلٹ"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"اسپلٹ اسکرین کا استعمال کرنے کیلئے دوسری ایپ پر تھپتھپائیں"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"ایپ یا آپ کی تنظیم کی جانب سے اس کارروائی کی اجازت نہیں ہے"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"نیویگیشن کا ٹیوٹوریل نظر انداز کریں؟"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"آپ اسے بعد میں <xliff:g id="NAME">%1$s</xliff:g> ایپ میں تلاش کر سکتے ہیں"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"منسوخ کریں"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"نظر انداز کریں"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ٹاکس بار کا تعلیمی پینل ظاہر ہو گیا"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ٹاسک بار کا تعلیمی پینل بند ہو گیا"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"ٹاسک بار کو کسی بھی وقت چھپانے کیلئے ٹچ کریں اور دبائے رکھیں"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"آگے"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"پیچھے"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"بند کریں"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"ہو گیا"</string>
 </resources>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index 8b194de..ffa8a2d 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 daqiqa"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Bugun <xliff:g id="TIME">%1$s</xliff:g> qoldi"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Tavsiya etiladigan ilovalar"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Barcha ilovalar"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Taklif qilingan ilovalar"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Tavsiya etiladigan ilovalar bosh ekran pastidagi qatorda chiqadi"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Tavsiya etiladigan ilovalar bosh ekranning saralanganlar ruknida chiqadi"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Tizim navigatsiya sozlamalari"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Ulashish"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Skrinshot"</string>
+    <string name="action_split" msgid="2098009717623550676">"Ajratish"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Ekranni ikkiga ajratish uchun boshqa ilovani bosing"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Bu amal ilova yoki tashkilotingiz tomonidan taqiqlangan"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Navigatsiya darsi yopilsinmi?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Bu darslar <xliff:g id="NAME">%1$s</xliff:g> ilovasida chiqadi"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Bekor qilish"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Tashlab ketish"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Taʼlim vazifalar paneli chiqdi"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Taʼlim vazifalar paneli yopildi"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Vazifalar panelini ustiga bosib turib yashirish mumkin"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Keyingisi"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Orqaga"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Yopish"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Tayyor"</string>
 </resources>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index 7bd0c70..81d3dec 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 phút"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Hôm nay còn <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Các ứng dụng đề xuất"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Tất cả ứng dụng"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Các ứng dụng gợi ý của bạn"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Nhận các ứng dụng đề xuất ở cuối Màn hình chính"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Nhận các ứng dụng đề xuất trên hàng mục ưa thích của Màn hình chính"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Chế độ cài đặt di chuyển trên hệ thống"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Chia sẻ"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Chụp ảnh màn hình"</string>
+    <string name="action_split" msgid="2098009717623550676">"Chia đôi màn hình"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Nhấn vào một ứng dụng khác để dùng màn hình chia đôi"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Ứng dụng hoặc tổ chức của bạn không cho phép thực hiện hành động này"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Bỏ qua phần hướng dẫn thao tác?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Bạn có thể tìm lại phần hướng dẫn này trong ứng dụng <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Hủy"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Bỏ qua"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Đã hiện bảng hướng dẫn trên thanh tác vụ"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Đã đóng bảng hướng dẫn trên thanh tác vụ"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Chạm và giữ để ẩn thanh tác vụ"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Tiếp theo"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Quay lại"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Đóng"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Xong"</string>
 </resources>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 9e641de..a55a44c 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"不到 1 分钟"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"今天还可使用 <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"应用建议"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"所有应用"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"您可能想要使用的应用"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"在主屏幕底部获取应用建议"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"在主屏幕的收藏行获取应用建议"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"系统导航设置"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"分享"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"屏幕截图"</string>
+    <string name="action_split" msgid="2098009717623550676">"拆分"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"点按另一个应用即可使用分屏"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"该应用或您所在的单位不允许执行此操作"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"要跳过导航教程吗?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"您之后可以在“<xliff:g id="NAME">%1$s</xliff:g>”应用中找到此教程"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"取消"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"跳过"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"任务栏教程已显示"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"任务栏教程已关闭"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"轻触并按住即可隐藏任务栏"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"继续"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"返回"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"关闭"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"完成"</string>
 </resources>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index d1e8392..42329eb 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"少於 1 分鐘"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"今天剩餘時間:<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"應用程式建議"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"所有應用程式"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"您的預測應用程式"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"在主畫面底部取得應用程式建議"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"在主畫面「我的最愛」列取得應用程式建議"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"系統導覽設定"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"分享"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"螢幕截圖"</string>
+    <string name="action_split" msgid="2098009717623550676">"分割"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"輕按其他應用程式以使用分割螢幕"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"應用程式或您的機構不允許此操作"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"要略過手勢操作教學課程嗎?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"您之後可以在「<xliff:g id="NAME">%1$s</xliff:g>」應用程式找到這些說明"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"取消"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"略過"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"顯示咗工作列教學"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"閂咗工作列教學"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"按住即可隱藏工作列"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"繼續"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"返回"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"關閉"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"完成"</string>
 </resources>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index db10354..a014adb 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 分鐘"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"今天還能使用 <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"應用程式建議"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"所有應用程式"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"系統預測你會使用的應用程式"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"在主畫面底部顯示應用程式建議"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"在主畫面的收藏列取得應用程式建議"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"系統操作機制設定"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"分享"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"螢幕截圖"</string>
+    <string name="action_split" msgid="2098009717623550676">"分割"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"輕觸另一個應用程式即可使用分割畫面"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"這個應用程式或貴機構不允許執行這個動作"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"要略過手勢操作教學課程嗎?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"你之後可以在「<xliff:g id="NAME">%1$s</xliff:g>」應用程式找到這些說明"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"取消"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"略過"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"工作列教學課程已顯示"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"工作列教學課程已關閉"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"按住即可隱藏工作列"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"繼續"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"返回"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"關閉"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"完成"</string>
 </resources>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index bbfb840..dfa67d0 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -29,7 +29,6 @@
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 iminithi"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> esele namhlanje"</string>
     <string name="title_app_suggestions" msgid="4185902664111965088">"Iziphakamiso zohlelo lokusebenza"</string>
-    <string name="all_apps_label" msgid="8542784161730910663">"Zonke izinhlelo zokusebenza"</string>
     <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Ama-app akho aqagelwe"</string>
     <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Thola iziphakamiso ze-app emgqeni ongezansi wesikrini sakho sasekhaya"</string>
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Thola iziphakamiso zohlelo lokusebenza kumugqa wezintandokazi Zesikrini sakho sasekhaya"</string>
@@ -79,9 +78,18 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Amasethingi wokuzulazula isistimu"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Yabelana"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Isithombe-skrini"</string>
+    <string name="action_split" msgid="2098009717623550676">"Hlukanisa"</string>
+    <string name="toast_split_select_app" msgid="5453865907322018352">"Thepha enye i-app ukuze usebenzise isikrini sokuhlukanisa"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Lesi senzo asivunyelwanga uhlelo lokusebenza noma inhlangano yakho"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Yeqa isifundo sokuzulazula?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Lokhu ungakuthola kamuva ku-app ye-<xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Khansela"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Yeqa"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Imfuno yebha yomsebenzi ivelile"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Imfundo yebha yomsebenzi ivaliwe"</string>
+    <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Thinta futhi ubambe, bamba ukuze ufihle ibha yomsebenzi"</string>
+    <string name="taskbar_edu_next" msgid="4007618274426775841">"Okulandelayo"</string>
+    <string name="taskbar_edu_previous" msgid="459202320127201702">"Emuva"</string>
+    <string name="taskbar_edu_close" msgid="887022990168191073">"Vala"</string>
+    <string name="taskbar_edu_done" msgid="6880178093977704569">"Kwenziwe"</string>
 </resources>
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index 167c7c3..5edcc9d 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -14,8 +14,6 @@
      limitations under the License.
 -->
 <resources>
-    <color name="back_arrow_color_light">#FFFFFFFF</color>
-    <color name="back_arrow_color_dark">#99000000</color>
 
     <color name="chip_hint_foreground_color">#fff</color>
     <color name="chip_scrim_start_color">#39000000</color>
@@ -26,6 +24,52 @@
     <color name="all_apps_prediction_row_separator_dark">#3cffffff</color>
 
     <!-- Taskbar -->
-    <color name="taskbar_background">#101010</color>
+    <color name="taskbar_background">@color/overview_scrim_dark</color>
     <color name="taskbar_icon_selection_ripple">#E0E0E0</color>
+
+    <color name="taskbar_stashed_handle_light_color">#EBffffff</color>
+    <color name="taskbar_stashed_handle_dark_color">#99000000</color>
+
+    <!-- Gesture navigation tutorial -->
+    <color name="gesture_tutorial_back_arrow_color">#FFFFFFFF</color>
+
+    <color name="gesture_tutorial_fake_wallpaper_color">#f9f9f9</color> <!-- White -->
+    <color name="gesture_tutorial_ripple_color">#A0C2F9</color> <!-- Light Blue -->
+    <color name="gesture_tutorial_fake_task_view_color">#6DA1FF</color> <!-- Light Blue -->
+    <!-- Must contrast gesture_tutorial_fake_wallpaper_color -->
+    <color name="gesture_tutorial_fake_previous_task_view_color">#3C4043</color> <!-- Gray -->
+    <color name="gesture_tutorial_action_button_label_color">#FF000000</color>
+    <color name="gesture_tutorial_primary_color">#B7F29F</color> <!-- Light Green -->
+
+    <!-- Mock hotseat -->
+    <color name="mock_app_icon_1">#8AB4F8</color>
+    <color name="mock_app_icon_2">#F28B82</color>
+    <color name="mock_app_icon_3">#FDD663</color>
+    <color name="mock_app_icon_4">#81C995</color>
+    <color name="mock_search_bar">#3C4043</color>
+
+    <!-- Mock conversation -->
+    <color name="mock_conversation_background">#f1f3f4</color>
+    <color name="mock_conversation_top_bar">#e8eaed</color>
+    <color name="mock_conversation_top_bar_item">#dadce0</color>
+    <color name="mock_conversation_sent_message">#bdc1c6</color>
+    <color name="mock_conversation_received_message">#e8eaed</color>
+    <color name="mock_conversation_message_input">#dadce0</color>
+    <color name="mock_conversation_profile_icon">#dadce0</color>
+
+    <!-- Mock conversations list -->
+    <color name="mock_list_background">#dadce0</color>
+    <color name="mock_list_top_bar">#e8eaed</color>
+    <color name="mock_list_top_bar_item">#f8f9fa</color>
+    <color name="mock_list_profile_icon">#9aa0a6</color>
+    <color name="mock_list_preview_message">#bdc1c6</color>
+    <color name="mock_list_button">#bdc1c6</color>
+
+    <!-- Mock web page -->
+    <color name="mock_webpage_background">#f1f3f4</color>
+    <color name="mock_webpage_url_bar">#6e7175</color>
+    <color name="mock_webpage_url_bar_item">#9a9a9a</color>
+    <color name="mock_webpage_top_bar">#e8eaed</color>
+    <color name="mock_webpage_top_bar_item">#80868b</color>
+    <color name="mock_webpage_page_text">#bdc1c6</color>
 </resources>
\ No newline at end of file
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index d67b23b..31c0f5f 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -30,6 +30,7 @@
          determines how many thumbnails will be fetched in the background. -->
     <integer name="recentsThumbnailCacheSize">3</integer>
     <integer name="recentsIconCacheSize">12</integer>
+    <integer name="recentsScrollHapticMinGapMillis">20</integer>
 
     <!-- Assistant Gesture -->
     <integer name="assistant_gesture_min_time_threshold">200</integer>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 6cc64e0..e08eda8 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -16,7 +16,8 @@
 
 <resources>
     <dimen name="task_thumbnail_icon_size">48dp</dimen>
-    <dimen name="task_thumbnail_icon_size_grid">32dp</dimen>
+    <dimen name="task_thumbnail_icon_drawable_size">48dp</dimen>
+    <dimen name="task_thumbnail_icon_drawable_size_grid">32dp</dimen>
     <!-- For screens without rounded corners -->
     <dimen name="task_corner_radius_small">2dp</dimen>
     <!-- For Launchers that want to override the default dialog corner radius -->
@@ -26,24 +27,29 @@
     <dimen name="task_menu_corner_radius">22dp</dimen>
     <dimen name="task_menu_item_corner_radius">4dp</dimen>
     <dimen name="task_menu_spacing">2dp</dimen>
+    <dimen name="task_menu_width_grid">200dp</dimen>
     <dimen name="overview_proactive_row_height">48dp</dimen>
     <dimen name="overview_proactive_row_bottom_margin">16dp</dimen>
 
     <dimen name="overview_minimum_next_prev_size">50dp</dimen>
     <dimen name="overview_task_margin">16dp</dimen>
+    <dimen name="overview_task_margin_focused">12dp</dimen>
+    <dimen name="overview_task_margin_grid">4dp</dimen>
 
     <!-- Overrideable in overlay that provides the Overview Actions. -->
     <dimen name="overview_actions_height">48dp</dimen>
-    <dimen name="overview_actions_bottom_margin_gesture">28dp</dimen>
-    <dimen name="overview_actions_bottom_margin_three_button">8dp</dimen>
+    <dimen name="overview_actions_margin_gesture">28dp</dimen>
+    <dimen name="overview_actions_top_margin_gesture_grid_portrait">19.37dp</dimen>
+    <dimen name="overview_actions_bottom_margin_gesture_grid_portrait">22dp</dimen>
+    <dimen name="overview_actions_top_margin_gesture_grid_landscape">19.1dp</dimen>
+    <dimen name="overview_actions_bottom_margin_gesture_grid_landscape">10dp</dimen>
+    <dimen name="overview_actions_margin_three_button">8dp</dimen>
     <dimen name="overview_actions_horizontal_margin">16dp</dimen>
 
-    <dimen name="overview_grid_top_margin">77dp</dimen>
-    <dimen name="overview_grid_bottom_margin">90dp</dimen>
-    <dimen name="overview_grid_side_margin">54dp</dimen>
-    <dimen name="overview_grid_row_spacing">42dp</dimen>
-    <dimen name="overview_grid_focus_vertical_margin">90dp</dimen>
-    <dimen name="split_placeholder_size">110dp</dimen>
+    <dimen name="overview_grid_side_margin">50dp</dimen>
+    <dimen name="overview_grid_row_spacing_portrait">17.13dp</dimen>
+    <dimen name="overview_grid_row_spacing_landscape">13.38dp</dimen>
+    <dimen name="overview_grid_focus_vertical_margin">0dp</dimen>
 
     <!-- These speeds are in dp/s -->
     <dimen name="max_task_dismiss_drag_velocity">2.25dp</dimen>
@@ -52,6 +58,7 @@
     <dimen name="default_task_dismiss_drag_velocity_grid_focus_task">5dp</dimen>
 
     <dimen name="recents_page_spacing">16dp</dimen>
+    <dimen name="recents_page_spacing_grid">36dp</dimen>
     <dimen name="recents_clear_all_deadzone_vertical_margin">70dp</dimen>
 
     <!-- The speed in dp/s at which the user needs to be scrolling in recents such that we start
@@ -70,6 +77,8 @@
     <item name="content_scale" format="float" type="dimen">0.97</item>
     <dimen name="closing_window_trans_y">115dp</dimen>
 
+    <dimen name="quick_switch_scaling_scroll_threshold">100dp</dimen>
+
     <dimen name="recents_empty_message_text_size">16sp</dimen>
     <dimen name="recents_empty_message_text_padding">16dp</dimen>
 
@@ -108,17 +117,50 @@
     <dimen name="gestures_overscroll_finish_threshold">136dp</dimen>
 
     <!-- Tips Gesture Tutorial -->
-    <dimen name="gesture_tutorial_title_margin_start_end">40dp</dimen>
-    <dimen name="gesture_tutorial_subtitle_margin_start_end">16dp</dimen>
     <dimen name="gesture_tutorial_feedback_margin_start_end">24dp</dimen>
-    <dimen name="gesture_tutorial_button_margin_start_end">18dp</dimen>
+    <dimen name="gesture_tutorial_foldable_feedback_margin_start_end">140dp</dimen>
+    <dimen name="gesture_tutorial_multi_row_task_view_spacing">72dp</dimen>
+    <dimen name="gesture_tutorial_small_task_view_corner_radius">18dp</dimen>
+
+    <!-- Gesture Tutorial mock conversations -->
+    <dimen name="gesture_tutorial_message_icon_size">44dp</dimen>
+    <dimen name="gesture_tutorial_message_icon_corner_radius">100dp</dimen>
+    <dimen name="gesture_tutorial_message_input_margin_top">36dp</dimen>
+    <dimen name="gesture_tutorial_message_large_margin_bottom">32dp</dimen>
+    <dimen name="gesture_tutorial_message_small_margin_bottom">4dp</dimen>
+    <dimen name="gesture_tutorial_message_padding_start">26dp</dimen>
+    <dimen name="gesture_tutorial_message_padding_end">18dp</dimen>
+    <dimen name="gesture_tutorial_foldable_message_padding_start_end">126dp</dimen>
+
+    <!-- Gesture Tutorial mock conversation lists -->
+    <dimen name="gesture_tutorial_conversation_icon_size">56dp</dimen>
+    <dimen name="gesture_tutorial_conversation_icon_corner_radius">100dp</dimen>
+    <dimen name="gesture_tutorial_conversation_list_padding_top">28dp</dimen>
+    <dimen name="gesture_tutorial_conversation_line_padding_start">20dp</dimen>
+
+    <!-- Gesture Tutorial mock hotseats -->
+    <dimen name="gesture_tutorial_hotseat_icon_size">60dp</dimen>
+    <dimen name="gesture_tutorial_hotseat_icon_corner_radius">100dp</dimen>
+    <dimen name="gesture_tutorial_hotseat_search_height">50dp</dimen>
+    <dimen name="gesture_tutorial_hotseat_search_corner_radius">100dp</dimen>
+    <dimen name="gesture_tutorial_hotseat_icon_search_margin">36dp</dimen>
+
+    <!-- Gesture Tutorial mock webpages -->
+    <dimen name="gesture_tutorial_webpage_padding_top">32dp</dimen>
+    <dimen name="gesture_tutorial_webpage_large_margin_top">24dp</dimen>
+    <dimen name="gesture_tutorial_webpage_small_margin_top">8dp</dimen>
+    <dimen name="gesture_tutorial_webpage_large_corner_radius">22dp</dimen>
+    <dimen name="gesture_tutorial_webpage_medium_corner_radius">8dp</dimen>
+    <dimen name="gesture_tutorial_webpage_small_corner_radius">4dp</dimen>
+    <dimen name="gesture_tutorial_webpage_large_line_height">36dp</dimen>
+    <dimen name="gesture_tutorial_webpage_small_line_height">22dp</dimen>
 
     <!-- All Set page -->
     <dimen name="allset_page_margin_horizontal">40dp</dimen>
     <dimen name="allset_title_margin_top">24dp</dimen>
     <dimen name="allset_title_icon_margin_top">32dp</dimen>
-    <dimen name="allset_hint_margin_bottom">52dp</dimen>
     <dimen name="allset_subtitle_margin_top">24dp</dimen>
+    <dimen name="allset_subtitle_width_max">348dp</dimen>
 
     <!-- All Apps Education tutorial -->
     <dimen name="swipe_edu_padding">8dp</dimen>
@@ -149,12 +191,19 @@
     <dimen name="accessibility_gesture_min_swipe_distance">80dp</dimen>
 
     <!-- Taskbar -->
-    <dimen name="taskbar_size">60dp</dimen>
-    <dimen name="taskbar_icon_size">44dp</dimen>
+    <dimen name="taskbar_size">@*android:dimen/taskbar_frame_height</dimen>
     <dimen name="taskbar_icon_touch_size">48dp</dimen>
     <dimen name="taskbar_icon_drag_icon_size">54dp</dimen>
-    <!-- Note that this applies to both sides of all icons, so visible space is double this. -->
-    <dimen name="taskbar_icon_spacing">8dp</dimen>
     <dimen name="taskbar_folder_margin">16dp</dimen>
     <dimen name="taskbar_nav_buttons_spacing">16dp</dimen>
+    <dimen name="taskbar_contextual_padding_top">8dp</dimen>
+    <dimen name="taskbar_nav_buttons_size">44dp</dimen>
+    <dimen name="taskbar_contextual_button_margin">40dp</dimen>
+    <dimen name="taskbar_hotseat_nav_spacing">42dp</dimen>
+    <dimen name="taskbar_contextual_buttons_size">35dp</dimen>
+    <dimen name="taskbar_stashed_size">24dp</dimen>
+    <dimen name="taskbar_stashed_handle_width">220dp</dimen>
+    <dimen name="taskbar_stashed_handle_height">6dp</dimen>
+    <dimen name="taskbar_edu_wave_anim_trans_y">25dp</dimen>
+    <dimen name="taskbar_edu_wave_anim_trans_y_return_overshoot">4dp</dimen>
 </resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 4aee2a9..6af0d60 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -53,8 +53,6 @@
 
     <!-- Accessibility title for the row of all-apps containing app predictions. [CHAR LIMIT=50] -->
     <string name="title_app_suggestions">App suggestions</string>
-    <!-- Label for the header text of the All Apps section in All Apps view, used to separate Predicted Apps and Actions section from All Apps section. [CHAR_LIMIT=50] -->
-    <string name="all_apps_label">All apps</string>
     <!-- Text of the tip when user lands in all apps view for the first time, indicating where the tip toast points to is the predicted apps section. [CHAR_LIMIT=50] -->
     <string name="all_apps_prediction_tip">Your predicted apps</string>
 
@@ -191,6 +189,10 @@
     <string name="action_share">Share</string>
     <!-- Label for a button that causes a screen shot of the current app to be taken. [CHAR_LIMIT=40] -->
     <string name="action_screenshot">Screenshot</string>
+    <!-- Label for a button that enters split screen selection mode. [CHAR_LIMIT=20] -->
+    <string name="action_split">Split</string>
+    <!-- Label for toast with instructions for split screen selection mode. [CHAR_LIMIT=50] -->
+    <string name="toast_split_select_app">Tap another app to use splitscreen</string>
     <!-- Message shown when an action is blocked by a policy enforced by the app or the organization managing the device. [CHAR_LIMIT=NONE] -->
     <string name="blocked_by_policy">This action isn\'t allowed by the app or your organization</string>
 
@@ -203,4 +205,27 @@
     <string name="gesture_tutorial_action_button_label_cancel">Cancel</string>
     <!-- Button text shown on a button on the tutorial skip dialog to exit the tutorial. [CHAR LIMIT=14] -->
     <string name="gesture_tutorial_action_button_label_skip">Skip</string>
+
+    <!-- ******* Taskbar Edu ******* -->
+    <!-- Accessibility text spoken when the taskbar education panel appears [CHAR_LIMIT=NONE] -->
+    <string name="taskbar_edu_opened">Taskbar education appeared</string>
+    <!-- Accessibility text spoken when the taskbar education panel disappears [CHAR_LIMIT=NONE] -->
+    <string name="taskbar_edu_closed">Taskbar education closed</string>
+    <!-- Text in dialog that lets a user know how they can use the taskbar to switch apps on their device.
+         [CHAR_LIMIT=60] -->
+    <string name="taskbar_edu_switch_apps" translatable="false">Use the taskbar to switch apps</string>
+    <!-- Text in dialog that lets a user know how they can use the taskbar to use multiple apps at once on their device.
+         [CHAR_LIMIT=60] -->
+    <string name="taskbar_edu_splitscreen" translatable="false">Drag to the side to use two apps at once</string>
+    <!-- Text in dialog that lets a user know how they can hide the taskbar on their device.
+         [CHAR_LIMIT=60] -->
+    <string name="taskbar_edu_stashing">Touch &amp; hold to hide the taskbar</string>
+    <!-- Text on button to go to the next screen of a tutorial [CHAR_LIMIT=16] -->
+    <string name="taskbar_edu_next">Next</string>
+    <!-- Text on button to go to the previous screen of a tutorial [CHAR_LIMIT=16] -->
+    <string name="taskbar_edu_previous">Back</string>
+    <!-- Text on button to exit a tutorial [CHAR_LIMIT=16] -->
+    <string name="taskbar_edu_close">Close</string>
+    <!-- Text on button to finish a tutorial [CHAR_LIMIT=16] -->
+    <string name="taskbar_edu_done">Done</string>
 </resources>
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 07c448d..b5444b5 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -135,8 +135,39 @@
         <item name="android:textAllCaps">false</item>
     </style>
 
+    <style name="OverviewClearAllButton" parent="@android:style/Widget.DeviceDefault.Button">
+        <item name="android:background">@drawable/bg_overview_clear_all_button</item>
+        <item name="android:minWidth">85dp</item>
+        <item name="android:minHeight">36dp</item>
+        <item name="android:stateListAnimator">@null</item>
+    </style>
+
     <!-- Icon displayed on the taskbar -->
     <style name="BaseIcon.Workspace.Taskbar" >
         <item name="iconDisplay">taskbar</item>
     </style>
+
+    <style name="TaskbarEdu.Button.Close" parent="@android:style/Widget.Material.Button">
+        <item name="android:background">@drawable/button_taskbar_edu_bordered</item>
+        <item name="android:stateListAnimator">@null</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:padding">4dp</item>
+    </style>
+
+    <style name="TaskbarEdu.Button.Next" parent="@android:style/Widget.Material.Button">
+        <item name="android:background">@drawable/button_taskbar_edu_colored</item>
+        <item name="android:stateListAnimator">@null</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:padding">4dp</item>
+    </style>
+
+    <style name="TextAppearance.TaskbarEdu.Title"
+        parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle" >
+        <item name="android:layout_marginHorizontal">16dp</item>
+        <item name="android:gravity">center_horizontal</item>
+        <item name="android:fontFamily">google-sans</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textSize">24sp</item>
+        <item name="android:lines">2</item>
+    </style>
 </resources>
\ No newline at end of file
diff --git a/quickstep/robolectric_tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/robolectric_tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
deleted file mode 100644
index 7c97b93..0000000
--- a/quickstep/robolectric_tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
+++ /dev/null
@@ -1,301 +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.model;
-
-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;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.robolectric.Shadows.shadowOf;
-
-import android.app.prediction.AppTarget;
-import android.app.prediction.AppTargetId;
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.Process;
-import android.os.UserHandle;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.icons.ComponentWithLabel;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.model.BgDataModel.FixedContainerItems;
-import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.shadows.ShadowDeviceFlag;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.LauncherModelHelper;
-import com.android.launcher3.util.ViewOnDrawExecutor;
-import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.widget.PendingAddWidgetInfo;
-import com.android.launcher3.widget.model.WidgetsListBaseEntry;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadow.api.Shadow;
-import org.robolectric.shadows.ShadowAppWidgetManager;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.stream.Collectors;
-
-@RunWith(RobolectricTestRunner.class)
-public final class WidgetsPredicationUpdateTaskTest {
-
-    private AppWidgetProviderInfo mApp1Provider1 = new AppWidgetProviderInfo();
-    private AppWidgetProviderInfo mApp1Provider2 = new AppWidgetProviderInfo();
-    private AppWidgetProviderInfo mApp2Provider1 = new AppWidgetProviderInfo();
-    private AppWidgetProviderInfo mApp4Provider1 = new AppWidgetProviderInfo();
-    private AppWidgetProviderInfo mApp4Provider2 = new AppWidgetProviderInfo();
-    private AppWidgetProviderInfo mApp5Provider1 = new AppWidgetProviderInfo();
-
-    private FakeBgDataModelCallback mCallback = new FakeBgDataModelCallback();
-    private Context mContext;
-    private LauncherModelHelper mModelHelper;
-    private UserHandle mUserHandle;
-    private InvariantDeviceProfile mTestProfile;
-
-    @Mock
-    private IconCache mIconCache;
-
-    @Before
-    public void setup() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        doAnswer(invocation -> {
-            ComponentWithLabel componentWithLabel = invocation.getArgument(0);
-            return componentWithLabel.getComponent().getShortClassName();
-        }).when(mIconCache).getTitleNoCache(any());
-
-        mContext = RuntimeEnvironment.application;
-        mModelHelper = new LauncherModelHelper();
-        mUserHandle = Process.myUserHandle();
-        mTestProfile = new InvariantDeviceProfile();
-        // 2 widgets, app4/provider1 & app5/provider1, have already been added to the workspace.
-        mModelHelper.initializeData("/widgets_predication_update_task_data.txt");
-
-        ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
-        mApp1Provider1.provider = ComponentName.createRelative("app1", "provider1");
-        ReflectionHelpers.setField(mApp1Provider1, "providerInfo",
-                packageManager.addReceiverIfNotPresent(mApp1Provider1.provider));
-        mApp1Provider2.provider = ComponentName.createRelative("app1", "provider2");
-        ReflectionHelpers.setField(mApp1Provider2, "providerInfo",
-                packageManager.addReceiverIfNotPresent(mApp1Provider2.provider));
-        mApp2Provider1.provider = ComponentName.createRelative("app2", "provider1");
-        ReflectionHelpers.setField(mApp2Provider1, "providerInfo",
-                packageManager.addReceiverIfNotPresent(mApp2Provider1.provider));
-        mApp4Provider1.provider = ComponentName.createRelative("app4", "provider1");
-        ReflectionHelpers.setField(mApp4Provider1, "providerInfo",
-                packageManager.addReceiverIfNotPresent(mApp4Provider1.provider));
-        mApp4Provider2.provider = ComponentName.createRelative("app4", ".provider2");
-        ReflectionHelpers.setField(mApp4Provider2, "providerInfo",
-                packageManager.addReceiverIfNotPresent(mApp4Provider2.provider));
-        mApp5Provider1.provider = ComponentName.createRelative("app5", "provider1");
-        ReflectionHelpers.setField(mApp5Provider1, "providerInfo",
-                packageManager.addReceiverIfNotPresent(mApp5Provider1.provider));
-
-        ShadowAppWidgetManager shadowAppWidgetManager =
-                shadowOf(mContext.getSystemService(AppWidgetManager.class));
-        shadowAppWidgetManager.addInstalledProvider(mApp1Provider1);
-        shadowAppWidgetManager.addInstalledProvider(mApp1Provider2);
-        shadowAppWidgetManager.addInstalledProvider(mApp2Provider1);
-        shadowAppWidgetManager.addInstalledProvider(mApp4Provider1);
-        shadowAppWidgetManager.addInstalledProvider(mApp4Provider2);
-        shadowAppWidgetManager.addInstalledProvider(mApp5Provider1);
-
-        mModelHelper.getModel().addCallbacks(mCallback);
-
-        MODEL_EXECUTOR.post(() -> mModelHelper.getBgDataModel().widgetsModel.update(
-                LauncherAppState.getInstance(mContext), /* packageUser= */ null));
-        waitUntilIdle();
-    }
-
-
-    @Test
-    public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder()
-            throws Exception {
-        // WHEN newPredicationTask is executed with app predication of 5 apps.
-        AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "className",
-                mUserHandle);
-        AppTarget app2 = new AppTarget(new AppTargetId("app2"), "app2", "className",
-                mUserHandle);
-        AppTarget app3 = new AppTarget(new AppTargetId("app3"), "app3", "className",
-                mUserHandle);
-        AppTarget app4 = new AppTarget(new AppTargetId("app4"), "app4", "className",
-                mUserHandle);
-        AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "className",
-                mUserHandle);
-        mModelHelper.executeTaskForTest(
-                newWidgetsPredicationTask(List.of(app5, app3, app2, app4, app1)))
-                .forEach(Runnable::run);
-
-        // THEN only 3 widgets are returned because
-        // 1. app5/provider1 & app4/provider1 have already been added to workspace. They are
-        //    excluded from the result.
-        // 2. app3 doesn't have a widget.
-        // 3. only 1 widget is picked from app1 because we only want to promote one widget per app.
-        List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
-                .stream()
-                .map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
-                .collect(Collectors.toList());
-        assertThat(recommendedWidgets).hasSize(3);
-        assertWidgetInfo(recommendedWidgets.get(0).info, mApp2Provider1);
-        assertWidgetInfo(recommendedWidgets.get(1).info, mApp4Provider2);
-        assertWidgetInfo(recommendedWidgets.get(2).info, mApp1Provider1);
-    }
-
-    @Test
-    public void widgetsRecommendationRan_localFilterDisabled_shouldReturnWidgetsInPredicationOrder()
-            throws Exception {
-        ShadowDeviceFlag shadowDeviceFlag = Shadow.extract(
-                FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER);
-        shadowDeviceFlag.setValue(false);
-
-        // WHEN newPredicationTask is executed with 5 predicated widgets.
-        AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1",
-                mUserHandle);
-        AppTarget widget2 = new AppTarget(new AppTargetId("app1"), "app1", "provider2",
-                mUserHandle);
-        // Not installed app
-        AppTarget widget3 = new AppTarget(new AppTargetId("app2"), "app3", "provider1",
-                mUserHandle);
-        // Not installed widget
-        AppTarget widget4 = new AppTarget(new AppTargetId("app4"), "app4", "provider3",
-                mUserHandle);
-        AppTarget widget5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
-                mUserHandle);
-        mModelHelper.executeTaskForTest(
-                newWidgetsPredicationTask(List.of(widget5, widget3, widget2, widget4, widget1)))
-                .forEach(Runnable::run);
-
-        // THEN only 3 widgets are returned because the launcher only filters out non-exist widgets.
-        List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
-                .stream()
-                .map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
-                .collect(Collectors.toList());
-        assertThat(recommendedWidgets).hasSize(3);
-        assertWidgetInfo(recommendedWidgets.get(0).info, mApp5Provider1);
-        assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider2);
-        assertWidgetInfo(recommendedWidgets.get(2).info, mApp1Provider1);
-    }
-
-    private void assertWidgetInfo(
-            LauncherAppWidgetProviderInfo actual, AppWidgetProviderInfo expected) {
-        assertThat(actual.provider).isEqualTo(expected.provider);
-        assertThat(actual.getUser()).isEqualTo(expected.getProfile());
-    }
-
-    private void waitUntilIdle() {
-        shadowOf(MODEL_EXECUTOR.getLooper()).idle();
-        shadowOf(MAIN_EXECUTOR.getLooper()).idle();
-    }
-
-    private WidgetsPredictionUpdateTask newWidgetsPredicationTask(List<AppTarget> appTargets) {
-        return new WidgetsPredictionUpdateTask(
-                new PredictorState(CONTAINER_WIDGETS_PREDICTION, "test_widgets_prediction"),
-                appTargets);
-    }
-
-    private final class FakeBgDataModelCallback implements BgDataModel.Callbacks {
-
-        private FixedContainerItems mRecommendedWidgets = null;
-
-        @Override
-        public void bindExtraContainerItems(FixedContainerItems item) {
-            mRecommendedWidgets = item;
-        }
-
-        @Override
-        public int getPageToBindSynchronously() {
-            return 0;
-        }
-
-        @Override
-        public void clearPendingBinds() { }
-
-        @Override
-        public void startBinding() { }
-
-        @Override
-        public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons) { }
-
-        @Override
-        public void bindScreens(IntArray orderedScreenIds) { }
-
-        @Override
-        public void finishFirstPageBind(ViewOnDrawExecutor executor) { }
-
-        @Override
-        public void finishBindingItems(int pageBoundFirst) { }
-
-        @Override
-        public void preAddApps() { }
-
-        @Override
-        public void bindAppsAdded(IntArray newScreens, ArrayList<ItemInfo> addNotAnimated,
-                ArrayList<ItemInfo> addAnimated) { }
-
-        @Override
-        public void bindIncrementalDownloadProgressUpdated(AppInfo app) { }
-
-        @Override
-        public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) { }
-
-        @Override
-        public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets) { }
-
-        @Override
-        public void bindRestoreItemsChange(HashSet<ItemInfo> updates) { }
-
-        @Override
-        public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher) { }
-
-        @Override
-        public void bindAllWidgets(List<WidgetsListBaseEntry> widgets) { }
-
-        @Override
-        public void onPageBoundSynchronously(int page) { }
-
-        @Override
-        public void executeOnNextDraw(ViewOnDrawExecutor executor) { }
-
-        @Override
-        public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap) { }
-
-        @Override
-        public void bindAllApplications(AppInfo[] apps, int flags) { }
-    }
-}
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
deleted file mode 100644
index 9df9ab1..0000000
--- a/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import static com.android.launcher3.util.LauncherUIHelper.doLayout;
-
-import android.app.ActivityManager.RunningTaskInfo;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-
-import com.android.quickstep.fallback.FallbackRecentsView;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.android.controller.ActivityController;
-import org.robolectric.annotation.LooperMode;
-import org.robolectric.annotation.LooperMode.Mode;
-import org.robolectric.shadows.ShadowLooper;
-import org.robolectric.util.ReflectionHelpers;
-
-
-@RunWith(RobolectricTestRunner.class)
-@LooperMode(Mode.PAUSED)
-@org.junit.Ignore
-public class RecentsActivityTest {
-
-    @Test
-    public void testRecentsActivityCreates() {
-        ActivityController<RecentsActivity> controller =
-                Robolectric.buildActivity(RecentsActivity.class);
-
-        RecentsActivity launcher = controller.setup().get();
-        doLayout(launcher);
-
-        // TODO: Ensure that LauncherAppState is not created
-    }
-
-    @Test
-    public void testRecents_showCurrentTask() {
-        ActivityController<RecentsActivity> controller =
-                Robolectric.buildActivity(RecentsActivity.class);
-
-        RecentsActivity activity = controller.setup().get();
-        doLayout(activity);
-
-        FallbackRecentsView frv = activity.getOverviewPanel();
-
-        RunningTaskInfo placeholderTask = new RunningTaskInfo();
-        placeholderTask.taskId = 22;
-        frv.showCurrentTask(placeholderTask);
-        doLayout(activity);
-
-        ThumbnailData thumbnailData = new ThumbnailData();
-        ReflectionHelpers.setField(thumbnailData, "thumbnail",
-                Bitmap.createBitmap(300, 500, Config.ARGB_8888));
-        frv.switchToScreenshot(thumbnailData, () -> { });
-        ShadowLooper.idleMainLooper();
-    }
-}
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
deleted file mode 100644
index fd93d98..0000000
--- a/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2020 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 static android.view.Display.DEFAULT_DISPLAY;
-
-import static org.mockito.Mockito.mock;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.hardware.display.DisplayManager;
-import android.view.Surface;
-import android.view.SurfaceControl;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.shadows.LShadowDisplay;
-import com.android.launcher3.util.DisplayController;
-import com.android.quickstep.LauncherActivityInterface;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
-
-import org.hamcrest.Description;
-import org.hamcrest.TypeSafeMatcher;
-import org.junit.Assert;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.LooperMode;
-import org.robolectric.annotation.LooperMode.Mode;
-import org.robolectric.shadow.api.Shadow;
-import org.robolectric.shadows.ShadowDisplayManager;
-
-@RunWith(RobolectricTestRunner.class)
-@LooperMode(Mode.PAUSED)
-public class TaskViewSimulatorTest {
-
-    @Test
-    public void taskProperlyScaled_portrait_noRotation_sameInsets1() {
-        new TaskMatrixVerifier()
-                .withLauncherSize(1200, 2450)
-                .withInsets(new Rect(0, 80, 0, 120))
-                .verifyNoTransforms();
-    }
-
-    @Test
-    public void taskProperlyScaled_portrait_noRotation_sameInsets2() {
-        new TaskMatrixVerifier()
-                .withLauncherSize(1200, 2450)
-                .withInsets(new Rect(55, 80, 55, 120))
-                .verifyNoTransforms();
-    }
-
-    @Test
-    public void taskProperlyScaled_landscape_noRotation_sameInsets1() {
-        new TaskMatrixVerifier()
-                .withLauncherSize(2450, 1250)
-                .withInsets(new Rect(0, 80, 0, 40))
-                .verifyNoTransforms();
-    }
-
-    @Test
-    public void taskProperlyScaled_landscape_noRotation_sameInsets2() {
-        new TaskMatrixVerifier()
-                .withLauncherSize(2450, 1250)
-                .withInsets(new Rect(0, 80, 120, 0))
-                .verifyNoTransforms();
-    }
-
-    @Test
-    public void taskProperlyScaled_landscape_noRotation_sameInsets3() {
-        new TaskMatrixVerifier()
-                .withLauncherSize(2450, 1250)
-                .withInsets(new Rect(55, 80, 55, 120))
-                .verifyNoTransforms();
-    }
-
-    @Test
-    public void taskProperlyScaled_landscape_rotated() {
-        new TaskMatrixVerifier()
-                .withLauncherSize(1200, 2450)
-                .withInsets(new Rect(0, 80, 0, 120))
-                .withAppBounds(
-                        new Rect(0, 0, 2450, 1200),
-                        new Rect(0, 80, 0, 120),
-                        Surface.ROTATION_90)
-                .verifyNoTransforms();
-    }
-
-    private static class TaskMatrixVerifier extends TransformParams {
-
-        private final Context mContext = RuntimeEnvironment.application;
-
-        private Rect mAppBounds = new Rect();
-        private Rect mLauncherInsets = new Rect();
-
-        private Rect mAppInsets;
-
-        private int mAppRotation = -1;
-        private DeviceProfile mDeviceProfile;
-
-        TaskMatrixVerifier withLauncherSize(int width, int height) {
-            ShadowDisplayManager.changeDisplay(DEFAULT_DISPLAY,
-                    String.format("w%sdp-h%sdp-mdpi", width, height));
-            if (mAppBounds.isEmpty()) {
-                mAppBounds.set(0, 0, width, height);
-            }
-            return this;
-        }
-
-        TaskMatrixVerifier withInsets(Rect insets) {
-            LShadowDisplay shadowDisplay = Shadow.extract(
-                    mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY));
-            shadowDisplay.setInsets(insets);
-            mLauncherInsets.set(insets);
-            return this;
-        }
-
-        TaskMatrixVerifier withAppBounds(Rect bounds, Rect insets, int appRotation) {
-            mAppBounds.set(bounds);
-            mAppInsets = insets;
-            mAppRotation = appRotation;
-            return this;
-        }
-
-        void verifyNoTransforms() {
-            mDeviceProfile = InvariantDeviceProfile.INSTANCE.get(mContext)
-                    .getDeviceProfile(mContext);
-            mDeviceProfile.updateInsets(mLauncherInsets);
-
-            TaskViewSimulator tvs = new TaskViewSimulator(mContext,
-                    LauncherActivityInterface.INSTANCE);
-            tvs.setDp(mDeviceProfile);
-
-            int launcherRotation = DisplayController.INSTANCE.get(mContext).getInfo().rotation;
-            if (mAppRotation < 0) {
-                mAppRotation = launcherRotation;
-            }
-            tvs.getOrientationState().update(launcherRotation, mAppRotation);
-            if (mAppInsets == null) {
-                mAppInsets = new Rect(mLauncherInsets);
-            }
-            tvs.setPreviewBounds(mAppBounds, mAppInsets);
-
-            tvs.fullScreenProgress.value = 1;
-            tvs.recentsViewScale.value = tvs.getFullScreenScale();
-            tvs.apply(this);
-        }
-
-        @Override
-        public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
-            SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
-            proxy.onBuildTargetParams(builder, mock(RemoteAnimationTargetCompat.class), this);
-            return new SurfaceParams[] {builder.build()};
-        }
-
-        @Override
-        public void applySurfaceParams(SurfaceParams[] params) {
-            // Verify that the task position remains the same
-            RectF newAppBounds = new RectF(mAppBounds);
-            params[0].matrix.mapRect(newAppBounds);
-            Assert.assertThat(newAppBounds, new AlmostSame(mAppBounds));
-
-            System.err.println("Bounds mapped: " + mAppBounds + " => " + newAppBounds);
-        }
-    }
-
-    private static class AlmostSame extends TypeSafeMatcher<RectF>  {
-
-        // Allow 1px error margin to account for float to int conversions
-        private final float mError = 1f;
-        private final Rect mExpected;
-
-        AlmostSame(Rect expected) {
-            mExpected = expected;
-        }
-
-        @Override
-        protected boolean matchesSafely(RectF item) {
-            return Math.abs(item.left - mExpected.left) < mError
-                    && Math.abs(item.top - mExpected.top) < mError
-                    && Math.abs(item.right - mExpected.right) < mError
-                    && Math.abs(item.bottom - mExpected.bottom) < mError;
-        }
-
-        @Override
-        public void describeTo(Description description) {
-            description.appendValue(mExpected);
-        }
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index de0416b..e8ea671 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -22,21 +22,25 @@
 import static com.android.launcher3.LauncherState.NO_OFFSET;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
+import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
 
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.app.ActivityOptions;
-import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
-import android.content.ServiceConnection;
+import android.graphics.Insets;
+import android.hardware.SensorManager;
+import android.hardware.devicestate.DeviceStateManager;
 import android.os.Bundle;
 import android.os.CancellationSignal;
-import android.os.IBinder;
 import android.view.View;
+import android.view.WindowInsets;
 import android.window.SplashScreen;
 
 import androidx.annotation.Nullable;
@@ -56,6 +60,8 @@
 import com.android.launcher3.taskbar.TaskbarStateHandler;
 import com.android.launcher3.uioverrides.RecentsViewStateController;
 import com.android.launcher3.util.ActivityOptionsWrapper;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ObjectWrapper;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.quickstep.OverviewCommandHelper;
@@ -65,18 +71,24 @@
 import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskUtils;
-import com.android.quickstep.TouchInteractionService;
 import com.android.quickstep.TouchInteractionService.TISBinder;
+import com.android.quickstep.util.LauncherUnfoldAnimationController;
+import com.android.quickstep.util.ProxyScreenStatusProvider;
 import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.util.RemoteFadeOutAnimationListener;
 import com.android.quickstep.util.SplitSelectStateController;
+import com.android.quickstep.util.TISBindHelper;
 import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.SplitPlaceholderView;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.unfold.UnfoldTransitionFactory;
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
+import com.android.systemui.unfold.config.UnfoldTransitionConfig;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.List;
 import java.util.stream.Stream;
 
@@ -98,45 +110,63 @@
 
     private OverviewActionsView mActionsView;
 
+    private TISBindHelper mTISBindHelper;
     private @Nullable TaskbarManager mTaskbarManager;
     private @Nullable OverviewCommandHelper mOverviewCommandHelper;
     private @Nullable LauncherTaskbarUIController mTaskbarUIController;
-    private final ServiceConnection mTisBinderConnection = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
-            mTaskbarManager = ((TISBinder) iBinder).getTaskbarManager();
-            mTaskbarManager.setLauncher(BaseQuickstepLauncher.this);
 
-            mOverviewCommandHelper = ((TISBinder) iBinder).getOverviewCommandHelper();
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName componentName) { }
-    };
     private final TaskbarStateHandler mTaskbarStateHandler = new TaskbarStateHandler(this);
 
     // Will be updated when dragging from taskbar.
     private @Nullable DragOptions mNextWorkspaceDragOptions = null;
-    private SplitPlaceholderView mSplitPlaceholderView;
+
+    private @Nullable UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider;
+    private @Nullable LauncherUnfoldAnimationController mLauncherUnfoldAnimationController;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this);
         addMultiWindowModeChangedListener(mDepthController);
+        initUnfoldTransitionProgressProvider();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        if (mLauncherUnfoldAnimationController != null) {
+            mLauncherUnfoldAnimationController.onResume();
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        if (mLauncherUnfoldAnimationController != null) {
+            mLauncherUnfoldAnimationController.onPause();
+        }
+
+        super.onPause();
     }
 
     @Override
     public void onDestroy() {
         mAppTransitionManager.onActivityDestroyed();
+        if (mUnfoldTransitionProgressProvider != null) {
+            mUnfoldTransitionProgressProvider.destroy();
+        }
 
         SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
 
-
-        unbindService(mTisBinderConnection);
+        mTISBindHelper.onDestroy();
         if (mTaskbarManager != null) {
-            mTaskbarManager.setLauncher(null);
+            mTaskbarManager.clearActivity(this);
         }
+
+        if (mLauncherUnfoldAnimationController != null) {
+            mLauncherUnfoldAnimationController.onDestroy();
+        }
+
         super.onDestroy();
     }
 
@@ -272,32 +302,58 @@
 
         SysUINavigationMode.INSTANCE.get(this).updateMode();
         mActionsView = findViewById(R.id.overview_actions_view);
-        mSplitPlaceholderView = findViewById(R.id.split_placeholder);
         RecentsView overviewPanel = (RecentsView) getOverviewPanel();
-        mSplitPlaceholderView.init(
-                new SplitSelectStateController(mHandler, SystemUiProxy.INSTANCE.get(this))
-        );
-        overviewPanel.init(mActionsView, mSplitPlaceholderView);
+        SplitSelectStateController controller =
+                new SplitSelectStateController(mHandler, SystemUiProxy.INSTANCE.get(this));
+        overviewPanel.init(mActionsView, controller);
         mActionsView.setDp(getDeviceProfile());
         mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this));
 
         mAppTransitionManager = new QuickstepTransitionManager(this);
         mAppTransitionManager.registerRemoteAnimations();
+        mAppTransitionManager.registerRemoteTransitions();
 
-        bindService(new Intent(this, TouchInteractionService.class), mTisBinderConnection, 0);
+        mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
+    }
 
+    private void onTISConnected(TISBinder binder) {
+        mTaskbarManager = binder.getTaskbarManager();
+        mTaskbarManager.setActivity(this);
+        mOverviewCommandHelper = binder.getOverviewCommandHelper();
+    }
+
+    private void initUnfoldTransitionProgressProvider() {
+        final UnfoldTransitionConfig config = UnfoldTransitionFactory.createConfig(this);
+        if (config.isEnabled()) {
+            mUnfoldTransitionProgressProvider =
+                    UnfoldTransitionFactory.createUnfoldTransitionProgressProvider(
+                            this,
+                            config,
+                            ProxyScreenStatusProvider.INSTANCE,
+                            getSystemService(DeviceStateManager.class),
+                            getSystemService(SensorManager.class),
+                            getMainThreadHandler(),
+                            getMainExecutor()
+                    );
+
+            mLauncherUnfoldAnimationController = new LauncherUnfoldAnimationController(
+                    this,
+                    getWindowManager(),
+                    mUnfoldTransitionProgressProvider
+            );
+        }
     }
 
     public void setTaskbarUIController(LauncherTaskbarUIController taskbarUIController) {
         mTaskbarUIController = taskbarUIController;
     }
 
-    public <T extends OverviewActionsView> T getActionsView() {
-        return (T) mActionsView;
+    public @Nullable LauncherTaskbarUIController getTaskbarUIController() {
+        return mTaskbarUIController;
     }
 
-    public SplitPlaceholderView getSplitPlaceholderView() {
-        return mSplitPlaceholderView;
+    public <T extends OverviewActionsView> T getActionsView() {
+        return (T) mActionsView;
     }
 
     @Override
@@ -319,14 +375,15 @@
         return mDepthController;
     }
 
-    public @Nullable LauncherTaskbarUIController getTaskbarUIController() {
-        return mTaskbarUIController;
-    }
-
     public TaskbarStateHandler getTaskbarStateHandler() {
         return mTaskbarStateHandler;
     }
 
+    @Nullable
+    public UnfoldTransitionProgressProvider getUnfoldTransitionProgressProvider() {
+        return mUnfoldTransitionProgressProvider;
+    }
+
     @Override
     public boolean supportsAdaptiveIconAnimation(View clickedView) {
         return mAppTransitionManager.hasControlRemoteAppTransitionPermission()
@@ -375,14 +432,6 @@
     }
 
     @Override
-    public float getNormalTaskbarScale() {
-        if (mTaskbarUIController != null) {
-            return mTaskbarUIController.getTaskbarScaleOnHome();
-        }
-        return super.getNormalTaskbarScale();
-    }
-
-    @Override
     public void onDragLayerHierarchyChanged() {
         onLauncherStateOrFocusChanged();
     }
@@ -436,8 +485,8 @@
     }
 
     @Override
-    public void finishBindingItems(int pageBoundFirst) {
-        super.finishBindingItems(pageBoundFirst);
+    public void finishBindingItems(IntSet pagesBoundFirst) {
+        super.finishBindingItems(pagesBoundFirst);
         // Instantiate and initialize WellbeingModel now that its loading won't interfere with
         // populating workspace.
         // TODO: Find a better place for this
@@ -505,4 +554,35 @@
     public void setHintUserWillBeActive() {
         addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
     }
+
+    @Override
+    public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) {
+        super.onDisplayInfoChanged(context, info, flags);
+        // When changing screens, force moving to rest state similar to StatefulActivity.onStop, as
+        // StatefulActivity isn't called consistently.
+        if ((flags & CHANGE_ACTIVE_SCREEN) != 0) {
+            getStateManager().moveToRestState();
+        }
+    }
+
+    @Override
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        super.dump(prefix, fd, writer, args);
+        if (mDepthController != null) {
+            mDepthController.dump(prefix, writer);
+        }
+    }
+
+    @Override
+    public void updateWindowInsets(WindowInsets.Builder updatedInsetsBuilder,
+            WindowInsets oldInsets) {
+        // Override the tappable insets to be 0 on the bottom for gesture nav (otherwise taskbar
+        // would count towards it). This is used for the bottom protection in All Apps for example.
+        if (SysUINavigationMode.getMode(this) == NO_BUTTON) {
+            Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
+            Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
+                    oldTappableInsets.right, 0);
+            updatedInsetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
+        }
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index b557779..ddb20a1 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -28,18 +28,24 @@
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.Utilities.mapBoundToRange;
 import static com.android.launcher3.Utilities.postAsyncCallback;
+import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
 import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
+import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_BACK_SWIPE_HOME_ANIMATION;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_SCRIM_FOR_APP_LAUNCH;
 import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
 import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
+import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
 import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
-import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
+import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
+import static com.android.launcher3.views.FloatingIconView.getFloatingIconView;
 import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
 import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
 import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
@@ -51,20 +57,24 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.CancellationSignal;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.util.Pair;
 import android.util.Size;
 import android.view.SurfaceControl;
@@ -86,8 +96,12 @@
 import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.taskbar.LauncherTaskbarUIController;
+import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.ActivityOptionsWrapper;
+import com.android.launcher3.util.DynamicResource;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.launcher3.util.ObjectWrapper;
 import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.FloatingIconView;
@@ -96,8 +110,11 @@
 import com.android.quickstep.RemoteAnimationTargets;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskViewUtils;
+import com.android.quickstep.util.AppCloseConfig;
 import com.android.quickstep.util.MultiValueUpdateListener;
+import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.util.StaggeredWorkspaceAnim;
 import com.android.quickstep.util.SurfaceTransactionApplier;
 import com.android.quickstep.util.WorkspaceRevealAnim;
 import com.android.quickstep.views.FloatingWidgetView;
@@ -422,6 +439,10 @@
                         4 - rotationChange);
             }
         }
+        // TODO(b/196637509): don't do this for immersive apps.
+        if (mDeviceProfile.isTaskbarPresentInApps) {
+            bounds.bottom -= mDeviceProfile.taskbarSize;
+        }
         return bounds;
     }
 
@@ -506,7 +527,10 @@
 
             final boolean scrimEnabled = ENABLE_SCRIM_FOR_APP_LAUNCH.get();
             if (scrimEnabled) {
-                int scrimColor = Themes.getAttrColor(mLauncher, R.attr.overviewScrimColor);
+                boolean useTaskbarColor = mDeviceProfile.isTaskbarPresentInApps;
+                int scrimColor = useTaskbarColor
+                        ? mLauncher.getResources().getColor(R.color.taskbar_background)
+                        : Themes.getAttrColor(mLauncher, R.attr.overviewScrimColor);
                 int scrimColorTrans = ColorUtils.setAlphaComponent(scrimColor, 0);
                 int[] colors = isAppOpening
                         ? new int[]{scrimColorTrans, scrimColor}
@@ -519,6 +543,30 @@
                             colors);
                     scrim.setDuration(CONTENT_SCRIM_DURATION);
                     scrim.setInterpolator(DEACCEL_1_5);
+
+                    if (useTaskbarColor) {
+                        // Hide the taskbar background color since it would duplicate the scrim.
+                        scrim.addListener(new AnimatorListenerAdapter() {
+                            @Override
+                            public void onAnimationStart(Animator animation) {
+                                LauncherTaskbarUIController taskbarUIController =
+                                        mLauncher.getTaskbarUIController();
+                                if (taskbarUIController != null) {
+                                    taskbarUIController.forceHideBackground(true);
+                                }
+                            }
+
+                            @Override
+                            public void onAnimationEnd(Animator animation) {
+                                LauncherTaskbarUIController taskbarUIController =
+                                        mLauncher.getTaskbarUIController();
+                                if (taskbarUIController != null) {
+                                    taskbarUIController.forceHideBackground(false);
+                                }
+                            }
+                        });
+                    }
+
                     launcherAnimator.play(scrim);
                 }
             }
@@ -633,6 +681,10 @@
                 if (v instanceof BubbleTextView) {
                     ((BubbleTextView) v).setStayPressed(false);
                 }
+                LauncherTaskbarUIController taskbarController = mLauncher.getTaskbarUIController();
+                if (taskbarController != null) {
+                    taskbarController.showEdu();
+                }
                 openingTargets.release();
             }
         });
@@ -641,7 +693,7 @@
                 ? Math.max(crop.width(), crop.height()) / 2f
                 : 0f;
         final float finalWindowRadius = mDeviceProfile.isMultiWindowMode
-                ? 0 : getWindowCornerRadius(mLauncher.getResources());
+                ? 0 : getWindowCornerRadius(mLauncher);
         final float finalShadowRadius = appTargetsAreTranslucent ? 0 : mMaxShadowRadius;
 
         MultiValueUpdateListener listener = new MultiValueUpdateListener() {
@@ -804,7 +856,13 @@
         // Since we added a start delay, call update here to init the FloatingIconView properly.
         listener.onUpdate(0, true /* initOnly */);
 
-        animatorSet.playTogether(appAnimator, getBackgroundAnimator(appTargets));
+        // If app targets are translucent, do not animate the background as it causes a visible
+        // flicker when it resets itself at the end of its animation.
+        if (appTargetsAreTranslucent) {
+            animatorSet.play(appAnimator);
+        } else {
+            animatorSet.playTogether(appAnimator, getBackgroundAnimator());
+        }
         return animatorSet;
     }
 
@@ -832,7 +890,7 @@
         }
 
         final float finalWindowRadius = mDeviceProfile.isMultiWindowMode
-                ? 0 : getWindowCornerRadius(mLauncher.getResources());
+                ? 0 : getWindowCornerRadius(mLauncher);
         final FloatingWidgetView floatingView = FloatingWidgetView.getFloatingWidgetView(mLauncher,
                 v, widgetBackgroundBounds,
                 new Size(windowTargetBounds.width(), windowTargetBounds.height()),
@@ -941,11 +999,20 @@
             }
         });
 
-        animatorSet.playTogether(appAnimator, getBackgroundAnimator(appTargets));
+        // If app targets are translucent, do not animate the background as it causes a visible
+        // flicker when it resets itself at the end of its animation.
+        if (appTargetsAreTranslucent) {
+            animatorSet.play(appAnimator);
+        } else {
+            animatorSet.playTogether(appAnimator, getBackgroundAnimator());
+        }
         return animatorSet;
     }
 
-    private ObjectAnimator getBackgroundAnimator(RemoteAnimationTargetCompat[] appTargets) {
+    /**
+     * Returns animator that controls depth/blur of the background.
+     */
+    private ObjectAnimator getBackgroundAnimator() {
         // When launching an app from overview that doesn't map to a task, we still want to just
         // blur the wallpaper instead of the launcher surface as well
         boolean allowBlurringLauncher = mLauncher.getStateManager().getState() != OVERVIEW;
@@ -1044,7 +1111,7 @@
             mLauncherOpenTransition = RemoteAnimationAdapterCompat.buildRemoteTransition(
                     new LauncherAnimationRunner(mHandler, mWallpaperOpenTransitionRunner,
                             false /* startAtFrontOfQueue */));
-            mLauncherOpenTransition.addHomeOpenCheck();
+            mLauncherOpenTransition.addHomeOpenCheck(mLauncher.getComponentName());
             SystemUiProxy.INSTANCE.getNoCreate().registerRemoteTransition(mLauncherOpenTransition);
         }
     }
@@ -1085,7 +1152,16 @@
     }
 
     private boolean launcherIsATargetWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
-        return taskIsATargetWithMode(targets, mLauncher.getTaskId(), mode);
+        for (RemoteAnimationTargetCompat target : targets) {
+            if (target.mode == mode && target.taskInfo != null
+                    // Compare component name instead of task-id because transitions will promote
+                    // the target up to the root task while getTaskId returns the leaf.
+                    && target.taskInfo.topActivity != null
+                    && target.taskInfo.topActivity.equals(mLauncher.getComponentName())) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
@@ -1105,7 +1181,7 @@
         ValueAnimator unlockAnimator = ValueAnimator.ofFloat(0, 1);
         unlockAnimator.setDuration(CLOSING_TRANSITION_DURATION_MS);
         float cornerRadius = mDeviceProfile.isMultiWindowMode ? 0 :
-                QuickStepContract.getWindowCornerRadius(mLauncher.getResources());
+                QuickStepContract.getWindowCornerRadius(mLauncher);
         unlockAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
@@ -1135,10 +1211,184 @@
     }
 
     /**
-     * Animator that controls the transformations of the windows the targets that are closing.
+     * Returns view on the workspace that corresponds to the closing app in the list of app targets
      */
-    private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets) {
+    private @Nullable View findWorkspaceView(RemoteAnimationTargetCompat[] appTargets) {
+        for (RemoteAnimationTargetCompat appTarget : appTargets) {
+            if (appTarget.mode == MODE_CLOSING) {
+                View workspaceView = findWorkspaceView(appTarget);
+                if (workspaceView != null) {
+                    return workspaceView;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns view on the workspace that corresponds to the {@param runningTaskTarget}.
+     */
+    private @Nullable View findWorkspaceView(RemoteAnimationTargetCompat runningTaskTarget) {
+        if (runningTaskTarget == null || runningTaskTarget.taskInfo == null) {
+            return null;
+        }
+
+        final ComponentName[] taskInfoActivities = new ComponentName[] {
+                runningTaskTarget.taskInfo.baseActivity,
+                runningTaskTarget.taskInfo.origActivity,
+                runningTaskTarget.taskInfo.realActivity,
+                runningTaskTarget.taskInfo.topActivity};
+
+        String packageName = null;
+        for (ComponentName component : taskInfoActivities) {
+            if (component != null && component.getPackageName() != null) {
+                packageName = component.getPackageName();
+                break;
+            }
+        }
+
+        if (packageName == null) {
+            return null;
+        }
+
+        // Find the associated item info for the launch cookie (if available), note that predicted
+        // apps actually have an id of -1, so use another default id here
+        final ArrayList<IBinder> launchCookies = runningTaskTarget.taskInfo.launchCookies == null
+                ? new ArrayList<>()
+                : runningTaskTarget.taskInfo.launchCookies;
+
+        int launchCookieItemId = NO_MATCHING_ID;
+        for (IBinder cookie : launchCookies) {
+            Integer itemId = ObjectWrapper.unwrap(cookie);
+            if (itemId != null) {
+                launchCookieItemId = itemId;
+                break;
+            }
+        }
+
+        return mLauncher.getWorkspace().getFirstMatchForAppClose(launchCookieItemId,
+                packageName, UserHandle.of(runningTaskTarget.taskInfo.userId));
+    }
+
+    private @NonNull RectF getDefaultWindowTargetRect() {
+        RecentsView recentsView = mLauncher.getOverviewPanel();
+        PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
+        DeviceProfile dp = mLauncher.getDeviceProfile();
+        final int halfIconSize = dp.iconSizePx / 2;
+        float primaryDimension = orientationHandler
+                .getPrimaryValue(dp.availableWidthPx, dp.availableHeightPx);
+        float secondaryDimension = orientationHandler
+                .getSecondaryValue(dp.availableWidthPx, dp.availableHeightPx);
+        final float targetX =  primaryDimension / 2f;
+        final float targetY = secondaryDimension - dp.hotseatBarSizePx;
+        return new RectF(targetX - halfIconSize, targetY - halfIconSize,
+                targetX + halfIconSize, targetY + halfIconSize);
+    }
+
+    /**
+     * Closing animator that animates the window into its final location on the workspace.
+     */
+    private void getClosingWindowAnimators(AnimatorSet animation,
+            RemoteAnimationTargetCompat[] targets, View workspaceView) {
+        FloatingIconView floatingIconView = null;
+        FloatingWidgetView floatingWidget = null;
+        RectF targetRect = new RectF();
+
+        RemoteAnimationTargetCompat runningTaskTarget = null;
+        boolean isTransluscent = false;
+        for (RemoteAnimationTargetCompat target : targets) {
+            if (target.mode == MODE_CLOSING) {
+                runningTaskTarget = target;
+                isTransluscent = runningTaskTarget.isTranslucent;
+                break;
+            }
+        }
+
+        // Get floating view and target rect.
+        if (workspaceView instanceof LauncherAppWidgetHostView) {
+            Size windowSize = new Size(mDeviceProfile.availableWidthPx,
+                    mDeviceProfile.availableHeightPx);
+            int fallbackBackgroundColor =
+                    FloatingWidgetView.getDefaultBackgroundColor(mLauncher, runningTaskTarget);
+            floatingWidget = FloatingWidgetView.getFloatingWidgetView(mLauncher,
+                    (LauncherAppWidgetHostView) workspaceView, targetRect, windowSize,
+                    mDeviceProfile.isMultiWindowMode ? 0 : getWindowCornerRadius(mLauncher),
+                    isTransluscent, fallbackBackgroundColor);
+        } else if (workspaceView != null) {
+            floatingIconView = getFloatingIconView(mLauncher, workspaceView,
+                    true /* hideOriginal */, targetRect, false /* isOpening */);
+        } else {
+            targetRect.set(getDefaultWindowTargetRect());
+        }
+
+        final RectF startRect = new RectF(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
+        RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mLauncher,
+                mDeviceProfile);
+
+        // Hook up floating views to the closing window animators.
+        if (floatingIconView != null) {
+            anim.addAnimatorListener(floatingIconView);
+            floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged);
+            floatingIconView.setFastFinishRunnable(anim::end);
+            FloatingIconView finalFloatingIconView = floatingIconView;
+
+            // We want the window alpha to be 0 once this threshold is met, so that the
+            // FolderIconView can be seen morphing into the icon shape.
+            final float windowAlphaThreshold = 1f - SHAPE_PROGRESS_DURATION;
+
+            RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect) {
+                @Override
+                public void onUpdate(@Nullable AppCloseConfig values, RectF currentRectF,
+                        float progress) {
+                    finalFloatingIconView.update(1f, 255 /* fgAlpha */, currentRectF, progress,
+                            windowAlphaThreshold, getCornerRadius(progress), false);
+
+                    super.onUpdate(values, currentRectF, progress);
+                }
+            };
+            anim.addOnUpdateListener(runner);
+        } else if (floatingWidget != null) {
+            anim.addAnimatorListener(floatingWidget);
+            floatingWidget.setOnTargetChangeListener(anim::onTargetPositionChanged);
+            floatingWidget.setFastFinishRunnable(anim::end);
+
+            final float floatingWidgetAlpha = isTransluscent ? 0 : 1;
+            FloatingWidgetView finalFloatingWidget = floatingWidget;
+            RectFSpringAnim.OnUpdateListener  runner = new SpringAnimRunner(targets, targetRect) {
+                @Override
+                public void onUpdate(@Nullable AppCloseConfig values, RectF currentRectF,
+                        float progress) {
+                    final float fallbackBackgroundAlpha =
+                            1 - mapBoundToRange(progress, 0.8f, 1, 0, 1, EXAGGERATED_EASE);
+                    final float foregroundAlpha =
+                            mapBoundToRange(progress, 0.5f, 1, 0, 1, EXAGGERATED_EASE);
+                    finalFloatingWidget.update(currentRectF, floatingWidgetAlpha, foregroundAlpha,
+                            fallbackBackgroundAlpha, 1 - progress);
+
+                    super.onUpdate(values, currentRectF, progress);
+                }
+            };
+            anim.addOnUpdateListener(runner);
+        }
+
+        // Use a fixed velocity to start the animation.
+        float velocityPxPerS = DynamicResource.provider(mLauncher)
+                .getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
+        PointF velocity = new PointF(0, -velocityPxPerS);
+        animation.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
+                true /* animateOverviewScrim */, workspaceView).getAnimators());
+        animation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                anim.start(mLauncher, velocity);
+            }
+        });
+    }
+
+    /**
+     * Closing window animator that moves the window down and offscreen.
+     */
+    private Animator getFallbackClosingWindowAnimators(RemoteAnimationTargetCompat[] appTargets) {
         final int rotationChange = getRotationChange(appTargets);
         SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer);
         Matrix matrix = new Matrix();
@@ -1147,7 +1397,7 @@
         ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1);
         int duration = CLOSING_TRANSITION_DURATION_MS;
         float windowCornerRadius = mDeviceProfile.isMultiWindowMode
-                ? 0 : getWindowCornerRadius(mLauncher.getResources());
+                ? 0 : getWindowCornerRadius(mLauncher);
         float startShadowRadius = areAllTargetsTranslucent(appTargets) ? 0 : mMaxShadowRadius;
         closingAnimator.setDuration(duration);
         closingAnimator.addUpdateListener(new MultiValueUpdateListener() {
@@ -1277,7 +1527,7 @@
                 LauncherAnimationRunner.AnimationResult result) {
             if (mLauncher.isDestroyed()) {
                 AnimatorSet anim = new AnimatorSet();
-                anim.play(getClosingWindowAnimators(appTargets, wallpaperTargets));
+                anim.play(getFallbackClosingWindowAnimators(appTargets));
                 result.setAnimation(anim, mLauncher.getApplicationContext());
                 return;
             }
@@ -1304,9 +1554,25 @@
 
             if (anim == null) {
                 anim = new AnimatorSet();
-                anim.play(mFromUnlock
-                        ? getUnlockWindowAnimator(appTargets, wallpaperTargets)
-                        : getClosingWindowAnimators(appTargets, wallpaperTargets));
+
+                View workspaceView = findWorkspaceView(appTargets);
+                boolean isWorkspaceViewVisible = workspaceView != null
+                        && !mLauncher.isInState(LauncherState.ALL_APPS);
+                boolean playFallBackAnimation = !isWorkspaceViewVisible
+                        && (launcherIsATargetWithMode(appTargets, MODE_OPENING)
+                        || mLauncher.isForceInvisible());
+
+                boolean playWorkspaceReveal = true;
+                if (mFromUnlock) {
+                    anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets));
+                } else if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get()
+                        && !playFallBackAnimation) {
+                    getClosingWindowAnimators(anim, appTargets, workspaceView);
+                    // We play StaggeredWorkspaceAnim as a part of the closing window animation.
+                    playWorkspaceReveal = false;
+                } else {
+                    anim.play(getFallbackClosingWindowAnimators(appTargets));
+                }
 
                 // Normally, we run the launcher content animation when we are transitioning
                 // home, but if home is already visible, then we don't want to animate the
@@ -1334,7 +1600,9 @@
                             }
                         });
                     } else {
-                        anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators());
+                        if (playWorkspaceReveal) {
+                            anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators());
+                        }
                     }
                 }
             }
@@ -1483,4 +1751,73 @@
             mTransitionManager.mTaskStartParams.put(taskId, Pair.create(supportedType, color));
         }
     }
+
+    /**
+     * RectFSpringAnim update listener to be used for app to home animation.
+     */
+    private class SpringAnimRunner implements RectFSpringAnim.OnUpdateListener {
+        private final RemoteAnimationTargetCompat[] mAppTargets;
+        private final Matrix mMatrix = new Matrix();
+        private final Point mTmpPos = new Point();
+        private final Rect mCurrentRect = new Rect();
+        private final float mStartRadius;
+        private final float mEndRadius;
+        private final SurfaceTransactionApplier mSurfaceApplier;
+
+        SpringAnimRunner(RemoteAnimationTargetCompat[] appTargets, RectF targetRect) {
+            mAppTargets = appTargets;
+            mStartRadius = QuickStepContract.getWindowCornerRadius(mLauncher);
+            mEndRadius = Math.max(1, targetRect.width()) / 2f;
+            mSurfaceApplier = new SurfaceTransactionApplier(mDragLayer);
+        }
+
+        public float getCornerRadius(float progress) {
+            return Utilities.mapRange(progress, mStartRadius, mEndRadius);
+        }
+
+        @Override
+        public void onUpdate(@Nullable AppCloseConfig values, RectF currentRectF, float progress) {
+            SurfaceParams[] params = new SurfaceParams[mAppTargets.length];
+            for (int i = mAppTargets.length - 1; i >= 0; i--) {
+                RemoteAnimationTargetCompat target = mAppTargets[i];
+                SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+
+                if (target.localBounds != null) {
+                    mTmpPos.set(target.localBounds.left, target.localBounds.top);
+                } else {
+                    mTmpPos.set(target.position.x, target.position.y);
+                }
+
+                if (target.mode == MODE_CLOSING) {
+                    float alpha = getWindowAlpha(progress);
+                    currentRectF.round(mCurrentRect);
+
+                    builder.withMatrix(mMatrix)
+                            .withWindowCrop(mCurrentRect)
+                            .withAlpha(alpha)
+                            .withCornerRadius(getCornerRadius(progress));
+                } else if (target.mode == MODE_OPENING) {
+                    mMatrix.setTranslate(mTmpPos.x, mTmpPos.y);
+                    builder.withMatrix(mMatrix)
+                            .withAlpha(1f);
+                }
+                params[i] = builder.build();
+            }
+            mSurfaceApplier.scheduleApply(params);
+        }
+
+        protected float getWindowAlpha(float progress) {
+            // Alpha interpolates between [1, 0] between progress values [start, end]
+            final float start = 0f;
+            final float end = 0.85f;
+
+            if (progress <= start) {
+                return 1f;
+            }
+            if (progress >= end) {
+                return 0f;
+            }
+            return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5);
+        }
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
index 63a569a..1b0f967 100644
--- a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -282,8 +282,7 @@
 
     @Override
     public void setInsets(Rect insets, DeviceProfile grid) {
-        int leftRightPadding = grid.desiredWorkspaceLeftRightMarginPx
-                + grid.cellLayoutPaddingLeftRightPx;
+        int leftRightPadding = grid.allAppsLeftRightPadding;
         setPadding(leftRightPadding, getPaddingTop(), leftRightPadding, getPaddingBottom());
     }
 
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index 6afbf9a..9ad8bb2 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -48,6 +48,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
 @TargetApi(Build.VERSION_CODES.P)
 public class PredictionRowView extends LinearLayout implements
@@ -170,10 +171,9 @@
     private void applyPredictedApps(List<ItemInfo> items) {
         mPendingPredictedItems = null;
         mPredictedApps.clear();
-        items.stream()
+        mPredictedApps.addAll(items.stream()
                 .filter(itemInfo -> itemInfo instanceof WorkspaceItemInfo)
-                .map(itemInfo -> (WorkspaceItemInfo) itemInfo)
-                .forEach(mPredictedApps::add);
+                .map(itemInfo -> (WorkspaceItemInfo) itemInfo).collect(Collectors.toList()));
         applyPredictionApps();
     }
 
@@ -249,8 +249,7 @@
 
     @Override
     public void setInsets(Rect insets, DeviceProfile grid) {
-        int leftRightPadding = grid.desiredWorkspaceLeftRightMarginPx
-                + grid.cellLayoutPaddingLeftRightPx;
+        int leftRightPadding = grid.allAppsLeftRightPadding;
         setPadding(leftRightPadding, getPaddingTop(), leftRightPadding, getPaddingBottom());
     }
 
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index a6844e4..680012c 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -15,18 +15,22 @@
  */
 package com.android.launcher3.hybridhotseat;
 
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent
-        .LAUNCHER_HOTSEAT_EDU_ONLY_TIP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_ONLY_TIP;
 
 import android.content.Intent;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.Gravity;
 import android.view.View;
 
+import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.FolderInfo;
@@ -47,6 +51,8 @@
  */
 public class HotseatEduController {
 
+    private static final String TAG = "HotseatEduController";
+
     public static final String SETTINGS_ACTION =
             "android.settings.ACTION_CONTENT_SUGGESTIONS_SETTINGS";
 
@@ -188,8 +194,12 @@
                     .getInt(LauncherSettings.Settings.EXTRA_VALUE);
             mNewScreens = IntArray.wrap(pageId);
         }
-        for (int i = 0; i < mLauncher.getDeviceProfile().numShownHotseatIcons; i++) {
-            View child = mHotseat.getChildAt(i, 0);
+        boolean isPortrait = !mLauncher.getDeviceProfile().isVerticalBarLayout();
+        int hotseatItemsNum = mLauncher.getDeviceProfile().numShownHotseatIcons;
+        for (int i = 0; i < hotseatItemsNum; i++) {
+            int x = isPortrait ? i : 0;
+            int y = isPortrait ? 0 : hotseatItemsNum - i - 1;
+            View child = mHotseat.getChildAt(x, y);
             if (child == null || child.getTag() == null) continue;
             ItemInfo tag = (ItemInfo) child.getTag();
             if (tag.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) continue;
@@ -229,8 +239,7 @@
                     R.string.hotseat_prediction_settings, null,
                     () -> mLauncher.startActivity(getSettingsIntent()));
         } else {
-            new ArrowTipView(mLauncher).show(
-                    mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
+            showHotseatArrowTip(true, mLauncher.getString(R.string.hotseat_tip_no_empty_slots));
         }
     }
 
@@ -251,15 +260,50 @@
         if (requiresMigration && canMigrateToFirstPage) {
             showDialog();
         } else {
-            new ArrowTipView(mLauncher).show(mLauncher.getString(
+            if (showHotseatArrowTip(requiresMigration, mLauncher.getString(
                     requiresMigration ? R.string.hotseat_tip_no_empty_slots
-                            : R.string.hotseat_auto_enrolled),
-                    mHotseat.getTop());
-            mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_ONLY_TIP);
+                            : R.string.hotseat_auto_enrolled))) {
+                mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_ONLY_TIP);
+            }
             finishOnboarding();
         }
     }
 
+    /**
+     * Finds a child suitable child in hotseat and shows arrow tip pointing at it.
+     *
+     * @param usePinned used to determine target view. If true, will use the first matching pinned
+     *                  item. Otherwise, will use the first predicted child
+     * @param message   String to be shown inside the arrowView
+     * @return whether suitable child was found and tip was shown
+     */
+    private boolean showHotseatArrowTip(boolean usePinned, String message) {
+        int childCount = mHotseat.getShortcutsAndWidgets().getChildCount();
+        boolean isPortrait = !mLauncher.getDeviceProfile().isVerticalBarLayout();
+
+        BubbleTextView tipTargetView = null;
+        for (int i = childCount - 1; i > -1; i--) {
+            int x = isPortrait ? i : 0;
+            int y = isPortrait ? 0 : i;
+            View v = mHotseat.getShortcutsAndWidgets().getChildAt(x, y);
+            if (v instanceof BubbleTextView && v.getTag() instanceof WorkspaceItemInfo) {
+                ItemInfo info = (ItemInfo) v.getTag();
+                boolean isPinned = info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+                if (isPinned == usePinned) {
+                    tipTargetView = (BubbleTextView) v;
+                    break;
+                }
+            }
+        }
+        if (tipTargetView == null) {
+            Log.e(TAG, "Unable to find suitable view for ArrowTip");
+            return false;
+        }
+        Rect bounds = Utilities.getViewBounds(tipTargetView);
+        new ArrowTipView(mLauncher).show(message, Gravity.END, bounds.centerX(), bounds.top);
+        return true;
+    }
+
     void showDialog() {
         if (mPredictedApps == null || mPredictedApps.isEmpty()) {
             return;
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index 14b0c5d..c41f2ce 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -77,6 +77,11 @@
         mContent = this;
     }
 
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        setTranslationShift(TRANSLATION_SHIFT_CLOSED);
+    }
 
     @Override
     protected void onFinishInflate() {
@@ -85,7 +90,7 @@
         mSampleHotseat = findViewById(R.id.sample_prediction);
 
         DeviceProfile grid = mActivityContext.getDeviceProfile();
-        Rect padding = grid.getHotseatLayoutPadding();
+        Rect padding = grid.getHotseatLayoutPadding(getContext());
 
         mSampleHotseat.getLayoutParams().height = grid.cellHeightPx;
         mSampleHotseat.setGridSize(grid.numShownHotseatIcons, 1);
@@ -200,9 +205,9 @@
         }
         AbstractFloatingView.closeAllOpenViews(mActivityContext);
         attachToContainer();
-        mActivityContext.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_SEEN);
         animateOpen();
         populatePreview(predictions);
+        mActivityContext.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_SEEN);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 85e5ab0..85d9f01 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -55,8 +55,8 @@
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.uioverrides.PredictedAppIcon;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.OnboardingPrefs;
-import com.android.launcher3.views.ArrowTipView;
 import com.android.launcher3.views.Snackbar;
 
 import java.util.ArrayList;
@@ -152,38 +152,15 @@
      */
     public void showEdu() {
         mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> {
-            if (mPredictedItems.isEmpty()) {
-                // launcher has empty predictions set
-                Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_disabled,
-                        R.string.hotseat_prediction_settings, null,
-                        () -> mLauncher.startActivity(getSettingsIntent()));
-            } else if (getPredictedIcons().size() >= (mHotSeatItemsCount + 1) / 2) {
-                showDiscoveryTip();
-            } else {
-                HotseatEduController eduController = new HotseatEduController(mLauncher);
-                eduController.setPredictedApps(mPredictedItems.stream()
-                        .map(i -> (WorkspaceItemInfo) i)
-                        .collect(Collectors.toList()));
-                eduController.showEdu();
-            }
+            HotseatEduController eduController = new HotseatEduController(mLauncher);
+            eduController.setPredictedApps(mPredictedItems.stream()
+                    .map(i -> (WorkspaceItemInfo) i)
+                    .collect(Collectors.toList()));
+            eduController.showEdu();
         }));
     }
 
     /**
-     * Shows educational tip for hotseat if user does not go through Tips app.
-     */
-    private void showDiscoveryTip() {
-        if (getPredictedIcons().isEmpty()) {
-            new ArrowTipView(mLauncher).show(
-                    mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
-        } else {
-            Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
-                    R.string.hotseat_prediction_settings, null,
-                    () -> mLauncher.startActivity(getSettingsIntent()));
-        }
-    }
-
-    /**
      * Returns if hotseat client has predictions
      */
     public boolean hasPredictions() {
@@ -200,6 +177,7 @@
         }
 
         int predictionIndex = 0;
+        int numViewsAnimated = 0;
         ArrayList<WorkspaceItemInfo> newItems = new ArrayList<>();
         // make sure predicted icon removal and filling predictions don't step on each other
         if (mIconRemoveAnimators != null && mIconRemoveAnimators.isRunning()) {
@@ -233,7 +211,11 @@
                     (WorkspaceItemInfo) mPredictedItems.get(predictionIndex++);
             if (isPredictedIcon(child) && child.isEnabled()) {
                 PredictedAppIcon icon = (PredictedAppIcon) child;
-                icon.applyFromWorkspaceItem(predictedItem);
+                boolean animateIconChange = icon.shouldAnimateIconChange(predictedItem);
+                icon.applyFromWorkspaceItem(predictedItem, animateIconChange, numViewsAnimated);
+                if (animateIconChange) {
+                    numViewsAnimated++;
+                }
                 icon.finishBinding(mPredictionLongClickListener);
             } else {
                 newItems.add(predictedItem);
@@ -262,10 +244,6 @@
         } else {
             removeOutlineDrawings();
         }
-
-        if (mLauncher.getTaskbarUIController() != null) {
-            mLauncher.getTaskbarUIController().onHotseatUpdated();
-        }
     }
 
     private void removeOutlineDrawings() {
@@ -496,6 +474,28 @@
                 .log(LAUNCHER_HOTSEAT_RANKED);
     }
 
+    /**
+     * Called when app/shortcut icon is removed by system. This is used to prune visible stale
+     * predictions while while waiting for AppAPrediction service to send new batch of predictions.
+     *
+     * @param matcher filter matching items that have been removed
+     */
+    public void onModelItemsRemoved(ItemInfoMatcher matcher) {
+        if (mPredictedItems.removeIf(matcher::matchesInfo)) {
+            fillGapsWithPrediction(true);
+        }
+    }
+
+    /**
+     * Called when user completes adding item requiring a config activity to the hotseat
+     */
+    public void onDeferredDrop(int cellX, int cellY) {
+        View child = mHotseat.getChildAt(cellX, cellY);
+        if (child instanceof PredictedAppIcon) {
+            removeIconWithoutNotify((PredictedAppIcon) child);
+        }
+    }
+
     private class PinPrediction extends SystemShortcut<QuickstepLauncher> {
 
         private PinPrediction(QuickstepLauncher target, ItemInfo itemInfo) {
diff --git a/quickstep/src/com/android/launcher3/model/AppEventProducer.java b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
index eed493d..b665db6 100644
--- a/quickstep/src/com/android/launcher3/model/AppEventProducer.java
+++ b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
@@ -30,6 +30,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_REMOVE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_FOLDER_CREATED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONRESUME;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
@@ -161,6 +162,11 @@
             if (isTrackedForHotseatPrediction(atomInfo)) {
                 sendEvent(atomInfo, ACTION_PIN, CONTAINER_HOTSEAT_PREDICTION);
             }
+        } else if (event == LAUNCHER_ONRESUME) {
+            AppTarget target = new AppTarget.Builder(new AppTargetId("id:launcher"),
+                    mContext.getPackageName(), Process.myUserHandle())
+                    .build();
+            sendEvent(target, atomInfo, ACTION_LAUNCH, CONTAINER_PREDICTION);
         }
     }
 
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index a9c2a5e..55a140d 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -25,7 +25,9 @@
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
 import static com.android.launcher3.Utilities.getDevicePrefs;
 import static com.android.launcher3.hybridhotseat.HotseatPredictionModel.convertDataModelToAppTargetBundle;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
+import android.app.StatsManager;
 import android.app.prediction.AppPredictionContext;
 import android.app.prediction.AppPredictionManager;
 import android.app.prediction.AppPredictor;
@@ -39,13 +41,14 @@
 import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
 import android.util.Log;
+import android.util.StatsEvent;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.InstanceIdSequence;
 import com.android.launcher3.model.BgDataModel.FixedContainerItems;
@@ -54,10 +57,10 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.IntSparseArrayMap;
 import com.android.launcher3.util.PersistedItemArray;
 import com.android.quickstep.logging.StatsLogCompatManager;
+import com.android.systemui.shared.system.SysUiStatsLog;
 
 import java.util.Collections;
 import java.util.List;
@@ -68,7 +71,7 @@
 /**
  * Model delegate which loads prediction items
  */
-public class QuickstepModelDelegate extends ModelDelegate implements OnIDPChangeListener {
+public class QuickstepModelDelegate extends ModelDelegate {
 
     public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state";
     private static final String LAST_SNAPSHOT_TIME_MILLIS = "LAST_SNAPSHOT_TIME_MILLIS";
@@ -86,6 +89,7 @@
 
     private final InvariantDeviceProfile mIDP;
     private final AppEventProducer mAppEventProducer;
+    private final StatsManager mStatsManager;
 
     protected boolean mActive = false;
 
@@ -93,8 +97,8 @@
         mAppEventProducer = new AppEventProducer(context, this::onAppTargetEvent);
 
         mIDP = InvariantDeviceProfile.INSTANCE.get(context);
-        mIDP.addOnChangeListener(this);
         StatsLogCompatManager.LOGS_CONSUMER.add(mAppEventProducer);
+        mStatsManager = context.getSystemService(StatsManager.class);
     }
 
     @Override
@@ -157,10 +161,60 @@
             additionalSnapshotEvents(instanceId);
             prefs.edit().putLong(LAST_SNAPSHOT_TIME_MILLIS, now).apply();
         }
+
+        // Only register for launcher snapshot logging if this is the primary ModelDelegate
+        // instance, as there will be additional instances that may be destroyed at any time.
+        if (mIsPrimaryInstance) {
+            registerSnapshotLoggingCallback();
+        }
     }
 
     protected void additionalSnapshotEvents(InstanceId snapshotInstanceId){}
 
+    /**
+     * Registers a callback to log launcher workspace layout using Statsd pulled atom.
+     */
+    protected void registerSnapshotLoggingCallback() {
+        if (mStatsManager == null) {
+            Log.d(TAG, "Failed to get StatsManager");
+        }
+
+        try {
+            mStatsManager.setPullAtomCallback(
+                    SysUiStatsLog.LAUNCHER_LAYOUT_SNAPSHOT,
+                    null /* PullAtomMetadata */,
+                    MODEL_EXECUTOR,
+                    (i, eventList) -> {
+                        InstanceId instanceId = new InstanceIdSequence().newInstanceId();
+                        IntSparseArrayMap<ItemInfo> itemsIdMap;
+                        synchronized (mDataModel) {
+                            itemsIdMap = mDataModel.itemsIdMap.clone();
+                        }
+
+                        for (ItemInfo info : itemsIdMap) {
+                            FolderInfo parent = info.container > 0
+                                    ? (FolderInfo) itemsIdMap.get(info.container) : null;
+                            LauncherAtom.ItemInfo itemInfo = info.buildProto(parent);
+                            Log.d(TAG, itemInfo.toString());
+                            StatsEvent statsEvent = StatsLogCompatManager.buildStatsEvent(itemInfo,
+                                    instanceId);
+                            eventList.add(statsEvent);
+                        }
+                        Log.d(TAG,
+                                String.format(
+                                        "Successfully logged %d workspace items with instanceId=%d",
+                                        itemsIdMap.size(), instanceId.getId()));
+                        additionalSnapshotEvents(instanceId);
+                        return StatsManager.PULL_SUCCESS;
+                    }
+            );
+            Log.d(TAG, "Successfully registered for launcher snapshot logging!");
+        } catch (RuntimeException e) {
+            Log.e(TAG, "Failed to register launcher snapshot logging callback with StatsManager",
+                    e);
+        }
+    }
+
     @Override
     public void validateData() {
         super.validateData();
@@ -177,9 +231,10 @@
         super.destroy();
         mActive = false;
         StatsLogCompatManager.LOGS_CONSUMER.remove(mAppEventProducer);
-
+        if (mIsPrimaryInstance) {
+            mStatsManager.clearPullAtomCallback(SysUiStatsLog.LAUNCHER_LAYOUT_SNAPSHOT);
+        }
         destroyPredictors();
-        mIDP.removeOnChangeListener(this);
     }
 
     private void destroyPredictors() {
@@ -224,7 +279,7 @@
     private void registerPredictor(PredictorState state, AppPredictor predictor) {
         state.predictor = predictor;
         state.predictor.registerPredictionUpdates(
-                Executors.MODEL_EXECUTOR, t -> handleUpdate(state, t));
+                MODEL_EXECUTOR, t -> handleUpdate(state, t));
         state.predictor.requestPredictionUpdate();
     }
 
@@ -239,7 +294,7 @@
     private void registerWidgetsPredictor(AppPredictor predictor) {
         mWidgetsRecommendationState.predictor = predictor;
         mWidgetsRecommendationState.predictor.registerPredictionUpdates(
-                Executors.MODEL_EXECUTOR, targets -> {
+                MODEL_EXECUTOR, targets -> {
                     if (mWidgetsRecommendationState.setTargets(targets)) {
                         // No diff, skip
                         return;
@@ -250,12 +305,6 @@
         mWidgetsRecommendationState.predictor.requestPredictionUpdate();
     }
 
-    @Override
-    public void onIdpChanged(InvariantDeviceProfile profile) {
-        // Reinitialize everything
-        Executors.MODEL_EXECUTOR.execute(this::recreatePredictors);
-    }
-
     private void onAppTargetEvent(AppTargetEvent event, int client) {
         PredictorState state = client == CONTAINER_PREDICTION ? mAllAppsState : mHotseatState;
         if (state.predictor != null) {
diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
index 154b78b..e489cb3 100644
--- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -377,7 +377,7 @@
     /**
      * Shortcut factory for generating wellbeing action
      */
-    public static final SystemShortcut.Factory SHORTCUT_FACTORY =
+    public static final SystemShortcut.Factory<BaseDraggingActivity> SHORTCUT_FACTORY =
             (activity, info) -> (info.getTargetComponent() == null) ? null : INSTANCE.get(activity)
                     .getShortcutForApp(
                             info.getTargetComponent().getPackageName(), info.user.getIdentifier(),
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index bb58f45..9d70cfa 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -44,6 +44,8 @@
 import com.android.systemui.shared.system.BlurUtils;
 import com.android.systemui.shared.system.WallpaperManagerCompat;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.function.Consumer;
 
 /**
@@ -96,7 +98,11 @@
                 public void onDraw() {
                     View view = mLauncher.getDragLayer();
                     ViewRootImpl viewRootImpl = view.getViewRootImpl();
-                    setSurface(viewRootImpl != null ? viewRootImpl.getSurfaceControl() : null);
+                    boolean applied = setSurface(
+                            viewRootImpl != null ? viewRootImpl.getSurfaceControl() : null);
+                    if (!applied) {
+                        dispatchTransactionSurface(mDepth);
+                    }
                     view.post(() -> view.getViewTreeObserver().removeOnDrawListener(this));
                 }
             };
@@ -134,9 +140,18 @@
      */
     private float mDepth;
     /**
+     * Last blur value, in pixels, that was applied.
+     * For debugging purposes.
+     */
+    private int mCurrentBlur;
+    /**
      * If we're launching and app and should not be blurring the screen for performance reasons.
      */
     private boolean mBlurDisabledForAppLaunch;
+    /**
+     * If we requested early wake-up offsets to SurfaceFlinger.
+     */
+    private boolean mInEarlyWakeUp;
 
     // Workaround for animating the depth when multiwindow mode changes.
     private boolean mIgnoreStateChangesDuringMultiWindowAnimation = false;
@@ -198,20 +213,22 @@
 
     /**
      * Sets the specified app target surface to apply the blur to.
+     * @return true when surface was valid and transaction was dispatched.
      */
-    public void setSurface(SurfaceControl surface) {
+    public boolean setSurface(SurfaceControl surface) {
         // Set launcher as the SurfaceControl when we don't need an external target anymore.
         if (surface == null) {
             ViewRootImpl viewRootImpl = mLauncher.getDragLayer().getViewRootImpl();
             surface = viewRootImpl != null ? viewRootImpl.getSurfaceControl() : null;
         }
-
         if (mSurface != surface) {
             mSurface = surface;
             if (surface != null) {
                 dispatchTransactionSurface(mDepth);
+                return true;
             }
         }
+        return false;
     }
 
     @Override
@@ -225,6 +242,8 @@
             setDepth(toDepth);
         } else if (toState == LauncherState.OVERVIEW) {
             dispatchTransactionSurface(mDepth);
+        } else if (toState == LauncherState.BACKGROUND_APP) {
+            mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener);
         }
     }
 
@@ -292,17 +311,25 @@
         }
 
         if (supportsBlur) {
-            // We cannot mark the window as opaque in overview because there will be an app window
-            // below the launcher layer, and we need to draw it -- without blurs.
-            boolean isOverview = mLauncher.isInState(LauncherState.OVERVIEW);
-            boolean opaque = mLauncher.getScrimView().isFullyOpaque() && !isOverview;
+            boolean opaque = mLauncher.getScrimView().isFullyOpaque();
 
-            int blur = opaque || isOverview || !mCrossWindowBlursEnabled
-                    || mBlurDisabledForAppLaunch ? 0 : (int) (depth * mMaxBlurRadius);
+            mCurrentBlur = !mCrossWindowBlursEnabled || mBlurDisabledForAppLaunch
+                    ? 0 : (int) (depth * mMaxBlurRadius);
             SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()
-                    .setBackgroundBlurRadius(mSurface, blur)
+                    .setBackgroundBlurRadius(mSurface, mCurrentBlur)
                     .setOpaque(mSurface, opaque);
 
+            // Set early wake-up flags when we know we're executing an expensive operation, this way
+            // SurfaceFlinger will adjust its internal offsets to avoid jank.
+            boolean wantsEarlyWakeUp = depth > 0 && depth < 1;
+            if (wantsEarlyWakeUp && !mInEarlyWakeUp) {
+                transaction.setEarlyWakeupStart();
+                mInEarlyWakeUp = true;
+            } else if (!wantsEarlyWakeUp && mInEarlyWakeUp) {
+                transaction.setEarlyWakeupEnd();
+                mInEarlyWakeUp = false;
+            }
+
             AttachedSurfaceControl rootSurfaceControl =
                     mLauncher.getRootView().getRootSurfaceControl();
             if (rootSurfaceControl != null) {
@@ -328,4 +355,18 @@
         mwAnimation.setAutoCancel(true);
         mwAnimation.start();
     }
+
+    public void dump(String prefix, PrintWriter writer) {
+        writer.println(prefix + this.getClass().getSimpleName());
+        writer.println(prefix + "\tmMaxBlurRadius=" + mMaxBlurRadius);
+        writer.println(prefix + "\tmCrossWindowBlursEnabled=" + mCrossWindowBlursEnabled);
+        writer.println(prefix + "\tmSurface=" + mSurface);
+        writer.println(prefix + "\tmOverlayScrollProgress=" + mOverlayScrollProgress);
+        writer.println(prefix + "\tmDepth=" + mDepth);
+        writer.println(prefix + "\tmCurrentBlur=" + mCurrentBlur);
+        writer.println(prefix + "\tmBlurDisabledForAppLaunch=" + mBlurDisabledForAppLaunch);
+        writer.println(prefix + "\tmInEarlyWakeUp=" + mInEarlyWakeUp);
+        writer.println(prefix + "\tmIgnoreStateChangesDuringMultiWindowAnimation="
+                + mIgnoreStateChangesDuringMultiWindowAnimation);
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java b/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java
deleted file mode 100644
index 540f748..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 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.taskbar;
-
-import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
-import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
-import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
-import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
-
-import android.annotation.DrawableRes;
-import android.view.View;
-import android.widget.ImageView;
-
-import com.android.launcher3.R;
-import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
-
-/**
- * Creates Buttons for Taskbar for 3 button nav.
- * Can add animations and state management for buttons in this class as things progress.
- */
-public class ButtonProvider {
-
-    private final int mMarginLeftRight;
-    private final TaskbarActivityContext mContext;
-
-    public ButtonProvider(TaskbarActivityContext context) {
-        mContext = context;
-        mMarginLeftRight = context.getResources()
-                .getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
-    }
-
-    public View getBack() {
-        // Back button
-        return getButtonForDrawable(R.drawable.ic_sysbar_back, BUTTON_BACK);
-    }
-
-    public View getDown() {
-        // Ime down button
-        return getButtonForDrawable(R.drawable.ic_sysbar_back, BUTTON_BACK);
-    }
-
-    public View getHome() {
-        // Home button
-        return getButtonForDrawable(R.drawable.ic_sysbar_home, BUTTON_HOME);
-    }
-
-    public View getRecents() {
-        // Recents button
-        return getButtonForDrawable(R.drawable.ic_sysbar_recent, BUTTON_RECENTS);
-    }
-
-    public View getImeSwitcher() {
-        // IME Switcher Button
-        return getButtonForDrawable(R.drawable.ic_ime_switcher, BUTTON_IME_SWITCH);
-    }
-
-    private View getButtonForDrawable(@DrawableRes int drawableId, @TaskbarButton int buttonType) {
-        ImageView buttonView = new ImageView(mContext);
-        buttonView.setImageResource(drawableId);
-        buttonView.setBackgroundResource(R.drawable.taskbar_icon_click_feedback_roundrect);
-        buttonView.setPadding(mMarginLeftRight, 0, mMarginLeftRight, 0);
-        buttonView.setOnClickListener(view -> mContext.onNavigationButtonClick(buttonType));
-        return buttonView;
-    }
-
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
new file mode 100644
index 0000000..24a88a4
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
@@ -0,0 +1,90 @@
+/*
+ * 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.taskbar;
+
+import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
+import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
+import static com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATION;
+
+import android.animation.Animator;
+
+import com.android.launcher3.statemanager.StateManager;
+import com.android.quickstep.RecentsActivity;
+import com.android.quickstep.fallback.RecentsState;
+import com.android.quickstep.views.RecentsView;
+
+/**
+ * A data source which integrates with the fallback RecentsActivity instance (for 3P launchers).
+ */
+public class FallbackTaskbarUIController extends TaskbarUIController {
+
+    private final RecentsActivity mRecentsActivity;
+
+    private final StateManager.StateListener<RecentsState> mStateListener =
+            new StateManager.StateListener<RecentsState>() {
+                @Override
+                public void onStateTransitionStart(RecentsState toState) {
+                    animateToRecentsState(toState);
+
+                    // Handle tapping on live tile.
+                    RecentsView recentsView = mRecentsActivity.getOverviewPanel();
+                    recentsView.setTaskLaunchListener(toState == RecentsState.DEFAULT
+                            ? (() -> animateToRecentsState(RecentsState.BACKGROUND_APP)) : null);
+                }
+            };
+
+    // Initialized in init.
+    TaskbarControllers mControllers;
+
+    public FallbackTaskbarUIController(RecentsActivity recentsActivity) {
+        mRecentsActivity = recentsActivity;
+    }
+
+    @Override
+    protected void init(TaskbarControllers taskbarControllers) {
+        mControllers = taskbarControllers;
+
+        mRecentsActivity.setTaskbarUIController(this);
+        mRecentsActivity.getStateManager().addStateListener(mStateListener);
+    }
+
+    @Override
+    protected void onDestroy() {
+        mRecentsActivity.setTaskbarUIController(null);
+        mRecentsActivity.getStateManager().removeStateListener(mStateListener);
+    }
+
+    /**
+     * Creates an animation to animate the taskbar for the given state (but does not start it).
+     * Currently this animation just force stashes the taskbar in Overview.
+     */
+    public Animator createAnimToRecentsState(RecentsState toState, long duration) {
+        boolean forceStashed = toState.hasOverviewActions();
+        TaskbarStashController controller = mControllers.taskbarStashController;
+        // Set both FLAG_IN_STASHED_LAUNCHER_STATE and FLAG_IN_APP to ensure the state is respected.
+        // For all other states, just use the current stashed-in-app setting (e.g. if long clicked).
+        controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, forceStashed);
+        controller.updateStateForFlag(FLAG_IN_APP, !forceStashed);
+        return controller.applyStateWithoutStart(duration);
+    }
+
+    private void animateToRecentsState(RecentsState toState) {
+        Animator anim = createAnimToRecentsState(toState, TASKBAR_STASH_DURATION);
+        if (anim != null) {
+            anim.start();
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java b/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java
deleted file mode 100644
index 287caab..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 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.taskbar;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.RelativeLayout;
-
-import com.android.launcher3.views.ActivityContext;
-
-public class ImeBarView extends RelativeLayout {
-
-    private ButtonProvider mButtonProvider;
-    private View mImeView;
-
-    public ImeBarView(Context context) {
-        this(context, null);
-    }
-
-    public ImeBarView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public ImeBarView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public void init(ButtonProvider buttonProvider) {
-        mButtonProvider = buttonProvider;
-
-        ActivityContext context = getActivityContext();
-        RelativeLayout.LayoutParams imeParams = new RelativeLayout.LayoutParams(
-                context.getDeviceProfile().iconSizePx,
-                context.getDeviceProfile().iconSizePx
-        );
-        RelativeLayout.LayoutParams downParams = new RelativeLayout.LayoutParams(imeParams);
-
-        imeParams.addRule(ALIGN_PARENT_END);
-        imeParams.setMarginEnd(context.getDeviceProfile().iconSizePx);
-        downParams.setMarginStart(context.getDeviceProfile().iconSizePx);
-        downParams.addRule(ALIGN_PARENT_START);
-
-        // Down Arrow
-        View downView = mButtonProvider.getDown();
-        downView.setLayoutParams(downParams);
-        downView.setRotation(-90);
-        addView(downView);
-
-        // IME switcher button
-        mImeView = mButtonProvider.getImeSwitcher();
-        mImeView.setLayoutParams(imeParams);
-        addView(mImeView);
-    }
-
-    public void setImeSwitcherVisibility(boolean show) {
-        mImeView.setVisibility(show ? VISIBLE : GONE);
-    }
-
-    private <T extends Context & ActivityContext> T getActivityContext() {
-        return ActivityContext.lookupContext(getContext());
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index c2d107c..f206252 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -15,202 +15,318 @@
  */
 package com.android.launcher3.taskbar;
 
+import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
+import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
+import static com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATION;
+import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.view.MotionEvent;
+import android.view.View;
 
-import androidx.annotation.Nullable;
+import androidx.annotation.NonNull;
 
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.QuickstepTransitionManager;
-import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.InstanceIdSequence;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.util.MultiValueAlpha;
+import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.launcher3.util.OnboardingPrefs;
+import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
+import com.android.quickstep.RecentsAnimationController;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.recents.model.ThumbnailData;
 
+import java.util.Arrays;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
 
 /**
  * A data source which integrates with a Launcher instance
- * TODO: Rename to have Launcher prefix
  */
-
 public class LauncherTaskbarUIController extends TaskbarUIController {
 
     private final BaseQuickstepLauncher mLauncher;
-    private final TaskbarStateHandler mTaskbarStateHandler;
-    private final TaskbarAnimationController mTaskbarAnimationController;
-    private final TaskbarHotseatController mHotseatController;
 
-    private final TaskbarActivityContext mContext;
-    final TaskbarDragLayer mTaskbarDragLayer;
-    final TaskbarView mTaskbarView;
+    private final AnimatedFloat mIconAlignmentForResumedState =
+            new AnimatedFloat(this::onIconAlignmentRatioChanged);
+    private final AnimatedFloat mIconAlignmentForGestureState =
+            new AnimatedFloat(this::onIconAlignmentRatioChanged);
+    private final AnimatedFloat mIconAlignmentForLauncherState =
+            new AnimatedFloat(this::onIconAlignmentRatioChangedForStateTransition);
 
-    private @Nullable Animator mAnimator;
-    private boolean mIsAnimatingToLauncher;
+    private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
+            this::onStashedInAppChanged;
 
-    public LauncherTaskbarUIController(
-            BaseQuickstepLauncher launcher, TaskbarActivityContext context) {
-        mContext = context;
-        mTaskbarDragLayer = context.getDragLayer();
-        mTaskbarView = mTaskbarDragLayer.findViewById(R.id.taskbar_view);
+    private final StateManager.StateListener<LauncherState> mStateListener =
+            new StateManager.StateListener<LauncherState>() {
+                private Animator mAnimator;
 
+                @Override
+                public void onStateTransitionStart(LauncherState toState) {
+                    // Stash animation from going to launcher should be already handled in
+                    // createAnimToLauncher.
+                    TaskbarStashController controller = mControllers.taskbarStashController;
+                    long duration = TASKBAR_STASH_DURATION;
+                    controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
+                            toState.isTaskbarStashed());
+                    Animator stashAnimator = controller.applyStateWithoutStart(duration);
+                    if (stashAnimator != null) {
+                        if (mAnimator != null) {
+                            mAnimator.cancel();
+                        }
+                        PendingAnimation pendingAnimation = new PendingAnimation(duration);
+                        pendingAnimation.add(stashAnimator);
+                        pendingAnimation.setFloat(mIconAlignmentForLauncherState,
+                                AnimatedFloat.VALUE, toState.isTaskbarStashed() ? 0 : 1,
+                                FAST_OUT_SLOW_IN);
+                        pendingAnimation.addListener(new AnimatorListenerAdapter() {
+                            @Override
+                            public void onAnimationStart(Animator animator) {
+                                mTargetStateOverrideForStateTransition = toState;
+                                // Copy hotseat alpha over to taskbar icons
+                                mIconAlphaForHome.setValue(mLauncher.getHotseat().getIconsAlpha());
+                                mLauncher.getHotseat().setIconsAlpha(0);
+                            }
+
+                            @Override
+                            public void onAnimationEnd(Animator animator) {
+                                if (toState.isTaskbarStashed()) {
+                                    // Reset hotseat alpha to default
+                                    mLauncher.getHotseat().setIconsAlpha(1);
+                                }
+                                mTargetStateOverrideForStateTransition = null;
+                                mAnimator = null;
+                            }
+                        });
+                        mAnimator = pendingAnimation.buildAnim();
+                        mAnimator.start();
+                    }
+                }
+
+                @Override
+                public void onStateTransitionComplete(LauncherState finalState) {
+                    TaskbarStashController controller = mControllers.taskbarStashController;
+                    controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
+                            finalState.isTaskbarStashed());
+                    controller.applyState();
+                }
+            };
+
+    // Initialized in init.
+    private TaskbarControllers mControllers;
+    private AnimatedFloat mTaskbarBackgroundAlpha;
+    private AnimatedFloat mTaskbarOverrideBackgroundAlpha;
+    private AlphaProperty mIconAlphaForHome;
+    private boolean mIsAnimatingToLauncherViaResume;
+    private boolean mIsAnimatingToLauncherViaGesture;
+    private TaskbarKeyguardController mKeyguardController;
+
+    private LauncherState mTargetStateOverride = null;
+    private LauncherState mTargetStateOverrideForStateTransition = null;
+
+    private final DeviceProfile.OnDeviceProfileChangeListener mProfileChangeListener =
+            new DeviceProfile.OnDeviceProfileChangeListener() {
+                @Override
+                public void onDeviceProfileChanged(DeviceProfile dp) {
+                    mControllers.taskbarViewController.onRotationChanged(
+                            mLauncher.getDeviceProfile());
+                }
+            };
+
+    public LauncherTaskbarUIController(BaseQuickstepLauncher launcher) {
         mLauncher = launcher;
-        mTaskbarStateHandler = mLauncher.getTaskbarStateHandler();
-        mTaskbarAnimationController = new TaskbarAnimationController(mLauncher,
-                createTaskbarAnimationControllerCallbacks());
-        mHotseatController = new TaskbarHotseatController(
-                mLauncher, mTaskbarView::updateHotseatItems);
     }
 
     @Override
-    protected void onCreate() {
-        mTaskbarStateHandler.setAnimationController(mTaskbarAnimationController);
-        mTaskbarAnimationController.init();
-        mHotseatController.init();
-        setTaskbarViewVisible(!mLauncher.hasBeenResumed());
-        alignRealHotseatWithTaskbar();
+    protected void init(TaskbarControllers taskbarControllers) {
+        mControllers = taskbarControllers;
+
+        mTaskbarBackgroundAlpha = mControllers.taskbarDragLayerController
+                .getTaskbarBackgroundAlpha();
+        mTaskbarOverrideBackgroundAlpha = mControllers.taskbarDragLayerController
+                .getOverrideBackgroundAlpha();
+
+        MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha();
+        mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
+
         mLauncher.setTaskbarUIController(this);
+        mKeyguardController = taskbarControllers.taskbarKeyguardController;
+
+        onLauncherResumedOrPaused(mLauncher.hasBeenResumed(), true /* fromInit */);
+        mIconAlignmentForResumedState.finishAnimation();
+        onIconAlignmentRatioChanged();
+
+        onStashedInAppChanged(mLauncher.getDeviceProfile());
+        mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
+        mLauncher.getStateManager().addStateListener(mStateListener);
+        mLauncher.addOnDeviceProfileChangeListener(mProfileChangeListener);
     }
 
     @Override
     protected void onDestroy() {
-        if (mAnimator != null) {
-            // End this first, in case it relies on properties that are about to be cleaned up.
-            mAnimator.end();
-        }
-        mTaskbarStateHandler.setAnimationController(null);
-        mTaskbarAnimationController.cleanup();
-        mHotseatController.cleanup();
-        setTaskbarViewVisible(true);
+        onLauncherResumedOrPaused(false);
+        mIconAlignmentForResumedState.finishAnimation();
+        mIconAlignmentForGestureState.finishAnimation();
+        mIconAlignmentForLauncherState.finishAnimation();
+
+        mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
+        mLauncher.getStateManager().removeStateListener(mStateListener);
         mLauncher.getHotseat().setIconsAlpha(1f);
         mLauncher.setTaskbarUIController(null);
+        mLauncher.removeOnDeviceProfileChangeListener(mProfileChangeListener);
     }
 
     @Override
     protected boolean isTaskbarTouchable() {
-        return !mIsAnimatingToLauncher;
+        return !isAnimatingToLauncher();
     }
 
-    private TaskbarAnimationControllerCallbacks createTaskbarAnimationControllerCallbacks() {
-        return new TaskbarAnimationControllerCallbacks() {
-            @Override
-            public void updateTaskbarBackgroundAlpha(float alpha) {
-                mTaskbarDragLayer.setTaskbarBackgroundAlpha(alpha);
-            }
+    private boolean isAnimatingToLauncher() {
+        return mIsAnimatingToLauncherViaResume || mIsAnimatingToLauncherViaGesture;
+    }
 
-            @Override
-            public void updateTaskbarVisibilityAlpha(float alpha) {
-                mTaskbarView.setAlpha(alpha);
-            }
-
-            @Override
-            public void updateImeBarVisibilityAlpha(float alpha) {
-                mTaskbarDragLayer.updateImeBarVisibilityAlpha(alpha);
-            }
-
-            @Override
-            public void updateTaskbarScale(float scale) {
-                mTaskbarView.setScaleX(scale);
-                mTaskbarView.setScaleY(scale);
-            }
-
-            @Override
-            public void updateTaskbarTranslationY(float translationY) {
-                if (translationY < 0) {
-                    // Resize to accommodate the max translation we'll reach.
-                    mContext.setTaskbarWindowHeight(mContext.getDeviceProfile().taskbarSize
-                            + mLauncher.getHotseat().getTaskbarOffsetY());
-                } else {
-                    mContext.setTaskbarWindowHeight(mContext.getDeviceProfile().taskbarSize);
-                }
-                mTaskbarView.setTranslationY(translationY);
-            }
-        };
+    @Override
+    protected void updateContentInsets(Rect outContentInsets) {
+        int contentHeight = mControllers.taskbarStashController.getContentHeight();
+        TaskbarDragLayer dragLayer = mControllers.taskbarActivityContext.getDragLayer();
+        outContentInsets.top = dragLayer.getHeight() - contentHeight;
     }
 
     /**
      * Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
      */
     public void onLauncherResumedOrPaused(boolean isResumed) {
+        onLauncherResumedOrPaused(isResumed, false /* fromInit */);
+    }
+
+    private void onLauncherResumedOrPaused(boolean isResumed, boolean fromInit) {
+        if (mKeyguardController.isScreenOff()) {
+            if (!isResumed) {
+                return;
+            } else {
+                // Resuming implicitly means device unlocked
+                mKeyguardController.setScreenOn();
+            }
+        }
+
         long duration = QuickstepTransitionManager.CONTENT_ALPHA_DURATION;
-        if (mAnimator != null) {
-            mAnimator.cancel();
+        if (fromInit) {
+            // Since we are creating the starting state, we don't have a state to animate from, so
+            // set our state immediately.
+            duration = 0;
         }
-        if (isResumed) {
-            mAnimator = createAnimToLauncher(null, duration);
-        } else {
-            mAnimator = createAnimToApp(duration);
-        }
-        mAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mAnimator = null;
-            }
-        });
-        mAnimator.start();
+        ObjectAnimator anim = mIconAlignmentForResumedState.animateToValue(
+                getCurrentIconAlignmentRatio(), isResumed ? 1 : 0)
+                .setDuration(duration);
+
+        anim.addListener(AnimatorListeners.forEndCallback(
+                () -> mIsAnimatingToLauncherViaResume = false));
+        anim.start();
+        mIsAnimatingToLauncherViaResume = isResumed;
+
+        TaskbarStashController stashController = mControllers.taskbarStashController;
+        stashController.updateStateForFlag(FLAG_IN_APP, !isResumed);
+        stashController.applyState(duration);
     }
 
     /**
-     * Create Taskbar animation when going from an app to Launcher.
+     * Create Taskbar animation when going from an app to Launcher as part of recents transition.
      * @param toState If known, the state we will end up in when reaching Launcher.
+     * @param callbacks callbacks to track the recents animation lifecycle. The state change is
+     *                 automatically reset once the recents animation finishes
      */
-    public Animator createAnimToLauncher(@Nullable LauncherState toState, long duration) {
-        PendingAnimation anim = new PendingAnimation(duration);
-        anim.add(mTaskbarAnimationController.createAnimToBackgroundAlpha(0, duration));
-        if (toState != null) {
-            mTaskbarStateHandler.setStateWithAnimation(toState, new StateAnimationConfig(), anim);
+    public Animator createAnimToLauncher(@NonNull LauncherState toState,
+            @NonNull RecentsAnimationCallbacks callbacks, long duration) {
+        AnimatorSet animatorSet = new AnimatorSet();
+        TaskbarStashController stashController = mControllers.taskbarStashController;
+        stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
+                toState.isTaskbarStashed());
+        if (toState.isTaskbarStashed()) {
+            animatorSet.play(stashController.applyStateWithoutStart(duration));
+        } else {
+            animatorSet.play(mIconAlignmentForGestureState
+                    .animateToValue(1)
+                    .setDuration(duration));
         }
-
-        anim.addListener(new AnimatorListenerAdapter() {
+        animatorSet.addListener(new AnimatorListenerAdapter() {
             @Override
-            public void onAnimationStart(Animator animation) {
-                mIsAnimatingToLauncher = true;
-                mTaskbarView.setHolesAllowedInLayout(true);
-                mTaskbarView.updateHotseatItemsVisibility();
+            public void onAnimationEnd(Animator animator) {
+                mTargetStateOverride = null;
+                animator.removeListener(this);
             }
 
             @Override
-            public void onAnimationEnd(Animator animation) {
-                mIsAnimatingToLauncher = false;
-                setTaskbarViewVisible(false);
+            public void onAnimationStart(Animator animator) {
+                mTargetStateOverride = toState;
+                mIsAnimatingToLauncherViaGesture = true;
+                stashController.updateStateForFlag(FLAG_IN_APP, false);
+                stashController.applyState(duration);
             }
         });
 
-        return anim.buildAnim();
-    }
-
-    private Animator createAnimToApp(long duration) {
-        PendingAnimation anim = new PendingAnimation(duration);
-        anim.add(mTaskbarAnimationController.createAnimToBackgroundAlpha(1, duration));
-        anim.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                mTaskbarView.updateHotseatItemsVisibility();
-                setTaskbarViewVisible(true);
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mTaskbarView.setHolesAllowedInLayout(false);
-            }
+        TaskBarRecentsAnimationListener listener = new TaskBarRecentsAnimationListener(callbacks);
+        callbacks.addListener(listener);
+        RecentsView recentsView = mLauncher.getOverviewPanel();
+        recentsView.setTaskLaunchListener(() -> {
+            listener.endGestureStateOverride(true);
+            callbacks.removeListener(listener);
         });
-        return anim.buildAnim();
+
+        return animatorSet;
     }
 
-    @Override
-    protected void onImeVisible(TaskbarDragLayer containerView, boolean isVisible) {
-        mTaskbarAnimationController.animateToVisibilityForIme(isVisible ? 0 : 1);
+    private float getCurrentIconAlignmentRatio() {
+        return Math.max(mIconAlignmentForResumedState.value, mIconAlignmentForGestureState.value);
     }
 
-    /**
-     * Should be called when one or more items in the Hotseat have changed.
-     */
-    public void onHotseatUpdated() {
-        mHotseatController.onHotseatUpdated();
+    private float getCurrentIconAlignmentRatioForLauncherState() {
+        return mIconAlignmentForLauncherState.value;
+    }
+
+    private void onIconAlignmentRatioChangedForStateTransition() {
+        onIconAlignmentRatioChanged(
+                mTargetStateOverrideForStateTransition != null
+                        ? mTargetStateOverrideForStateTransition
+                        : mLauncher.getStateManager().getState(),
+                this::getCurrentIconAlignmentRatioForLauncherState);
+    }
+
+    private void onIconAlignmentRatioChanged() {
+        onIconAlignmentRatioChanged(mTargetStateOverride != null ? mTargetStateOverride
+                : mLauncher.getStateManager().getState(), this::getCurrentIconAlignmentRatio);
+    }
+
+    private void onIconAlignmentRatioChanged(LauncherState state,
+            Supplier<Float> alignmentSupplier) {
+        if (mControllers == null) {
+            return;
+        }
+        float alignment = alignmentSupplier.get();
+        mControllers.taskbarViewController.setLauncherIconAlignment(
+                alignment, mLauncher.getDeviceProfile());
+
+        mTaskbarBackgroundAlpha.updateValue(1 - alignment);
+
+        setIconAlpha(state, alignment);
     }
 
     /**
@@ -218,58 +334,113 @@
      * @return Whether any Taskbar item could handle the given MotionEvent if given the chance.
      */
     public boolean isEventOverAnyTaskbarItem(MotionEvent ev) {
-        return mTaskbarView.isEventOverAnyItem(ev);
+        return mControllers.taskbarViewController.isEventOverAnyItem(ev);
     }
 
     public boolean isDraggingItem() {
-        return mTaskbarView.isDraggingItem();
+        return mControllers.taskbarDragController.isDragging();
     }
 
-    /**
-     * Pads the Hotseat to line up exactly with Taskbar's copy of the Hotseat.
-     */
-    @Override
-    public void alignRealHotseatWithTaskbar() {
-        Rect hotseatBounds = new Rect();
-        DeviceProfile grid = mLauncher.getDeviceProfile();
-        int hotseatHeight = grid.workspacePadding.bottom + grid.taskbarSize;
-        int taskbarOffset = mLauncher.getHotseat().getTaskbarOffsetY();
-        int hotseatTopDiff = hotseatHeight - grid.taskbarSize - taskbarOffset;
-        int hotseatBottomDiff = taskbarOffset;
-
-        RectF hotseatBoundsF = mTaskbarView.getHotseatBounds();
-        Utilities.scaleRectFAboutPivot(hotseatBoundsF, getTaskbarScaleOnHome(),
-                mTaskbarView.getPivotX(), mTaskbarView.getPivotY());
-        hotseatBoundsF.round(hotseatBounds);
-        mLauncher.getHotseat().setPadding(hotseatBounds.left,
-                hotseatBounds.top + hotseatTopDiff,
-                mTaskbarView.getWidth() - hotseatBounds.right,
-                mTaskbarView.getHeight() - hotseatBounds.bottom + hotseatBottomDiff);
+    public View getRootView() {
+        return mControllers.taskbarActivityContext.getDragLayer();
     }
 
-    /**
-     * Returns the ratio of the taskbar icon size on home vs in an app.
-     */
-    public float getTaskbarScaleOnHome() {
-        DeviceProfile inAppDp = mContext.getDeviceProfile();
-        DeviceProfile onHomeDp = mLauncher.getDeviceProfile();
-        return (float) onHomeDp.cellWidthPx / inAppDp.cellWidthPx;
+    private void setIconAlpha(LauncherState state, float progress) {
+        if ((state.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
+            // If the hotseat icons are visible, then switch taskbar in last frame
+            setTaskbarViewVisible(progress < 1);
+        } else {
+            mIconAlphaForHome.setValue(1 - progress);
+        }
     }
 
-    void setTaskbarViewVisible(boolean isVisible) {
-        mTaskbarView.setIconsVisibility(isVisible);
+    private void setTaskbarViewVisible(boolean isVisible) {
+        mIconAlphaForHome.setValue(isVisible ? 1 : 0);
         mLauncher.getHotseat().setIconsAlpha(isVisible ? 0f : 1f);
     }
 
+    @Override
+    protected void onStashedInAppChanged() {
+        onStashedInAppChanged(mLauncher.getDeviceProfile());
+    }
+
+    private void onStashedInAppChanged(DeviceProfile deviceProfile) {
+        boolean taskbarStashedInApps = mControllers.taskbarStashController.isStashedInApp();
+        deviceProfile.isTaskbarPresentInApps = !taskbarStashedInApps;
+    }
+
     /**
-     * Contains methods that TaskbarAnimationController can call to interface with
-     * TaskbarController.
+     * Sets whether the background behind the taskbar/nav bar should be hidden.
      */
-    protected interface TaskbarAnimationControllerCallbacks {
-        void updateTaskbarBackgroundAlpha(float alpha);
-        void updateTaskbarVisibilityAlpha(float alpha);
-        void updateImeBarVisibilityAlpha(float alpha);
-        void updateTaskbarScale(float scale);
-        void updateTaskbarTranslationY(float translationY);
+    public void forceHideBackground(boolean forceHide) {
+        mTaskbarOverrideBackgroundAlpha.updateValue(forceHide ? 0 : 1);
+    }
+
+    @Override
+    public Stream<ItemInfoWithIcon> getAppIconsForEdu() {
+        return Arrays.stream(mLauncher.getAppsView().getAppsStore().getApps());
+    }
+
+    /**
+     * Starts the taskbar education flow, if the user hasn't seen it yet.
+     */
+    public void showEdu() {
+        if (!FeatureFlags.ENABLE_TASKBAR_EDU.get()
+                || Utilities.IS_RUNNING_IN_TEST_HARNESS
+                || mLauncher.getOnboardingPrefs().getBoolean(OnboardingPrefs.TASKBAR_EDU_SEEN)) {
+            return;
+        }
+        mLauncher.getOnboardingPrefs().markChecked(OnboardingPrefs.TASKBAR_EDU_SEEN);
+
+        mControllers.taskbarEduController.showEdu();
+    }
+
+    /**
+     * Manually ends the taskbar education flow.
+     */
+    public void hideEdu() {
+        if (!FeatureFlags.ENABLE_TASKBAR_EDU.get()) {
+            return;
+        }
+
+        mControllers.taskbarEduController.hideEdu();
+    }
+
+    @Override
+    public void onTaskbarIconLaunched(WorkspaceItemInfo item) {
+        InstanceId instanceId = new InstanceIdSequence().newInstanceId();
+        mLauncher.logAppLaunch(mControllers.taskbarActivityContext.getStatsLogManager(), item,
+                instanceId);
+    }
+
+    private final class TaskBarRecentsAnimationListener implements RecentsAnimationListener {
+        private final RecentsAnimationCallbacks mCallbacks;
+
+        TaskBarRecentsAnimationListener(RecentsAnimationCallbacks callbacks) {
+            mCallbacks = callbacks;
+        }
+
+        @Override
+        public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+            endGestureStateOverride(true);
+        }
+
+        @Override
+        public void onRecentsAnimationFinished(RecentsAnimationController controller) {
+            endGestureStateOverride(!controller.getFinishTargetIsLauncher());
+        }
+
+        private void endGestureStateOverride(boolean finishedToApp) {
+            mCallbacks.removeListener(this);
+            mIsAnimatingToLauncherViaGesture = false;
+
+            mIconAlignmentForGestureState
+                    .animateToValue(0)
+                    .start();
+
+            TaskbarStashController controller = mControllers.taskbarStashController;
+            controller.updateStateForFlag(FLAG_IN_APP, finishedToApp);
+            controller.applyState();
+        }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
new file mode 100644
index 0000000..b768d60
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -0,0 +1,504 @@
+/*
+ * 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.taskbar;
+
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y_LONG_CLICK;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
+import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_IME;
+import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_KEYGUARD;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
+
+import android.animation.ObjectAnimator;
+import android.annotation.DrawableRes;
+import android.annotation.IdRes;
+import android.annotation.LayoutRes;
+import android.content.res.ColorStateList;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.Region.Op;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.util.Property;
+import android.view.Gravity;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnHoverListener;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AlphaUpdateListener;
+import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
+import com.android.launcher3.taskbar.contextual.RotationButton;
+import com.android.launcher3.taskbar.contextual.RotationButtonController;
+import com.android.launcher3.util.MultiValueAlpha;
+import com.android.launcher3.util.Themes;
+import com.android.quickstep.AnimatedFloat;
+
+import java.util.ArrayList;
+import java.util.function.IntPredicate;
+
+/**
+ * Controller for managing nav bar buttons in taskbar
+ */
+public class NavbarButtonsViewController {
+
+    private final Rect mTempRect = new Rect();
+
+    private static final int FLAG_SWITCHER_SUPPORTED = 1 << 0;
+    private static final int FLAG_IME_VISIBLE = 1 << 1;
+    private static final int FLAG_ROTATION_BUTTON_VISIBLE = 1 << 2;
+    private static final int FLAG_A11Y_VISIBLE = 1 << 3;
+    private static final int FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE = 1 << 4;
+    private static final int FLAG_KEYGUARD_VISIBLE = 1 << 5;
+    private static final int FLAG_KEYGUARD_OCCLUDED = 1 << 6;
+    private static final int FLAG_DISABLE_HOME = 1 << 7;
+    private static final int FLAG_DISABLE_RECENTS = 1 << 8;
+    private static final int FLAG_DISABLE_BACK = 1 << 9;
+
+    private static final int MASK_IME_SWITCHER_VISIBLE = FLAG_SWITCHER_SUPPORTED | FLAG_IME_VISIBLE;
+
+    private View.OnLongClickListener mA11yLongClickListener;
+    private final ArrayList<StatePropertyHolder> mPropertyHolders = new ArrayList<>();
+    private final ArrayList<View> mAllButtons = new ArrayList<>();
+    private int mState;
+
+    private final TaskbarActivityContext mContext;
+    private final FrameLayout mNavButtonsView;
+    private final ViewGroup mNavButtonContainer;
+    // Used for IME+A11Y buttons
+    private final ViewGroup mEndContextualContainer;
+    private final ViewGroup mStartContextualContainer;
+
+    private final AnimatedFloat mTaskbarNavButtonTranslationY = new AnimatedFloat(
+            this::updateNavButtonTranslationY);
+
+    // Initialized in init.
+    private TaskbarControllers mControllers;
+    private View mA11yButton;
+    private int mSysuiStateFlags;
+    private View mBackButton;
+
+    public NavbarButtonsViewController(TaskbarActivityContext context, FrameLayout navButtonsView) {
+        mContext = context;
+        mNavButtonsView = navButtonsView;
+        mNavButtonContainer = mNavButtonsView.findViewById(R.id.end_nav_buttons);
+        mEndContextualContainer = mNavButtonsView.findViewById(R.id.end_contextual_buttons);
+        mStartContextualContainer = mNavButtonsView.findViewById(R.id.start_contextual_buttons);
+    }
+
+    /**
+     * Initializes the controller
+     */
+    public void init(TaskbarControllers controllers, TaskbarSharedState sharedState) {
+        mControllers = controllers;
+        mNavButtonsView.getLayoutParams().height = mContext.getDeviceProfile().taskbarSize;
+        parseSystemUiFlags(sharedState.sysuiStateFlags);
+
+        mA11yLongClickListener = view -> {
+            mControllers.navButtonController.onButtonClick(BUTTON_A11Y_LONG_CLICK);
+            return true;
+        };
+
+        mPropertyHolders.add(new StatePropertyHolder(
+                mControllers.taskbarViewController.getTaskbarIconAlpha()
+                        .getProperty(ALPHA_INDEX_IME),
+                flags -> (flags & FLAG_IME_VISIBLE) == 0, MultiValueAlpha.VALUE, 1, 0));
+
+        boolean isThreeButtonNav = mContext.isThreeButtonNav();
+        // IME switcher
+        View imeSwitcherButton = addButton(R.drawable.ic_ime_switcher, BUTTON_IME_SWITCH,
+                isThreeButtonNav ? mStartContextualContainer : mEndContextualContainer,
+                mControllers.navButtonController, R.id.ime_switcher);
+        mPropertyHolders.add(new StatePropertyHolder(imeSwitcherButton,
+                flags -> ((flags & MASK_IME_SWITCHER_VISIBLE) == MASK_IME_SWITCHER_VISIBLE)
+                        && ((flags & FLAG_ROTATION_BUTTON_VISIBLE) == 0)));
+
+        mPropertyHolders.add(new StatePropertyHolder(
+                mControllers.taskbarViewController.getTaskbarIconAlpha()
+                        .getProperty(ALPHA_INDEX_KEYGUARD),
+                flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0, MultiValueAlpha.VALUE, 1, 0));
+
+        mPropertyHolders.add(new StatePropertyHolder(mControllers.taskbarDragLayerController
+                .getKeyguardBgTaskbar(),
+                flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0, AnimatedFloat.VALUE, 1, 0));
+
+        // Force nav buttons (specifically back button) to be visible during setup wizard.
+        boolean isInSetup = !mContext.isUserSetupComplete();
+        if (isThreeButtonNav || isInSetup) {
+            initButtons(mNavButtonContainer, mEndContextualContainer,
+                    mControllers.navButtonController);
+
+            if (isInSetup) {
+                // Since setup wizard only has back button enabled, it looks strange to be
+                // end-aligned, so start-align instead.
+                FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams)
+                        mNavButtonContainer.getLayoutParams();
+                navButtonsLayoutParams.setMarginStart(navButtonsLayoutParams.getMarginEnd());
+                navButtonsLayoutParams.setMarginEnd(0);
+                navButtonsLayoutParams.gravity = Gravity.START;
+                mNavButtonContainer.requestLayout();
+
+                if (!isThreeButtonNav) {
+                    // Tint all the nav buttons since there's no taskbar background in SUW.
+                    for (int i = 0; i < mNavButtonContainer.getChildCount(); i++) {
+                        if (!(mNavButtonContainer.getChildAt(i) instanceof ImageView)) continue;
+                        ImageView button = (ImageView) mNavButtonContainer.getChildAt(i);
+                        button.setImageTintList(ColorStateList.valueOf(Themes.getAttrColor(
+                                button.getContext(), android.R.attr.textColorPrimary)));
+                    }
+                }
+            }
+
+            // Animate taskbar background when IME shows
+            mPropertyHolders.add(new StatePropertyHolder(
+                    mControllers.taskbarDragLayerController.getNavbarBackgroundAlpha(),
+                    flags -> (flags & FLAG_IME_VISIBLE) != 0 ||
+                            (flags & FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE) != 0,
+                    AnimatedFloat.VALUE, 1, 0));
+
+            // Rotation button
+            RotationButton rotationButton = new RotationButtonImpl(
+                    addButton(mEndContextualContainer, R.id.rotate_suggestion,
+                            R.layout.taskbar_contextual_button));
+            rotationButton.hide();
+            mControllers.rotationButtonController.setRotationButton(rotationButton);
+        } else {
+            mControllers.rotationButtonController.setRotationButton(new RotationButton() {});
+            View imeDownButton = addButton(R.drawable.ic_sysbar_back, BUTTON_BACK,
+                    mStartContextualContainer, mControllers.navButtonController, R.id.back);
+            imeDownButton.setRotation(Utilities.isRtl(mContext.getResources()) ? 90 : -90);
+            // Rotate when Ime visible
+            mPropertyHolders.add(new StatePropertyHolder(imeDownButton,
+                    flags -> (flags & FLAG_IME_VISIBLE) != 0));
+        }
+
+        applyState();
+        mPropertyHolders.forEach(StatePropertyHolder::endAnimation);
+    }
+
+    private void initButtons(ViewGroup navContainer, ViewGroup endContainer,
+            TaskbarNavButtonController navButtonController) {
+
+        mBackButton = addButton(R.drawable.ic_sysbar_back, BUTTON_BACK,
+                mNavButtonContainer, mControllers.navButtonController, R.id.back);
+        mPropertyHolders.add(new StatePropertyHolder(mBackButton,
+                flags -> (flags & FLAG_DISABLE_BACK) == 0));
+        boolean isRtl = Utilities.isRtl(mContext.getResources());
+        mPropertyHolders.add(new StatePropertyHolder(
+                mBackButton, flags -> (flags & FLAG_IME_VISIBLE) != 0, View.ROTATION,
+                isRtl ? 90 : -90, 0));
+        // Hide when keyguard is showing, show when bouncer or lock screen app is showing
+        mPropertyHolders.add(new StatePropertyHolder(mBackButton,
+                flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 ||
+                        (flags & FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE) != 0 ||
+                        (flags & FLAG_KEYGUARD_OCCLUDED) != 0));
+        // Translate back button to be at end/start of other buttons for keyguard
+        int navButtonSize = mContext.getResources().getDimensionPixelSize(
+                R.dimen.taskbar_nav_buttons_size);
+        mPropertyHolders.add(new StatePropertyHolder(
+                mBackButton, flags -> (flags & FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE) != 0
+                        || (flags & FLAG_KEYGUARD_VISIBLE) != 0,
+                VIEW_TRANSLATE_X, navButtonSize * (isRtl ? -2 : 2), 0));
+
+
+        // home and recents buttons
+        View homeButton = addButton(R.drawable.ic_sysbar_home, BUTTON_HOME, navContainer,
+                navButtonController, R.id.home);
+        mPropertyHolders.add(new StatePropertyHolder(homeButton,
+                flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 &&
+                        (flags & FLAG_DISABLE_HOME) == 0));
+        View recentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS,
+                navContainer, navButtonController, R.id.recent_apps);
+        mPropertyHolders.add(new StatePropertyHolder(recentsButton,
+                flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 &&
+                        (flags & FLAG_DISABLE_RECENTS) == 0));
+
+        // A11y button
+        mA11yButton = addButton(R.drawable.ic_sysbar_accessibility_button, BUTTON_A11Y,
+                endContainer, navButtonController, R.id.accessibility_button,
+                R.layout.taskbar_contextual_button);
+        mPropertyHolders.add(new StatePropertyHolder(mA11yButton,
+                flags -> (flags & FLAG_A11Y_VISIBLE) != 0
+                        && (flags & FLAG_ROTATION_BUTTON_VISIBLE) == 0));
+        mA11yButton.setOnLongClickListener(mA11yLongClickListener);
+    }
+
+    private void parseSystemUiFlags(int sysUiStateFlags) {
+        mSysuiStateFlags = sysUiStateFlags;
+        boolean isImeVisible = (sysUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0;
+        boolean isImeSwitcherShowing = (sysUiStateFlags & SYSUI_STATE_IME_SWITCHER_SHOWING) != 0;
+        boolean a11yVisible = (sysUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
+        boolean isHomeDisabled = (sysUiStateFlags & SYSUI_STATE_HOME_DISABLED) != 0;
+        boolean isRecentsDisabled = (sysUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0;
+        boolean isBackDisabled = (sysUiStateFlags & SYSUI_STATE_BACK_DISABLED) != 0;
+
+        // TODO(b/202218289) we're getting IME as not visible on lockscreen from system
+        updateStateForFlag(FLAG_IME_VISIBLE, isImeVisible);
+        updateStateForFlag(FLAG_SWITCHER_SUPPORTED, isImeSwitcherShowing);
+        updateStateForFlag(FLAG_A11Y_VISIBLE, a11yVisible);
+        updateStateForFlag(FLAG_DISABLE_HOME, isHomeDisabled);
+        updateStateForFlag(FLAG_DISABLE_RECENTS, isRecentsDisabled);
+        updateStateForFlag(FLAG_DISABLE_BACK, isBackDisabled);
+
+        if (mA11yButton != null) {
+            // Only used in 3 button
+            boolean a11yLongClickable =
+                    (sysUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
+            mA11yButton.setLongClickable(a11yLongClickable);
+        }
+    }
+
+    public void updateStateForSysuiFlags(int systemUiStateFlags) {
+        if (systemUiStateFlags == mSysuiStateFlags) {
+            return;
+        }
+        parseSystemUiFlags(systemUiStateFlags);
+        applyState();
+    }
+
+    /**
+     * Should be called when we need to show back button for bouncer
+     */
+    public void setBackForBouncer(boolean isBouncerVisible) {
+        updateStateForFlag(FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE, isBouncerVisible);
+        applyState();
+    }
+
+    /**
+     * Slightly misnamed, but should be called when keyguard OR AOD is showing.
+     * We consider keyguardVisible when it's showing bouncer OR is occlucded by another app
+     */
+    public void setKeyguardVisible(boolean isKeyguardVisible, boolean isKeyguardOccluded) {
+        updateStateForFlag(FLAG_KEYGUARD_VISIBLE, isKeyguardVisible || isKeyguardOccluded);
+        updateStateForFlag(FLAG_KEYGUARD_OCCLUDED, isKeyguardOccluded);
+        applyState();
+    }
+
+    /**
+     * Returns true if IME bar is visible
+     */
+    public boolean isImeVisible() {
+        return (mState & FLAG_IME_VISIBLE) != 0;
+    }
+
+    /**
+     * Returns true if the home button is disabled
+     */
+    public boolean isHomeDisabled() {
+        return (mState & FLAG_DISABLE_HOME) != 0;
+    }
+
+    /**
+     * Returns true if the recents (overview) button is disabled
+     */
+    public boolean isRecentsDisabled() {
+        return (mState & FLAG_DISABLE_RECENTS) != 0;
+    }
+
+    /**
+     * Adds the bounds corresponding to all visible buttons to provided region
+     */
+    public void addVisibleButtonsRegion(TaskbarDragLayer parent, Region outRegion) {
+        int count = mAllButtons.size();
+        for (int i = 0; i < count; i++) {
+            View button = mAllButtons.get(i);
+            if (button.getVisibility() == View.VISIBLE) {
+                parent.getDescendantRectRelativeToSelf(button, mTempRect);
+                outRegion.op(mTempRect, Op.UNION);
+            }
+        }
+    }
+
+    /** Use to set the translationY for the all nav+contextual buttons */
+    public AnimatedFloat getTaskbarNavButtonTranslationY() {
+        return mTaskbarNavButtonTranslationY;
+    }
+
+    /**
+     * Does not call {@link #applyState()}. Don't forget to!
+     */
+    private void updateStateForFlag(int flag, boolean enabled) {
+        if (enabled) {
+            mState |= flag;
+        } else {
+            mState &= ~flag;
+        }
+    }
+
+    private void applyState() {
+        int count = mPropertyHolders.size();
+        for (int i = 0; i < count; i++) {
+            mPropertyHolders.get(i).setState(mState);
+        }
+    }
+
+    private void updateNavButtonTranslationY() {
+        mNavButtonsView.setTranslationY(mTaskbarNavButtonTranslationY.value);
+    }
+
+    private ImageView addButton(@DrawableRes int drawableId, @TaskbarButton int buttonType,
+            ViewGroup parent, TaskbarNavButtonController navButtonController, @IdRes int id) {
+        return addButton(drawableId, buttonType, parent, navButtonController, id,
+                R.layout.taskbar_nav_button);
+    }
+
+    private ImageView addButton(@DrawableRes int drawableId, @TaskbarButton int buttonType,
+            ViewGroup parent, TaskbarNavButtonController navButtonController, @IdRes int id,
+            @LayoutRes int layoutId) {
+        ImageView buttonView = addButton(parent, id, layoutId);
+        buttonView.setImageResource(drawableId);
+        buttonView.setOnClickListener(view -> navButtonController.onButtonClick(buttonType));
+        return buttonView;
+    }
+
+    private ImageView addButton(ViewGroup parent, @IdRes int id, @LayoutRes int layoutId) {
+        ImageView buttonView = (ImageView) mContext.getLayoutInflater()
+                .inflate(layoutId, parent, false);
+        buttonView.setId(id);
+        parent.addView(buttonView);
+        mAllButtons.add(buttonView);
+        return buttonView;
+    }
+
+    public void onDestroy() {
+        mPropertyHolders.clear();
+    }
+
+    private class RotationButtonImpl implements RotationButton {
+
+        private final ImageView mButton;
+        private AnimatedVectorDrawable mImageDrawable;
+
+        RotationButtonImpl(ImageView button) {
+            mButton = button;
+        }
+
+        @Override
+        public void setRotationButtonController(RotationButtonController rotationButtonController) {
+            // TODO(b/187754252) UI polish, different icons based on light/dark context, etc
+            mImageDrawable = (AnimatedVectorDrawable) mButton.getContext()
+                    .getDrawable(rotationButtonController.getIconResId());
+            mButton.setImageDrawable(mImageDrawable);
+            mImageDrawable.setCallback(mButton);
+        }
+
+        @Override
+        public View getCurrentView() {
+            return mButton;
+        }
+
+        @Override
+        public void show() {
+            mButton.setVisibility(View.VISIBLE);
+            mState |= FLAG_ROTATION_BUTTON_VISIBLE;
+            applyState();
+        }
+
+        @Override
+        public void hide() {
+            mButton.setVisibility(View.GONE);
+            mState &= ~FLAG_ROTATION_BUTTON_VISIBLE;
+            applyState();
+        }
+
+        @Override
+        public boolean isVisible() {
+            return mButton.getVisibility() == View.VISIBLE;
+        }
+
+        @Override
+        public void updateIcon(int lightIconColor, int darkIconColor) {
+            // TODO(b/187754252): UI Polish
+        }
+
+        @Override
+        public void setOnClickListener(OnClickListener onClickListener) {
+            mButton.setOnClickListener(onClickListener);
+        }
+
+        @Override
+        public void setOnHoverListener(OnHoverListener onHoverListener) {
+            mButton.setOnHoverListener(onHoverListener);
+        }
+
+        @Override
+        public AnimatedVectorDrawable getImageDrawable() {
+            return mImageDrawable;
+        }
+
+        @Override
+        public void setDarkIntensity(float darkIntensity) {
+            // TODO(b/187754252) UI polish
+        }
+
+        @Override
+        public boolean acceptRotationProposal() {
+            return mButton.isAttachedToWindow();
+        }
+    }
+
+    private static class StatePropertyHolder {
+
+        private final float mEnabledValue, mDisabledValue;
+        private final ObjectAnimator mAnimator;
+        private final IntPredicate mEnableCondition;
+
+        private boolean mIsEnabled = true;
+
+        StatePropertyHolder(View view, IntPredicate enableCondition) {
+            this(view, enableCondition, LauncherAnimUtils.VIEW_ALPHA, 1, 0);
+            mAnimator.addListener(new AlphaUpdateListener(view));
+        }
+
+        <T> StatePropertyHolder(T target, IntPredicate enabledCondition,
+                Property<T, Float> property, float enabledValue, float disabledValue) {
+            mEnableCondition = enabledCondition;
+            mEnabledValue = enabledValue;
+            mDisabledValue = disabledValue;
+            mAnimator = ObjectAnimator.ofFloat(target, property, enabledValue, disabledValue);
+        }
+
+        public void setState(int flags) {
+            boolean isEnabled = mEnableCondition.test(flags);
+            if (mIsEnabled != isEnabled) {
+                mIsEnabled = isEnabled;
+                mAnimator.cancel();
+                mAnimator.setFloatValues(mIsEnabled ? mEnabledValue : mDisabledValue);
+                mAnimator.start();
+            }
+        }
+
+        public void endAnimation() {
+            if (mAnimator.isRunning()) {
+                mAnimator.end();
+            }
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java
new file mode 100644
index 0000000..6db5839
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java
@@ -0,0 +1,106 @@
+/*
+ * 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.taskbar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.ColorInt;
+import androidx.core.content.ContextCompat;
+
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.R;
+
+public class StashedHandleView extends View {
+
+    private static final long COLOR_CHANGE_DURATION = 120;
+
+    private final @ColorInt int mStashedHandleLightColor;
+    private final @ColorInt int mStashedHandleDarkColor;
+    private final Rect mSampledRegion = new Rect();
+    private final int[] mTmpArr = new int[2];
+
+    private @Nullable ObjectAnimator mColorChangeAnim;
+
+    public StashedHandleView(Context context) {
+        this(context, null);
+    }
+
+    public StashedHandleView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public StashedHandleView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public StashedHandleView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        mStashedHandleLightColor = ContextCompat.getColor(context,
+                R.color.taskbar_stashed_handle_light_color);
+        mStashedHandleDarkColor = ContextCompat.getColor(context,
+                R.color.taskbar_stashed_handle_dark_color);
+    }
+
+    /**
+     * Updates mSampledRegion to be the location of the stashedHandleBounds relative to the screen.
+     * @see #getSampledRegion()
+     */
+    public void updateSampledRegion(Rect stashedHandleBounds) {
+        getLocationOnScreen(mTmpArr);
+        mSampledRegion.set(stashedHandleBounds);
+        mSampledRegion.offset(mTmpArr[0], mTmpArr[1]);
+    }
+
+    public Rect getSampledRegion() {
+        return mSampledRegion;
+    }
+
+    /**
+     * Updates the handle color.
+     * @param isRegionDark Whether the background behind the handle is dark, and thus the handle
+     *                     should be light (and vice versa).
+     * @param animate Whether to animate the change, or apply it immediately.
+     */
+    public void updateHandleColor(boolean isRegionDark, boolean animate) {
+        int newColor = isRegionDark ? mStashedHandleLightColor : mStashedHandleDarkColor;
+        if (mColorChangeAnim != null) {
+            mColorChangeAnim.cancel();
+        }
+        if (animate) {
+            mColorChangeAnim = ObjectAnimator.ofArgb(this,
+                    LauncherAnimUtils.VIEW_BACKGROUND_COLOR, newColor);
+            mColorChangeAnim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mColorChangeAnim = null;
+                }
+            });
+            mColorChangeAnim.setDuration(COLOR_CHANGE_DURATION);
+            mColorChangeAnim.start();
+        } else {
+            setBackgroundColor(newColor);
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
new file mode 100644
index 0000000..2c80f06
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -0,0 +1,188 @@
+/*
+ * 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.taskbar;
+
+import android.animation.Animator;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.RevealOutlineAnimation;
+import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.MultiValueAlpha;
+import com.android.quickstep.AnimatedFloat;
+import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
+
+/**
+ * Handles properties/data collection, then passes the results to our stashed handle View to render.
+ */
+public class StashedHandleViewController {
+
+    public static final int ALPHA_INDEX_STASHED = 0;
+    public static final int ALPHA_INDEX_HOME_DISABLED = 1;
+    private static final int NUM_ALPHA_CHANNELS = 2;
+
+    /**
+     * The SharedPreferences key for whether the stashed handle region is dark.
+     */
+    private static final String SHARED_PREFS_STASHED_HANDLE_REGION_DARK_KEY =
+            "stashed_handle_region_is_dark";
+
+    private final TaskbarActivityContext mActivity;
+    private final SharedPreferences mPrefs;
+    private final StashedHandleView mStashedHandleView;
+    private final int mStashedHandleWidth;
+    private final int mStashedHandleHeight;
+    private final RegionSamplingHelper mRegionSamplingHelper;
+    private final MultiValueAlpha mTaskbarStashedHandleAlpha;
+    private final AnimatedFloat mTaskbarStashedHandleHintScale = new AnimatedFloat(
+            this::updateStashedHandleHintScale);
+
+    // Initialized in init.
+    private TaskbarControllers mControllers;
+
+    // The bounds we want to clip to in the settled state when showing the stashed handle.
+    private final Rect mStashedHandleBounds = new Rect();
+    private float mStashedHandleRadius;
+
+    private boolean mIsAtStashedRevealBounds = true;
+
+    public StashedHandleViewController(TaskbarActivityContext activity,
+            StashedHandleView stashedHandleView) {
+        mActivity = activity;
+        mPrefs = Utilities.getPrefs(mActivity);
+        mStashedHandleView = stashedHandleView;
+        mTaskbarStashedHandleAlpha = new MultiValueAlpha(mStashedHandleView, NUM_ALPHA_CHANNELS);
+        mTaskbarStashedHandleAlpha.setUpdateVisibility(true);
+        mStashedHandleView.updateHandleColor(
+                mPrefs.getBoolean(SHARED_PREFS_STASHED_HANDLE_REGION_DARK_KEY, false),
+                false /* animate */);
+        final Resources resources = mActivity.getResources();
+        mStashedHandleWidth = resources.getDimensionPixelSize(R.dimen.taskbar_stashed_handle_width);
+        mStashedHandleHeight = resources.getDimensionPixelSize(
+                R.dimen.taskbar_stashed_handle_height);
+        mRegionSamplingHelper = new RegionSamplingHelper(mStashedHandleView,
+                new RegionSamplingHelper.SamplingCallback() {
+                    @Override
+                    public void onRegionDarknessChanged(boolean isRegionDark) {
+                        mStashedHandleView.updateHandleColor(isRegionDark, true /* animate */);
+                        mPrefs.edit().putBoolean(SHARED_PREFS_STASHED_HANDLE_REGION_DARK_KEY,
+                                isRegionDark).apply();
+                    }
+
+                    @Override
+                    public Rect getSampledRegion(View sampledView) {
+                        return mStashedHandleView.getSampledRegion();
+                    }
+                }, Executors.UI_HELPER_EXECUTOR);
+    }
+
+    public void init(TaskbarControllers controllers) {
+        mControllers = controllers;
+        mStashedHandleView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarSize;
+
+        mTaskbarStashedHandleAlpha.getProperty(ALPHA_INDEX_STASHED).setValue(0);
+        mTaskbarStashedHandleHintScale.updateValue(1f);
+
+        final int stashedTaskbarHeight = mControllers.taskbarStashController.getStashedHeight();
+        mStashedHandleView.setOutlineProvider(new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+                final int stashedCenterX = view.getWidth() / 2;
+                final int stashedCenterY = view.getHeight() - stashedTaskbarHeight / 2;
+                mStashedHandleBounds.set(
+                        stashedCenterX - mStashedHandleWidth / 2,
+                        stashedCenterY - mStashedHandleHeight / 2,
+                        stashedCenterX + mStashedHandleWidth / 2,
+                        stashedCenterY + mStashedHandleHeight / 2);
+                mStashedHandleView.updateSampledRegion(mStashedHandleBounds);
+                mStashedHandleRadius = view.getHeight() / 2f;
+                outline.setRoundRect(mStashedHandleBounds, mStashedHandleRadius);
+            }
+        });
+
+        mStashedHandleView.addOnLayoutChangeListener((view, i, i1, i2, i3, i4, i5, i6, i7) -> {
+            final int stashedCenterX = view.getWidth() / 2;
+            final int stashedCenterY = view.getHeight() - stashedTaskbarHeight / 2;
+
+            view.setPivotX(stashedCenterX);
+            view.setPivotY(stashedCenterY);
+        });
+    }
+
+    public void onDestroy() {
+        mRegionSamplingHelper.stopAndDestroy();
+    }
+
+    public MultiValueAlpha getStashedHandleAlpha() {
+        return mTaskbarStashedHandleAlpha;
+    }
+
+    public AnimatedFloat getStashedHandleHintScale() {
+        return mTaskbarStashedHandleHintScale;
+    }
+
+    /**
+     * Creates and returns a {@link RevealOutlineAnimation} Animator that updates the stashed handle
+     * shape and size. When stashed, the shape is a thin rounded pill. When unstashed, the shape
+     * morphs into the size of where the taskbar icons will be.
+     */
+    public @Nullable Animator createRevealAnimToIsStashed(boolean isStashed) {
+        if (mIsAtStashedRevealBounds == isStashed) {
+            return null;
+        }
+        mIsAtStashedRevealBounds = isStashed;
+        final RevealOutlineAnimation handleRevealProvider = new RoundedRectRevealOutlineProvider(
+                mStashedHandleRadius, mStashedHandleRadius,
+                mControllers.taskbarViewController.getIconLayoutBounds(), mStashedHandleBounds);
+        return handleRevealProvider.createRevealAnimator(mStashedHandleView, !isStashed);
+    }
+
+    public void onIsStashed(boolean isStashed) {
+        mRegionSamplingHelper.setWindowVisible(isStashed);
+        if (isStashed) {
+            mStashedHandleView.updateSampledRegion(mStashedHandleBounds);
+            mRegionSamplingHelper.start(mStashedHandleView.getSampledRegion());
+        } else {
+            mRegionSamplingHelper.stop();
+        }
+    }
+
+    protected void updateStashedHandleHintScale() {
+        mStashedHandleView.setScaleX(mTaskbarStashedHandleHintScale.value);
+        mStashedHandleView.setScaleY(mTaskbarStashedHandleHintScale.value);
+    }
+
+    /**
+     * Should be called when the home button is disabled, so we can hide this handle as well.
+     */
+    public void setIsHomeButtonDisabled(boolean homeDisabled) {
+        mTaskbarStashedHandleAlpha.getProperty(ALPHA_INDEX_HOME_DISABLED).setValue(
+                homeDisabled ? 0 : 1);
+    }
+
+    public boolean isStashedHandleVisible() {
+        return mStashedHandleView.getVisibility() == View.VISIBLE;
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 4ba0ee0..370496a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -17,8 +17,11 @@
 
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
 import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
 import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
 
@@ -27,19 +30,21 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.LauncherApps;
+import android.graphics.Insets;
 import android.graphics.PixelFormat;
-import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
 import android.os.Process;
 import android.os.SystemProperties;
+import android.provider.Settings;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
 import android.view.Display;
 import android.view.Gravity;
 import android.view.LayoutInflater;
+import android.view.RoundedCorner;
 import android.view.View;
 import android.view.WindowManager;
+import android.widget.FrameLayout;
 import android.widget.Toast;
 
 import androidx.annotation.NonNull;
@@ -47,30 +52,28 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.DragSource;
-import com.android.launcher3.DropTarget;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.R;
-import com.android.launcher3.dragndrop.DragController;
-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.folder.FolderIcon;
+import com.android.launcher3.logger.LauncherAtom;
 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.taskbar.TaskbarNavButtonController.TaskbarButton;
+import com.android.launcher3.taskbar.contextual.RotationButtonController;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.SettingsCache;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.util.ViewCache;
 import com.android.launcher3.views.ActivityContext;
 import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.SystemUiProxy;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
 
 /**
  * The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements
@@ -88,59 +91,83 @@
     private final DeviceProfile mDeviceProfile;
     private final LayoutInflater mLayoutInflater;
     private final TaskbarDragLayer mDragLayer;
-    private final TaskbarIconController mIconController;
-    private final MyDragController mDragController;
+    private final TaskbarControllers mControllers;
 
     private final WindowManager mWindowManager;
+    private final @Nullable RoundedCorner mLeftCorner, mRightCorner;
     private WindowManager.LayoutParams mWindowLayoutParams;
+    private boolean mIsFullscreen;
+    // The size we should return to when we call setTaskbarWindowFullscreen(false)
+    private int mLastRequestedNonFullscreenHeight;
 
     private final SysUINavigationMode.Mode mNavMode;
-    private final TaskbarNavButtonController mNavButtonController;
+    private final ViewCache mViewCache = new ViewCache();
 
     private final boolean mIsSafeModeEnabled;
-
-    @NonNull
-    private TaskbarUIController mUIController = TaskbarUIController.DEFAULT;
-
-    private final View.OnClickListener mOnTaskbarIconClickListener;
-    private final View.OnLongClickListener mOnTaskbarIconLongClickListener;
+    private final boolean mIsUserSetupComplete;
+    private boolean mIsDestroyed = false;
 
     public TaskbarActivityContext(Context windowContext, DeviceProfile dp,
-            TaskbarNavButtonController buttonController) {
+            TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider
+            unfoldTransitionProgressProvider) {
         super(windowContext, Themes.getActivityThemeRes(windowContext));
         mDeviceProfile = dp;
-        mNavButtonController = buttonController;
+
         mNavMode = SysUINavigationMode.getMode(windowContext);
         mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
                 () -> getPackageManager().isSafeMode());
-
-        mOnTaskbarIconLongClickListener =
-                new TaskbarDragController(this)::startSystemDragOnLongClick;
-        mOnTaskbarIconClickListener = this::onTaskbarIconClicked;
+        mIsUserSetupComplete = SettingsCache.INSTANCE.get(this).getValue(
+                Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0);
 
         float taskbarIconSize = getResources().getDimension(R.dimen.taskbar_icon_size);
+        mDeviceProfile.updateIconSize(1, getResources());
         float iconScale = taskbarIconSize / mDeviceProfile.iconSizePx;
         mDeviceProfile.updateIconSize(iconScale, getResources());
 
         mLayoutInflater = LayoutInflater.from(this).cloneInContext(this);
-        mDragLayer = (TaskbarDragLayer) mLayoutInflater
-                .inflate(R.layout.taskbar, null, false);
-        mIconController = new TaskbarIconController(this, mDragLayer);
-        mDragController = new MyDragController(this);
+
+        // Inflate views.
+        mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate(
+                R.layout.taskbar, null, false);
+        TaskbarView taskbarView = mDragLayer.findViewById(R.id.taskbar_view);
+        TaskbarScrimView taskbarScrimView = mDragLayer.findViewById(R.id.taskbar_scrim);
+        FrameLayout navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view);
+        StashedHandleView stashedHandleView = mDragLayer.findViewById(R.id.stashed_handle);
 
         Display display = windowContext.getDisplay();
         Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY
                 ? windowContext.getApplicationContext()
                 : windowContext.getApplicationContext().createDisplayContext(display);
         mWindowManager = c.getSystemService(WindowManager.class);
+        mLeftCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
+        mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
+
+        // Construct controllers.
+        mControllers = new TaskbarControllers(this,
+                new TaskbarDragController(this),
+                buttonController,
+                new NavbarButtonsViewController(this, navButtonsView),
+                new RotationButtonController(this, R.color.popup_color_primary_light,
+                        R.color.popup_color_primary_light),
+                new TaskbarDragLayerController(this, mDragLayer),
+                new TaskbarViewController(this, taskbarView),
+                new TaskbarScrimViewController(this, taskbarScrimView),
+                new TaskbarUnfoldAnimationController(unfoldTransitionProgressProvider,
+                        mWindowManager),
+                new TaskbarKeyguardController(this),
+                new StashedHandleViewController(this, stashedHandleView),
+                new TaskbarStashController(this),
+                new TaskbarEduController(this));
     }
 
-    public void init() {
+    public void init(TaskbarSharedState sharedState) {
+        mLastRequestedNonFullscreenHeight = getDefaultTaskbarWindowHeight();
         mWindowLayoutParams = new WindowManager.LayoutParams(
                 MATCH_PARENT,
-                mDeviceProfile.taskbarSize,
-                TYPE_APPLICATION_OVERLAY,
-                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+                mLastRequestedNonFullscreenHeight,
+                TYPE_NAVIGATION_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_SLIPPERY,
                 PixelFormat.TRANSLUCENT);
         mWindowLayoutParams.setTitle(WINDOW_TITLE);
         mWindowLayoutParams.packageName = getPackageName();
@@ -148,31 +175,35 @@
         mWindowLayoutParams.setFitInsetsTypes(0);
         mWindowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
         mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        mWindowLayoutParams.setSystemApplicationOverlay(true);
+        mWindowLayoutParams.privateFlags =
+                WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
 
         WindowManagerWrapper wmWrapper = WindowManagerWrapper.getInstance();
         wmWrapper.setProvidesInsetsTypes(
                 mWindowLayoutParams,
                 new int[] { ITYPE_EXTRA_NAVIGATION_BAR, ITYPE_BOTTOM_TAPPABLE_ELEMENT }
         );
+        // Adjust the frame by the rounded corners (ie. leaving just the bar as the inset) when
+        // the IME is showing
+        mWindowLayoutParams.providedInternalImeInsets = Insets.of(0,
+                getDefaultTaskbarWindowHeight() - mDeviceProfile.taskbarSize, 0, 0);
 
-        mIconController.init(mOnTaskbarIconClickListener, mOnTaskbarIconLongClickListener);
+        // Initialize controllers after all are constructed.
+        mControllers.init(sharedState);
+
         mWindowManager.addView(mDragLayer, mWindowLayoutParams);
     }
 
-    /**
-     * Updates the TaskbarContainer height (pass deviceProfile.taskbarSize to reset).
-     */
-    public void setTaskbarWindowHeight(int height) {
-        if (mWindowLayoutParams.height == height) {
-            return;
-        }
-        mWindowLayoutParams.height = height;
-        mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
+    public boolean isThreeButtonNav() {
+        return mNavMode == Mode.THREE_BUTTONS;
     }
 
-    public boolean canShowNavButtons() {
-        return ENABLE_THREE_BUTTON_TASKBAR && mNavMode == Mode.THREE_BUTTONS;
+    public int getLeftCornerRadius() {
+        return mLeftCorner == null ? 0 : mLeftCorner.getRadius();
+    }
+
+    public int getRightCornerRadius() {
+        return mRightCorner == null ? 0 : mRightCorner.getRadius();
     }
 
     @Override
@@ -192,59 +223,178 @@
 
     @Override
     public Rect getFolderBoundingBox() {
-        return mDragLayer.getFolderBoundingBox();
+        return mControllers.taskbarDragLayerController.getFolderBoundingBox();
     }
 
     @Override
-    public DragController getDragController() {
-        return mDragController;
+    public TaskbarDragController getDragController() {
+        return mControllers.taskbarDragController;
+    }
+
+    @Override
+    public ViewCache getViewCache() {
+        return mViewCache;
+    }
+
+    @Override
+    public boolean supportsIme() {
+        // Currently we don't support IME because we have FLAG_NOT_FOCUSABLE. We can remove that
+        // flag when opening a floating view that needs IME (such as Folder), but then that means
+        // Taskbar will be below IME and thus users can't click the back button.
+        return false;
+    }
+
+    /**
+     * Change from hotseat/predicted hotseat to taskbar container.
+     */
+    @Override
+    public void applyOverwritesToLogItem(LauncherAtom.ItemInfo.Builder itemInfoBuilder) {
+        if (!itemInfoBuilder.hasContainerInfo()) {
+            return;
+        }
+        LauncherAtom.ContainerInfo oldContainer = itemInfoBuilder.getContainerInfo();
+
+        if (oldContainer.hasPredictedHotseatContainer()) {
+            LauncherAtom.PredictedHotseatContainer predictedHotseat =
+                    oldContainer.getPredictedHotseatContainer();
+            LauncherAtom.TaskBarContainer.Builder taskbarBuilder =
+                    LauncherAtom.TaskBarContainer.newBuilder();
+
+            if (predictedHotseat.hasIndex()) {
+                taskbarBuilder.setIndex(predictedHotseat.getIndex());
+            }
+            if (predictedHotseat.hasCardinality()) {
+                taskbarBuilder.setCardinality(predictedHotseat.getCardinality());
+            }
+
+            itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
+                    .setTaskBarContainer(taskbarBuilder));
+        } else if (oldContainer.hasHotseat()) {
+            LauncherAtom.HotseatContainer hotseat = oldContainer.getHotseat();
+            LauncherAtom.TaskBarContainer.Builder taskbarBuilder =
+                    LauncherAtom.TaskBarContainer.newBuilder();
+
+            if (hotseat.hasIndex()) {
+                taskbarBuilder.setIndex(hotseat.getIndex());
+            }
+
+            itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
+                    .setTaskBarContainer(taskbarBuilder));
+        } else if (oldContainer.hasFolder() && oldContainer.getFolder().hasHotseat()) {
+            LauncherAtom.FolderContainer.Builder folderBuilder = oldContainer.getFolder()
+                    .toBuilder();
+            LauncherAtom.HotseatContainer hotseat = folderBuilder.getHotseat();
+            LauncherAtom.TaskBarContainer.Builder taskbarBuilder =
+                    LauncherAtom.TaskBarContainer.newBuilder();
+
+            if (hotseat.hasIndex()) {
+                taskbarBuilder.setIndex(hotseat.getIndex());
+            }
+
+            folderBuilder.setTaskbar(taskbarBuilder);
+            folderBuilder.clearHotseat();
+            itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
+                    .setFolder(folderBuilder));
+        }
     }
 
     /**
      * Sets a new data-source for this taskbar instance
      */
     public void setUIController(@NonNull TaskbarUIController uiController) {
-        mUIController.onDestroy();
-        mUIController = uiController;
-        mIconController.setUIController(mUIController);
-        mUIController.onCreate();
+        mControllers.uiController.onDestroy();
+        mControllers.uiController = uiController;
+        mControllers.uiController.init(mControllers);
+    }
+
+    /**
+     * Sets the flag indicating setup UI is visible
+     */
+    public void setSetupUIVisible(boolean isVisible) {
+        mControllers.taskbarStashController.setSetupUIVisible(isVisible);
     }
 
     /**
      * Called when this instance of taskbar is no longer needed
      */
     public void onDestroy() {
+        mIsDestroyed = true;
         setUIController(TaskbarUIController.DEFAULT);
-        mIconController.onDestroy();
+        mControllers.onDestroy();
         mWindowManager.removeViewImmediate(mDragLayer);
     }
 
-    void onNavigationButtonClick(@TaskbarButton int buttonType) {
-        mNavButtonController.onButtonClick(buttonType);
+    public void updateSysuiStateFlags(int systemUiStateFlags) {
+        mControllers.navbarButtonsViewController.updateStateForSysuiFlags(systemUiStateFlags);
+        mControllers.taskbarViewController.setImeIsVisible(
+                mControllers.navbarButtonsViewController.isImeVisible());
+        boolean panelExpanded = (systemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) != 0;
+        boolean inSettings = (systemUiStateFlags & SYSUI_STATE_QUICK_SETTINGS_EXPANDED) != 0;
+        mControllers.taskbarViewController.setNotificationShadeIsExpanded(
+                panelExpanded || inSettings);
+        mControllers.taskbarViewController.setRecentsButtonDisabled(
+                mControllers.navbarButtonsViewController.isRecentsDisabled());
+        mControllers.stashedHandleViewController.setIsHomeButtonDisabled(
+                mControllers.navbarButtonsViewController.isHomeDisabled());
+        mControllers.taskbarKeyguardController.updateStateForSysuiFlags(systemUiStateFlags);
+        mControllers.taskbarStashController.updateStateForSysuiFlags(systemUiStateFlags);
+        mControllers.taskbarScrimViewController.updateStateForSysuiFlags(systemUiStateFlags);
     }
 
-    /**
-     * Should be called when the IME visibility changes, so we can hide/show Taskbar accordingly.
-     */
-    public void setImeIsVisible(boolean isImeVisible) {
-        mIconController.setImeIsVisible(isImeVisible);
+    public void onRotationProposal(int rotation, boolean isValid) {
+        mControllers.rotationButtonController.onRotationProposal(rotation, isValid);
     }
 
-    /**
-     * When in 3 button nav, the above doesn't get called since we prevent sysui nav bar from
-     * instantiating at all, which is what's responsible for sending sysui state flags over.
-     *
-     * @param vis IME visibility flag
-     */
-    public void updateImeStatus(int displayId, int vis, boolean showImeSwitcher) {
-        mIconController.updateImeStatus(displayId, vis, showImeSwitcher);
+    public void disableNavBarElements(int displayId, int state1, int state2, boolean animate) {
+        if (displayId != getDisplayId()) {
+            return;
+        }
+        mControllers.rotationButtonController.onDisable2FlagChanged(state2);
+    }
+
+    public void onSystemBarAttributesChanged(int displayId, int behavior) {
+        mControllers.rotationButtonController.onBehaviorChanged(displayId, behavior);
     }
 
     /**
      * Updates the TaskbarContainer to MATCH_PARENT vs original Taskbar size.
      */
-    protected void setTaskbarWindowFullscreen(boolean fullscreen) {
-        setTaskbarWindowHeight(fullscreen ? MATCH_PARENT : getDeviceProfile().taskbarSize);
+    public void setTaskbarWindowFullscreen(boolean fullscreen) {
+        SystemUiProxy.INSTANCE.getNoCreate().notifyTaskbarAutohideSuspend(fullscreen);
+        mIsFullscreen = fullscreen;
+        setTaskbarWindowHeight(fullscreen ? MATCH_PARENT : mLastRequestedNonFullscreenHeight);
+    }
+
+    public boolean isTaskbarWindowFullscreen() {
+        return mIsFullscreen;
+    }
+
+    /**
+     * Updates the TaskbarContainer height (pass {@link #getDefaultTaskbarWindowHeight()} to reset).
+     */
+    public void setTaskbarWindowHeight(int height) {
+        if (mWindowLayoutParams.height == height || mIsDestroyed) {
+            return;
+        }
+        if (height != MATCH_PARENT) {
+            mLastRequestedNonFullscreenHeight = height;
+            if (mIsFullscreen) {
+                // We still need to be fullscreen, so defer any change to our height until we call
+                // setTaskbarWindowFullscreen(false). For example, this could happen when dragging
+                // from the gesture region, as the drag will cancel the gesture and reset launcher's
+                // state, which in turn normally would reset the taskbar window height as well.
+                return;
+            }
+        }
+        mWindowLayoutParams.height = height;
+        mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
+    }
+
+    /**
+     * Returns the default height of the window, including the static corner radii above taskbar.
+     */
+    public int getDefaultTaskbarWindowHeight() {
+        return mDeviceProfile.taskbarSize + Math.max(getLeftCornerRadius(), getRightCornerRadius());
     }
 
     protected void onTaskbarIconClicked(View view) {
@@ -260,10 +410,11 @@
 
             getDragLayer().post(() -> {
                 folder.animateOpen();
+                getStatsLogManager().logger().withItemInfo(folder.mInfo).log(LAUNCHER_FOLDER_OPEN);
 
                 folder.iterateOverItems((itemInfo, itemView) -> {
-                    itemView.setOnClickListener(mOnTaskbarIconClickListener);
-                    itemView.setOnLongClickListener(mOnTaskbarIconLongClickListener);
+                    mControllers.taskbarViewController
+                            .setClickAndLongClickListenersForIcon(itemView);
                     // To play haptic when dragging, like other Taskbar items do.
                     itemView.setHapticFeedbackEnabled(true);
                     return false;
@@ -271,7 +422,9 @@
             });
         } else if (tag instanceof WorkspaceItemInfo) {
             WorkspaceItemInfo info = (WorkspaceItemInfo) tag;
-            if (!(info.isDisabled() && ItemClickHandler.handleDisabledItemClicked(info, this))) {
+            if (info.isDisabled()) {
+                ItemClickHandler.handleDisabledItemClicked(info, this);
+            } else {
                 Intent intent = new Intent(info.getIntent())
                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                 try {
@@ -295,6 +448,8 @@
                         getSystemService(LauncherApps.class).startMainActivity(
                                 intent.getComponent(), info.user, intent.getSourceBounds(), null);
                     }
+
+                    mControllers.uiController.onTaskbarIconLaunched(info);
                 } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
                     Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT)
                             .show();
@@ -308,26 +463,23 @@
         AbstractFloatingView.closeAllOpenViews(this);
     }
 
-    private static class MyDragController extends DragController<TaskbarActivityContext> {
-        MyDragController(TaskbarActivityContext activity) {
-            super(activity);
-        }
+    /**
+     * Called when we detect a long press in the nav region before passing the gesture slop.
+     * @return Whether taskbar handled the long press, and thus should cancel the gesture.
+     */
+    public boolean onLongPressToUnstashTaskbar() {
+        return mControllers.taskbarStashController.onLongPressToUnstashTaskbar();
+    }
 
-        @Override
-        protected DragView startDrag(@Nullable Drawable drawable, @Nullable View view,
-                DraggableView originalView, int dragLayerX, int dragLayerY, DragSource source,
-                ItemInfo dragInfo, Point dragOffset, Rect dragRegion, float initialDragViewScale,
-                float dragViewScaleOnDrop, DragOptions options) {
-            return null;
-        }
+    /**
+     * Called when we detect a motion down or up/cancel in the nav region while stashed.
+     * @param animateForward Whether to animate towards the unstashed hint state or back to stashed.
+     */
+    public void startTaskbarUnstashHint(boolean animateForward) {
+        mControllers.taskbarStashController.startUnstashHint(animateForward);
+    }
 
-        @Override
-        protected void exitDrag() {
-        }
-
-        @Override
-        protected DropTarget getDefaultDropTarget(int[] dropCoordinates) {
-            return null;
-        }
+    protected boolean isUserSetupComplete() {
+        return mIsUserSetupComplete;
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
deleted file mode 100644
index e20ddf8..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
+++ /dev/null
@@ -1,151 +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.taskbar;
-
-import static com.android.launcher3.LauncherState.TASKBAR;
-
-import android.animation.Animator;
-
-import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.taskbar.LauncherTaskbarUIController.TaskbarAnimationControllerCallbacks;
-import com.android.quickstep.AnimatedFloat;
-import com.android.quickstep.SystemUiProxy;
-import com.android.systemui.shared.system.QuickStepContract;
-
-/**
- * Works with TaskbarController to update the TaskbarView's visual properties based on factors such
- * as LauncherState, whether Launcher is in the foreground, etc.
- */
-public class TaskbarAnimationController {
-
-    private static final long IME_VISIBILITY_ALPHA_DURATION = 120;
-
-    private final BaseQuickstepLauncher mLauncher;
-    private final TaskbarAnimationControllerCallbacks mTaskbarCallbacks;
-
-    // Background alpha.
-    private final AnimatedFloat mTaskbarBackgroundAlpha = new AnimatedFloat(
-            this::onTaskbarBackgroundAlphaChanged);
-
-    // Overall visibility.
-    private final AnimatedFloat mTaskbarVisibilityAlphaForLauncherState = new AnimatedFloat(
-            this::updateVisibilityAlpha);
-    private final AnimatedFloat mTaskbarVisibilityAlphaForIme = new AnimatedFloat(
-            this::updateVisibilityAlphaForIme);
-
-    // Scale.
-    private final AnimatedFloat mTaskbarScaleForLauncherState = new AnimatedFloat(
-            this::updateScale);
-
-    // TranslationY.
-    private final AnimatedFloat mTaskbarTranslationYForLauncherState = new AnimatedFloat(
-            this::updateTranslationY);
-
-    public TaskbarAnimationController(BaseQuickstepLauncher launcher,
-            TaskbarAnimationControllerCallbacks taskbarCallbacks) {
-        mLauncher = launcher;
-        mTaskbarCallbacks = taskbarCallbacks;
-    }
-
-    protected void init() {
-        mTaskbarBackgroundAlpha.updateValue(mLauncher.hasBeenResumed() ? 0f : 1f);
-        boolean isVisibleForLauncherState = (mLauncher.getStateManager().getState()
-                .getVisibleElements(mLauncher) & TASKBAR) != 0;
-        mTaskbarVisibilityAlphaForLauncherState.updateValue(isVisibleForLauncherState ? 1f : 0f);
-        boolean isImeVisible = (SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags()
-                & QuickStepContract.SYSUI_STATE_IME_SHOWING) != 0;
-        mTaskbarVisibilityAlphaForIme.updateValue(isImeVisible ? 0f : 1f);
-
-        onTaskbarBackgroundAlphaChanged();
-        updateVisibilityAlpha();
-    }
-
-    protected void cleanup() {
-        setNavBarButtonAlpha(1f);
-    }
-
-    protected AnimatedFloat getTaskbarVisibilityForLauncherState() {
-        return mTaskbarVisibilityAlphaForLauncherState;
-    }
-
-    protected AnimatedFloat getTaskbarScaleForLauncherState() {
-        return mTaskbarScaleForLauncherState;
-    }
-
-    protected AnimatedFloat getTaskbarTranslationYForLauncherState() {
-        return mTaskbarTranslationYForLauncherState;
-    }
-
-    protected Animator createAnimToBackgroundAlpha(float toAlpha, long duration) {
-        return mTaskbarBackgroundAlpha.animateToValue(mTaskbarBackgroundAlpha.value, toAlpha)
-                .setDuration(duration);
-    }
-
-    protected void animateToVisibilityForIme(float toAlpha) {
-        mTaskbarVisibilityAlphaForIme.animateToValue(mTaskbarVisibilityAlphaForIme.value, toAlpha)
-                .setDuration(IME_VISIBILITY_ALPHA_DURATION).start();
-    }
-
-    private void onTaskbarBackgroundAlphaChanged() {
-        mTaskbarCallbacks.updateTaskbarBackgroundAlpha(mTaskbarBackgroundAlpha.value);
-        updateVisibilityAlpha();
-        updateScale();
-        updateTranslationY();
-    }
-
-    private void updateVisibilityAlpha() {
-        // We use mTaskbarBackgroundAlpha as a proxy for whether Launcher is resumed/paused, the
-        // assumption being that Taskbar should always be visible regardless of the current
-        // LauncherState if Launcher is paused.
-        float alphaDueToIme = mTaskbarVisibilityAlphaForIme.value;
-        float alphaDueToLauncher = Math.max(mTaskbarBackgroundAlpha.value,
-                mTaskbarVisibilityAlphaForLauncherState.value);
-        float taskbarAlpha = alphaDueToLauncher * alphaDueToIme;
-        mTaskbarCallbacks.updateTaskbarVisibilityAlpha(taskbarAlpha);
-
-        // Make the nav bar invisible if taskbar is visible.
-        setNavBarButtonAlpha(1f - taskbarAlpha);
-    }
-
-    private void updateVisibilityAlphaForIme() {
-        updateVisibilityAlpha();
-        float taskbarAlphaDueToIme = mTaskbarVisibilityAlphaForIme.value;
-        mTaskbarCallbacks.updateImeBarVisibilityAlpha(1f - taskbarAlphaDueToIme);
-    }
-
-    private void updateScale() {
-        // We use mTaskbarBackgroundAlpha as a proxy for whether Launcher is resumed/paused, the
-        // assumption being that Taskbar should always be at scale 1f regardless of the current
-        // LauncherState if Launcher is paused.
-        float scale = mTaskbarScaleForLauncherState.value;
-        scale = Utilities.mapRange(mTaskbarBackgroundAlpha.value, scale, 1f);
-        mTaskbarCallbacks.updateTaskbarScale(scale);
-    }
-
-    private void updateTranslationY() {
-        // We use mTaskbarBackgroundAlpha as a proxy for whether Launcher is resumed/paused, the
-        // assumption being that Taskbar should always be at translationY 0f regardless of the
-        // current LauncherState if Launcher is paused.
-        float translationY = mTaskbarTranslationYForLauncherState.value;
-        translationY = Utilities.mapRange(mTaskbarBackgroundAlpha.value, translationY, 0f);
-        mTaskbarCallbacks.updateTaskbarTranslationY(translationY);
-    }
-
-    private void setNavBarButtonAlpha(float navBarAlpha) {
-        SystemUiProxy.INSTANCE.get(mLauncher).setNavBarButtonAlpha(navBarAlpha, false);
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
new file mode 100644
index 0000000..8684c29
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -0,0 +1,105 @@
+/*
+ * 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.taskbar;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.taskbar.contextual.RotationButtonController;
+
+/**
+ * Hosts various taskbar controllers to facilitate passing between one another.
+ */
+public class TaskbarControllers {
+
+    public final TaskbarActivityContext taskbarActivityContext;
+    public final TaskbarDragController taskbarDragController;
+    public final TaskbarNavButtonController navButtonController;
+    public final NavbarButtonsViewController navbarButtonsViewController;
+    public final RotationButtonController rotationButtonController;
+    public final TaskbarDragLayerController taskbarDragLayerController;
+    public final TaskbarScrimViewController taskbarScrimViewController;
+    public final TaskbarViewController taskbarViewController;
+    public final TaskbarUnfoldAnimationController taskbarUnfoldAnimationController;
+    public final TaskbarKeyguardController taskbarKeyguardController;
+    public final StashedHandleViewController stashedHandleViewController;
+    public final TaskbarStashController taskbarStashController;
+    public final TaskbarEduController taskbarEduController;
+
+    /** Do not store this controller, as it may change at runtime. */
+    @NonNull public TaskbarUIController uiController = TaskbarUIController.DEFAULT;
+
+    public TaskbarControllers(TaskbarActivityContext taskbarActivityContext,
+            TaskbarDragController taskbarDragController,
+            TaskbarNavButtonController navButtonController,
+            NavbarButtonsViewController navbarButtonsViewController,
+            RotationButtonController rotationButtonController,
+            TaskbarDragLayerController taskbarDragLayerController,
+            TaskbarViewController taskbarViewController,
+            TaskbarScrimViewController taskbarScrimViewController,
+            TaskbarUnfoldAnimationController taskbarUnfoldAnimationController,
+            TaskbarKeyguardController taskbarKeyguardController,
+            StashedHandleViewController stashedHandleViewController,
+            TaskbarStashController taskbarStashController,
+            TaskbarEduController taskbarEduController) {
+        this.taskbarActivityContext = taskbarActivityContext;
+        this.taskbarDragController = taskbarDragController;
+        this.navButtonController = navButtonController;
+        this.navbarButtonsViewController = navbarButtonsViewController;
+        this.rotationButtonController = rotationButtonController;
+        this.taskbarDragLayerController = taskbarDragLayerController;
+        this.taskbarViewController = taskbarViewController;
+        this.taskbarScrimViewController = taskbarScrimViewController;
+        this.taskbarUnfoldAnimationController = taskbarUnfoldAnimationController;
+        this.taskbarKeyguardController = taskbarKeyguardController;
+        this.stashedHandleViewController = stashedHandleViewController;
+        this.taskbarStashController = taskbarStashController;
+        this.taskbarEduController = taskbarEduController;
+    }
+
+    /**
+     * Initializes all controllers. Note that controllers can now reference each other through this
+     * TaskbarControllers instance, but should be careful to only access things that were created
+     * in constructors for now, as some controllers may still be waiting for init().
+     */
+    public void init(TaskbarSharedState sharedState) {
+        navbarButtonsViewController.init(this, sharedState);
+        if (taskbarActivityContext.isThreeButtonNav()) {
+            rotationButtonController.init();
+        }
+        taskbarDragLayerController.init(this);
+        taskbarViewController.init(this);
+        taskbarScrimViewController.init(this);
+        taskbarUnfoldAnimationController.init(this);
+        taskbarKeyguardController.init(navbarButtonsViewController);
+        stashedHandleViewController.init(this);
+        taskbarStashController.init(this, sharedState);
+        taskbarEduController.init(this);
+    }
+
+    /**
+     * Cleans up all controllers.
+     */
+    public void onDestroy() {
+        navbarButtonsViewController.onDestroy();
+        uiController.onDestroy();
+        rotationButtonController.onDestroy();
+        taskbarDragLayerController.onDestroy();
+        taskbarKeyguardController.onDestroy();
+        taskbarUnfoldAnimationController.onDestroy();
+        taskbarViewController.onDestroy();
+        stashedHandleViewController.onDestroy();
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index ee44927..1afbd17 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -20,19 +20,38 @@
 
 import android.content.ClipData;
 import android.content.ClipDescription;
-import android.content.Context;
 import android.content.Intent;
 import android.content.pm.LauncherApps;
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
 import android.view.DragEvent;
+import android.view.MotionEvent;
 import android.view.View;
 
+import androidx.annotation.Nullable;
+
+import com.android.internal.logging.InstanceId;
+import com.android.internal.logging.InstanceIdSequence;
+import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
+import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragDriver;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.icons.FastBitmapDrawable;
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.ClipDescriptionCompat;
@@ -41,14 +60,20 @@
 /**
  * Handles long click on Taskbar items to start a system drag and drop operation.
  */
-public class TaskbarDragController {
+public class TaskbarDragController extends DragController<TaskbarActivityContext>  {
 
-    private final Context mContext;
     private final int mDragIconSize;
+    private final int[] mTempXY = new int[2];
 
-    public TaskbarDragController(Context context) {
-        mContext = context;
-        Resources resources = mContext.getResources();
+    // Where the initial touch was relative to the dragged icon.
+    private int mRegistrationX;
+    private int mRegistrationY;
+
+    private boolean mIsSystemDragInProgress;
+
+    public TaskbarDragController(TaskbarActivityContext activity) {
+        super(activity);
+        Resources resources = mActivity.getResources();
         mDragIconSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_drag_icon_size);
     }
 
@@ -57,18 +82,165 @@
      * generate the ClipDescription and Intent.
      * @return Whether {@link View#startDragAndDrop} started successfully.
      */
-    protected boolean startSystemDragOnLongClick(View view) {
+    protected boolean startDragOnLongClick(View view) {
         if (!(view instanceof BubbleTextView)) {
             return false;
         }
 
         BubbleTextView btv = (BubbleTextView) view;
-        View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view) {
+
+        mActivity.setTaskbarWindowFullscreen(true);
+        view.post(() -> {
+            startInternalDrag(btv);
+            btv.setVisibility(INVISIBLE);
+        });
+        return true;
+    }
+
+    private void startInternalDrag(BubbleTextView btv) {
+        float iconScale = 1f;
+        Drawable icon = btv.getIcon();
+        if (icon instanceof FastBitmapDrawable) {
+            iconScale = ((FastBitmapDrawable) icon).getAnimatedScale();
+        }
+
+        // Clear the pressed state if necessary
+        btv.clearFocus();
+        btv.setPressed(false);
+        btv.clearPressedBackground();
+
+        final DragPreviewProvider previewProvider = new DragPreviewProvider(btv);
+        final Drawable drawable = previewProvider.createDrawable();
+        final float scale = previewProvider.getScaleAndPosition(drawable, mTempXY);
+        int dragLayerX = mTempXY[0];
+        int dragLayerY = mTempXY[1];
+
+        Rect dragRect = new Rect();
+        btv.getSourceVisualDragBounds(dragRect);
+        dragLayerY += dragRect.top;
+
+        DragOptions dragOptions = new DragOptions();
+        dragOptions.preDragCondition = new DragOptions.PreDragCondition() {
+            private DragView mDragView;
+
+            @Override
+            public boolean shouldStartDrag(double distanceDragged) {
+                return mDragView != null && mDragView.isAnimationFinished();
+            }
+
+            @Override
+            public void onPreDragStart(DropTarget.DragObject dragObject) {
+                mDragView = dragObject.dragView;
+            }
+
+            @Override
+            public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
+                mDragView = null;
+            }
+        };
+        // TODO: open popup/pre-drag
+        // PopupContainerWithArrow popupContainer = PopupContainerWithArrow.showForIcon(view);
+        // if (popupContainer != null) {
+        //     dragOptions.preDragCondition = popupContainer.createPreDragCondition();
+        // }
+
+        startDrag(
+                drawable,
+                /* view = */ null,
+                /* originalView = */ btv,
+                dragLayerX,
+                dragLayerY,
+                (View target, DropTarget.DragObject d, boolean success) -> {} /* DragSource */,
+                (WorkspaceItemInfo) btv.getTag(),
+                /* dragVisualizeOffset = */ null,
+                dragRect,
+                scale * iconScale,
+                scale,
+                dragOptions);
+    }
+
+    @Override
+    protected DragView startDrag(@Nullable Drawable drawable, @Nullable View view,
+            DraggableView originalView, int dragLayerX, int dragLayerY, DragSource source,
+            ItemInfo dragInfo, Point dragOffset, Rect dragRegion, float initialDragViewScale,
+            float dragViewScaleOnDrop, DragOptions options) {
+        mOptions = options;
+
+        mRegistrationX = mMotionDown.x - dragLayerX;
+        mRegistrationY = mMotionDown.y - dragLayerY;
+
+        final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
+        final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
+
+        mLastDropTarget = null;
+
+        mDragObject = new DropTarget.DragObject(mActivity.getApplicationContext());
+        mDragObject.originalView = originalView;
+        mDragObject.deferDragViewCleanupPostAnimation = false;
+
+        mIsInPreDrag = mOptions.preDragCondition != null
+                && !mOptions.preDragCondition.shouldStartDrag(0);
+
+        float scalePx = mDragIconSize - dragRegion.width();
+        final DragView dragView = mDragObject.dragView = new TaskbarDragView(
+                mActivity,
+                drawable,
+                mRegistrationX,
+                mRegistrationY,
+                initialDragViewScale,
+                dragViewScaleOnDrop,
+                scalePx);
+        dragView.setItemInfo(dragInfo);
+        mDragObject.dragComplete = false;
+
+        mDragObject.xOffset = mMotionDown.x - (dragLayerX + dragRegionLeft);
+        mDragObject.yOffset = mMotionDown.y - (dragLayerY + dragRegionTop);
+
+        mDragDriver = DragDriver.create(this, mOptions, /* secondaryEventConsumer = */ ev -> {});
+        if (!mOptions.isAccessibleDrag) {
+            mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
+        }
+
+        mDragObject.dragSource = source;
+        mDragObject.dragInfo = dragInfo;
+        mDragObject.originalDragInfo = mDragObject.dragInfo.makeShallowCopy();
+
+        if (dragRegion != null) {
+            dragView.setDragRegion(new Rect(dragRegion));
+        }
+
+        dragView.show(mLastTouch.x, mLastTouch.y);
+        mDistanceSinceScroll = 0;
+
+        if (!mIsInPreDrag) {
+            callOnDragStart();
+        } else if (mOptions.preDragCondition != null) {
+            mOptions.preDragCondition.onPreDragStart(mDragObject);
+        }
+
+        handleMoveEvent(mLastTouch.x, mLastTouch.y);
+
+        return dragView;
+    }
+
+    @Override
+    protected void callOnDragStart() {
+        super.callOnDragStart();
+        // Pre-drag has ended, start the global system drag.
+        AbstractFloatingView.closeAllOpenViews(mActivity);
+        startSystemDrag((BubbleTextView) mDragObject.originalView);
+    }
+
+    private void startSystemDrag(BubbleTextView btv) {
+        View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(btv) {
+
             @Override
             public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
                 shadowSize.set(mDragIconSize, mDragIconSize);
-                // TODO: should be based on last touch point on the icon.
-                shadowTouchPoint.set(shadowSize.x / 2, shadowSize.y / 2);
+                // The registration point was taken before the icon scaled to mDragIconSize, so
+                // offset the registration to where the touch is on the new size.
+                int offset = (mDragIconSize - btv.getIconSize()) / 2;
+                shadowTouchPoint.set(mRegistrationX + offset, mRegistrationY + offset);
             }
 
             @Override
@@ -81,12 +253,12 @@
             }
         };
 
-        Object tag = view.getTag();
+        Object tag = btv.getTag();
         ClipDescription clipDescription = null;
         Intent intent = null;
         if (tag instanceof WorkspaceItemInfo) {
             WorkspaceItemInfo item = (WorkspaceItemInfo) tag;
-            LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class);
+            LauncherApps launcherApps = mActivity.getSystemService(LauncherApps.class);
             clipDescription = new ClipDescription(item.title,
                     new String[] {
                             item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
@@ -115,29 +287,95 @@
         }
 
         if (clipDescription != null && intent != null) {
+            // Need to share the same InstanceId between launcher3 and WM Shell (internal).
+            InstanceId internalInstanceId = new InstanceIdSequence(
+                    com.android.launcher3.logging.InstanceId.INSTANCE_ID_MAX).newInstanceId();
+            com.android.launcher3.logging.InstanceId launcherInstanceId =
+                    new com.android.launcher3.logging.InstanceId(internalInstanceId.getId());
+
+            intent.putExtra(ClipDescription.EXTRA_LOGGING_INSTANCE_ID, internalInstanceId);
+
             ClipData clipData = new ClipData(clipDescription, new ClipData.Item(intent));
-            view.setOnDragListener(getDraggedViewDragListener());
-            return view.startDragAndDrop(clipData, shadowBuilder, null /* localState */,
-                    View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_OPAQUE);
+            if (btv.startDragAndDrop(clipData, shadowBuilder, null /* localState */,
+                    View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_OPAQUE)) {
+                onSystemDragStarted();
+
+                mActivity.getStatsLogManager().logger().withItemInfo(mDragObject.dragInfo)
+                        .withInstanceId(launcherInstanceId)
+                        .log(StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED);
+            }
         }
-        return false;
     }
 
-    /**
-     * Hide the original Taskbar item while it is being dragged.
-     */
-    private View.OnDragListener getDraggedViewDragListener() {
-        return (view, dragEvent) -> {
+    private void onSystemDragStarted() {
+        mIsSystemDragInProgress = true;
+        mActivity.getDragLayer().setOnDragListener((view, dragEvent) -> {
             switch (dragEvent.getAction()) {
                 case DragEvent.ACTION_DRAG_STARTED:
-                    view.setVisibility(INVISIBLE);
+                    // Return true to tell system we are interested in events, so we get DRAG_ENDED.
                     return true;
                 case DragEvent.ACTION_DRAG_ENDED:
-                    view.setVisibility(VISIBLE);
-                    view.setOnDragListener(null);
+                    mIsSystemDragInProgress = false;
+                    maybeOnDragEnd();
                     return true;
             }
             return false;
-        };
+        });
+    }
+
+    @Override
+    public boolean isDragging() {
+        return super.isDragging() || mIsSystemDragInProgress;
+    }
+
+    private void maybeOnDragEnd() {
+        if (!isDragging()) {
+            ((View) mDragObject.originalView).setVisibility(VISIBLE);
+        }
+    }
+
+    @Override
+    protected void callOnDragEnd() {
+        super.callOnDragEnd();
+        maybeOnDragEnd();
+    }
+
+    @Override
+    protected float getX(MotionEvent ev) {
+        // We will resize to fill the screen while dragging, so use screen coordinates. This ensures
+        // we start at the correct position even though touch down is on the smaller DragLayer size.
+        return ev.getRawX();
+    }
+
+    @Override
+    protected float getY(MotionEvent ev) {
+        // We will resize to fill the screen while dragging, so use screen coordinates. This ensures
+        // we start at the correct position even though touch down is on the smaller DragLayer size.
+        return ev.getRawY();
+    }
+
+    @Override
+    protected Point getClampedDragLayerPos(float x, float y) {
+        // No need to clamp, as we will take up the entire screen.
+        mTmpPoint.set(Math.round(x), Math.round(y));
+        return mTmpPoint;
+    }
+
+    @Override
+    protected void exitDrag() {
+        if (mDragObject != null) {
+            mActivity.getDragLayer().removeView(mDragObject.dragView);
+        }
+    }
+
+    @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.
+    }
+
+    @Override
+    protected DropTarget getDefaultDropTarget(int[] dropCoordinates) {
+        return null;
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index 45ec911..b42a60c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -18,14 +18,17 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Paint;
-import android.graphics.Rect;
+import android.graphics.Path;
 import android.util.AttributeSet;
+import android.view.MotionEvent;
 import android.view.View;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.R;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.systemui.shared.system.ViewTreeObserverWrapper;
@@ -37,14 +40,16 @@
  */
 public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> {
 
-    private final int mFolderMargin;
     private final Paint mTaskbarBackgroundPaint;
-
-    private TaskbarIconController.Callbacks mControllerCallbacks;
-    private TaskbarView mTaskbarView;
-
+    private final Path mInvertedLeftCornerPath, mInvertedRightCornerPath;
     private final OnComputeInsetsListener mTaskbarInsetsComputer = this::onComputeTaskbarInsets;
 
+    // Initialized in init.
+    private TaskbarDragLayerController.TaskbarDragLayerCallbacks mControllerCallbacks;
+    private float mLeftCornerRadius, mRightCornerRadius;
+
+    private float mTaskbarBackgroundOffset;
+
     public TaskbarDragLayer(@NonNull Context context) {
         this(context, null);
     }
@@ -61,25 +66,47 @@
     public TaskbarDragLayer(@NonNull Context context, @Nullable AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
         super(context, attrs, 1 /* alphaChannelCount */);
-        mFolderMargin = getResources().getDimensionPixelSize(R.dimen.taskbar_folder_margin);
         mTaskbarBackgroundPaint = new Paint();
         mTaskbarBackgroundPaint.setColor(getResources().getColor(R.color.taskbar_background));
+        mTaskbarBackgroundPaint.setAlpha(0);
+        mTaskbarBackgroundPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
+        mTaskbarBackgroundPaint.setStyle(Paint.Style.FILL);
+
+        // Will be set in init(), but this ensures they are always non-null.
+        mInvertedLeftCornerPath = new Path();
+        mInvertedRightCornerPath = new Path();
+    }
+
+    public void init(TaskbarDragLayerController.TaskbarDragLayerCallbacks callbacks) {
+        mControllerCallbacks = callbacks;
+
+        // Create the paths for the inverted rounded corners above the taskbar. Start with a filled
+        // square, and then subtracting out a circle from the appropriate corner.
+        mLeftCornerRadius = mActivity.getLeftCornerRadius();
+        mRightCornerRadius = mActivity.getRightCornerRadius();
+        Path square = new Path();
+        square.addRect(0, 0, mLeftCornerRadius, mLeftCornerRadius, Path.Direction.CW);
+        Path circle = new Path();
+        circle.addCircle(mLeftCornerRadius, 0, mLeftCornerRadius, Path.Direction.CW);
+        mInvertedLeftCornerPath.op(square, circle, Path.Op.DIFFERENCE);
+        square.reset();
+        square.addRect(0, 0, mRightCornerRadius, mRightCornerRadius, Path.Direction.CW);
+        circle.reset();
+        circle.addCircle(0, 0, mRightCornerRadius, Path.Direction.CW);
+        mInvertedRightCornerPath.op(square, circle, Path.Op.DIFFERENCE);
+
         recreateControllers();
     }
 
     @Override
     public void recreateControllers() {
-        mControllers = new TouchController[0];
-    }
-
-    public void init(TaskbarIconController.Callbacks callbacks, TaskbarView taskbarView) {
-        mControllerCallbacks = callbacks;
-        mTaskbarView = taskbarView;
+        mControllers = new TouchController[] {mActivity.getDragController()};
     }
 
     private void onComputeTaskbarInsets(InsetsInfo insetsInfo) {
         if (mControllerCallbacks != null) {
             mControllerCallbacks.updateInsetsTouchability(insetsInfo);
+            mControllerCallbacks.updateContentInsets(insetsInfo.contentInsets);
         }
     }
 
@@ -108,12 +135,6 @@
         return true;
     }
 
-    public void updateImeBarVisibilityAlpha(float alpha) {
-        if (mControllerCallbacks != null) {
-            mControllerCallbacks.updateImeBarVisibilityAlpha(alpha);
-        }
-    }
-
     @Override
     public void onViewRemoved(View child) {
         super.onViewRemoved(child);
@@ -124,22 +145,26 @@
 
     @Override
     protected void dispatchDraw(Canvas canvas) {
-        canvas.drawRect(0, canvas.getHeight() - mTaskbarView.getHeight(), canvas.getWidth(),
-                canvas.getHeight(), mTaskbarBackgroundPaint);
+        float backgroundHeight = mControllerCallbacks.getTaskbarBackgroundHeight()
+                * (1f - mTaskbarBackgroundOffset);
+        canvas.save();
+        canvas.translate(0, canvas.getHeight() - backgroundHeight);
+
+        // Draw the background behind taskbar content.
+        canvas.drawRect(0, 0, canvas.getWidth(), backgroundHeight, mTaskbarBackgroundPaint);
+
+        // Draw the inverted rounded corners above the taskbar.
+        canvas.translate(0, -mLeftCornerRadius);
+        canvas.drawPath(mInvertedLeftCornerPath, mTaskbarBackgroundPaint);
+        canvas.translate(0, mLeftCornerRadius);
+        canvas.translate(canvas.getWidth() - mRightCornerRadius, -mRightCornerRadius);
+        canvas.drawPath(mInvertedRightCornerPath, mTaskbarBackgroundPaint);
+
+        canvas.restore();
         super.dispatchDraw(canvas);
     }
 
     /**
-     * @return Bounds (in our coordinates) where an opened Folder can display.
-     */
-    protected Rect getFolderBoundingBox() {
-        Rect boundingBox = new Rect(0, 0, getWidth(), getHeight() - mTaskbarView.getHeight());
-        boundingBox.inset(mFolderMargin, mFolderMargin);
-        return boundingBox;
-    }
-
-
-    /**
      * Sets the alpha of the background color behind all the Taskbar contents.
      * @param alpha 0 is fully transparent, 1 is fully opaque.
      */
@@ -147,4 +172,19 @@
         mTaskbarBackgroundPaint.setAlpha((int) (alpha * 255));
         invalidate();
     }
+
+    /**
+     * Sets the translation of the background color behind all the Taskbar contents.
+     * @param offset 0 is fully onscreen, 1 is fully offscreen.
+     */
+    protected void setTaskbarBackgroundOffset(float offset) {
+        mTaskbarBackgroundOffset = offset;
+        invalidate();
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
+        return super.dispatchTouchEvent(ev);
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
new file mode 100644
index 0000000..05b0a11
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -0,0 +1,174 @@
+/*
+ * 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.taskbar;
+
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_CONTENT;
+import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_FRAME;
+import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
+
+import android.content.res.Resources;
+import android.graphics.Rect;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.AlphaUpdateListener;
+import com.android.quickstep.AnimatedFloat;
+import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo;
+
+/**
+ * Handles properties/data collection, then passes the results to TaskbarDragLayer to render.
+ */
+public class TaskbarDragLayerController {
+
+    private final TaskbarActivityContext mActivity;
+    private final TaskbarDragLayer mTaskbarDragLayer;
+    private final int mFolderMargin;
+
+    // Alpha properties for taskbar background.
+    private final AnimatedFloat mBgTaskbar = new AnimatedFloat(this::updateBackgroundAlpha);
+    private final AnimatedFloat mBgNavbar = new AnimatedFloat(this::updateBackgroundAlpha);
+    private final AnimatedFloat mKeyguardBgTaskbar = new AnimatedFloat(this::updateBackgroundAlpha);
+    // Used to hide our background color when someone else (e.g. ScrimView) is handling it.
+    private final AnimatedFloat mBgOverride = new AnimatedFloat(this::updateBackgroundAlpha);
+
+    // Translation property for taskbar background.
+    private final AnimatedFloat mBgOffset = new AnimatedFloat(this::updateBackgroundOffset);
+
+    // Initialized in init.
+    private TaskbarControllers mControllers;
+
+    public TaskbarDragLayerController(TaskbarActivityContext activity,
+            TaskbarDragLayer taskbarDragLayer) {
+        mActivity = activity;
+        mTaskbarDragLayer = taskbarDragLayer;
+        final Resources resources = mTaskbarDragLayer.getResources();
+        mFolderMargin = resources.getDimensionPixelSize(R.dimen.taskbar_folder_margin);
+    }
+
+    public void init(TaskbarControllers controllers) {
+        mControllers = controllers;
+        mTaskbarDragLayer.init(new TaskbarDragLayerCallbacks());
+
+        mBgTaskbar.value = 1;
+        mKeyguardBgTaskbar.value = 1;
+        mBgOverride.value = 1;
+        updateBackgroundAlpha();
+    }
+
+    public void onDestroy() {
+        mTaskbarDragLayer.onDestroy();
+    }
+
+    /**
+     * @return Bounds (in TaskbarDragLayer coordinates) where an opened Folder can display.
+     */
+    public Rect getFolderBoundingBox() {
+        Rect boundingBox = new Rect(0, 0, mTaskbarDragLayer.getWidth(),
+                mTaskbarDragLayer.getHeight() - mActivity.getDeviceProfile().taskbarSize);
+        boundingBox.inset(mFolderMargin, mFolderMargin);
+        return boundingBox;
+    }
+
+    public AnimatedFloat getTaskbarBackgroundAlpha() {
+        return mBgTaskbar;
+    }
+
+    public AnimatedFloat getNavbarBackgroundAlpha() {
+        return mBgNavbar;
+    }
+
+    public AnimatedFloat getKeyguardBgTaskbar() {
+        return mKeyguardBgTaskbar;
+    }
+
+    public AnimatedFloat getOverrideBackgroundAlpha() {
+        return mBgOverride;
+    }
+
+    public AnimatedFloat getTaskbarBackgroundOffset() {
+        return mBgOffset;
+    }
+
+    private void updateBackgroundAlpha() {
+        final float bgNavbar = mBgNavbar.value;
+        final float bgTaskbar = mBgTaskbar.value * mKeyguardBgTaskbar.value;
+        mTaskbarDragLayer.setTaskbarBackgroundAlpha(
+                mBgOverride.value * Math.max(bgNavbar, bgTaskbar)
+        );
+    }
+
+    private void updateBackgroundOffset() {
+        mTaskbarDragLayer.setTaskbarBackgroundOffset(mBgOffset.value);
+    }
+
+    /**
+     * Callbacks for {@link TaskbarDragLayer} to interact with its controller.
+     */
+    public class TaskbarDragLayerCallbacks {
+
+        /**
+         * Called to update the touchable insets.
+         * @see InsetsInfo#setTouchableInsets(int)
+         */
+        public void updateInsetsTouchability(InsetsInfo insetsInfo) {
+            insetsInfo.touchableRegion.setEmpty();
+            // Always have nav buttons be touchable
+            mControllers.navbarButtonsViewController.addVisibleButtonsRegion(
+                    mTaskbarDragLayer, insetsInfo.touchableRegion);
+
+            if (mTaskbarDragLayer.getAlpha() < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) {
+                // Let touches pass through us.
+                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
+            } else if (mControllers.navbarButtonsViewController.isImeVisible()) {
+                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_CONTENT);
+            } else if (!mControllers.uiController.isTaskbarTouchable()) {
+                // Let touches pass through us.
+                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
+            } else if (mControllers.taskbarViewController.areIconsVisible()
+                    || AbstractFloatingView.getOpenView(mActivity, TYPE_ALL) != null) {
+                // Taskbar has some touchable elements, take over the full taskbar area
+                insetsInfo.setTouchableInsets(mActivity.isTaskbarWindowFullscreen()
+                        ? TOUCHABLE_INSETS_FRAME : TOUCHABLE_INSETS_CONTENT);
+            } else {
+                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
+            }
+        }
+
+        /**
+         * Called to update the {@link InsetsInfo#contentInsets}.
+         */
+        public void updateContentInsets(Rect outContentInsets) {
+            mControllers.uiController.updateContentInsets(outContentInsets);
+        }
+
+        /**
+         * Called when a child is removed from TaskbarDragLayer.
+         */
+        public void onDragLayerViewRemoved() {
+            if (AbstractFloatingView.getAnyView(mActivity, TYPE_ALL) == null) {
+                mActivity.setTaskbarWindowFullscreen(false);
+            }
+        }
+
+        /**
+         * Returns how tall the background should be drawn at the bottom of the screen.
+         */
+        public int getTaskbarBackgroundHeight() {
+            return mActivity.getDeviceProfile().taskbarSize;
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragView.java
new file mode 100644
index 0000000..cf28eff
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragView.java
@@ -0,0 +1,56 @@
+/*
+ * 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.taskbar;
+
+import android.graphics.drawable.Drawable;
+
+import com.android.launcher3.R;
+import com.android.launcher3.dragndrop.DragView;
+
+/**
+ * A DragView drawn/used by the Taskbar. Note that this is only for the internal drag-and-drop,
+ * while the pre-drag is still in progress (i.e. when the long press popup is still open). After
+ * that ends, we switch to a system drag and drop view instead.
+ */
+public class TaskbarDragView extends DragView<TaskbarActivityContext> {
+    public TaskbarDragView(TaskbarActivityContext launcher, Drawable drawable, int registrationX,
+            int registrationY, float initialScale, float scaleOnDrop, float finalScaleDps) {
+        super(launcher, drawable, registrationX, registrationY, initialScale, scaleOnDrop,
+                finalScaleDps);
+    }
+
+    @Override
+    public void animateTo(int toTouchX, int toTouchY, Runnable onCompleteRunnable, int duration) {
+        Runnable onAnimationEnd = () -> {
+            if (onCompleteRunnable != null) {
+                onCompleteRunnable.run();
+            }
+            mActivity.getDragLayer().removeView(this);
+        };
+
+        duration = Math.max(duration,
+                getResources().getInteger(R.integer.config_dropAnimMinDuration));
+
+        animate()
+                .translationX(toTouchX - mRegistrationX)
+                .translationY(toTouchY - mRegistrationY)
+                .scaleX(mScaleOnDrop)
+                .scaleY(mScaleOnDrop)
+                .withEndAction(onAnimationEnd)
+                .setDuration(duration)
+                .start();
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
new file mode 100644
index 0000000..fd5c2ea
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
@@ -0,0 +1,210 @@
+/*
+ * 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.taskbar;
+
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.Keyframe;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.TimeInterpolator;
+import android.content.res.Resources;
+import android.text.TextUtils;
+import android.view.View;
+
+import com.android.launcher3.R;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.uioverrides.PredictedAppIcon;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/** Handles the Taskbar Education flow. */
+public class TaskbarEduController {
+
+    private static final long WAVE_ANIM_DELAY = 250;
+    private static final long WAVE_ANIM_STAGGER = 50;
+    private static final long WAVE_ANIM_EACH_ICON_DURATION = 633;
+    private static final long WAVE_ANIM_SLOT_MACHINE_DURATION = 1085;
+    // The fraction of each icon's animation at which we reach the top point of the wave.
+    private static final float WAVE_ANIM_FRACTION_TOP = 0.4f;
+    // The fraction of each icon's animation at which we reach the bottom, before overshooting.
+    private static final float WAVE_ANIM_FRACTION_BOTTOM = 0.9f;
+    private static final TimeInterpolator WAVE_ANIM_TO_TOP_INTERPOLATOR = FAST_OUT_SLOW_IN;
+    private static final TimeInterpolator WAVE_ANIM_TO_BOTTOM_INTERPOLATOR = ACCEL_2;
+    private static final TimeInterpolator WAVE_ANIM_OVERSHOOT_INTERPOLATOR = DEACCEL;
+    private static final TimeInterpolator WAVE_ANIM_OVERSHOOT_RETURN_INTERPOLATOR = ACCEL_DEACCEL;
+    private static final float WAVE_ANIM_ICON_SCALE = 1.2f;
+    // How many icons to cycle through in the slot machine (+ the original icon at each end).
+    private static final int WAVE_ANIM_SLOT_MACHINE_NUM_ICONS = 3;
+
+    private final TaskbarActivityContext mActivity;
+    private final float mWaveAnimTranslationY;
+    private final float mWaveAnimTranslationYReturnOvershoot;
+
+    // Initialized in init.
+    TaskbarControllers mControllers;
+
+    private TaskbarEduView mTaskbarEduView;
+    private Animator mAnim;
+
+    public TaskbarEduController(TaskbarActivityContext activity) {
+        mActivity = activity;
+
+        final Resources resources = activity.getResources();
+        mWaveAnimTranslationY = resources.getDimension(R.dimen.taskbar_edu_wave_anim_trans_y);
+        mWaveAnimTranslationYReturnOvershoot = resources.getDimension(
+                R.dimen.taskbar_edu_wave_anim_trans_y_return_overshoot);
+    }
+
+    public void init(TaskbarControllers controllers) {
+        mControllers = controllers;
+    }
+
+    void showEdu() {
+        mActivity.setTaskbarWindowFullscreen(true);
+        mActivity.getDragLayer().post(() -> {
+            mTaskbarEduView = (TaskbarEduView) mActivity.getLayoutInflater().inflate(
+                    R.layout.taskbar_edu, mActivity.getDragLayer(), false);
+            mTaskbarEduView.init(new TaskbarEduCallbacks());
+            mTaskbarEduView.addOnCloseListener(() -> mTaskbarEduView = null);
+            mTaskbarEduView.show();
+            startAnim(createWaveAnim());
+        });
+    }
+
+    void hideEdu() {
+        if (mTaskbarEduView != null) {
+            mTaskbarEduView.close(true /* animate */);
+        }
+    }
+
+    /**
+     * Starts the given animation, ending the previous animation first if it's still playing.
+     */
+    private void startAnim(Animator anim) {
+        if (mAnim != null) {
+            mAnim.end();
+        }
+        mAnim = anim;
+        mAnim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mAnim = null;
+            }
+        });
+        mAnim.start();
+    }
+
+    /**
+     * Creates a staggered "wave" animation where each icon translates and scales up in succession.
+     */
+    private Animator createWaveAnim() {
+        AnimatorSet waveAnim = new AnimatorSet();
+        View[] icons = mControllers.taskbarViewController.getIconViews();
+        for (int i = 0; i < icons.length; i++) {
+            View icon = icons[i];
+            AnimatorSet iconAnim = new AnimatorSet();
+
+            Keyframe[] scaleKeyframes = new Keyframe[] {
+                    Keyframe.ofFloat(0, 1f),
+                    Keyframe.ofFloat(WAVE_ANIM_FRACTION_TOP, WAVE_ANIM_ICON_SCALE),
+                    Keyframe.ofFloat(WAVE_ANIM_FRACTION_BOTTOM, 1f),
+                    Keyframe.ofFloat(1f, 1f)
+            };
+            scaleKeyframes[1].setInterpolator(WAVE_ANIM_TO_TOP_INTERPOLATOR);
+            scaleKeyframes[2].setInterpolator(WAVE_ANIM_TO_BOTTOM_INTERPOLATOR);
+
+            Keyframe[] translationYKeyframes = new Keyframe[] {
+                    Keyframe.ofFloat(0, 0f),
+                    Keyframe.ofFloat(WAVE_ANIM_FRACTION_TOP, -mWaveAnimTranslationY),
+                    Keyframe.ofFloat(WAVE_ANIM_FRACTION_BOTTOM, 0f),
+                    // Half of the remaining fraction overshoots, then the other half returns to 0.
+                    Keyframe.ofFloat(
+                            WAVE_ANIM_FRACTION_BOTTOM + (1 - WAVE_ANIM_FRACTION_BOTTOM) / 2f,
+                            mWaveAnimTranslationYReturnOvershoot),
+                    Keyframe.ofFloat(1f, 0f)
+            };
+            translationYKeyframes[1].setInterpolator(WAVE_ANIM_TO_TOP_INTERPOLATOR);
+            translationYKeyframes[2].setInterpolator(WAVE_ANIM_TO_BOTTOM_INTERPOLATOR);
+            translationYKeyframes[3].setInterpolator(WAVE_ANIM_OVERSHOOT_INTERPOLATOR);
+            translationYKeyframes[4].setInterpolator(WAVE_ANIM_OVERSHOOT_RETURN_INTERPOLATOR);
+
+            iconAnim.play(ObjectAnimator.ofPropertyValuesHolder(icon,
+                    PropertyValuesHolder.ofKeyframe(SCALE_PROPERTY, scaleKeyframes))
+                    .setDuration(WAVE_ANIM_EACH_ICON_DURATION));
+            iconAnim.play(ObjectAnimator.ofPropertyValuesHolder(icon,
+                    PropertyValuesHolder.ofKeyframe(View.TRANSLATION_Y, translationYKeyframes))
+                    .setDuration(WAVE_ANIM_EACH_ICON_DURATION));
+
+            if (icon instanceof PredictedAppIcon) {
+                // Play slot machine animation through random icons from AllAppsList.
+                PredictedAppIcon predictedAppIcon = (PredictedAppIcon) icon;
+                ItemInfo itemInfo = (ItemInfo) icon.getTag();
+                List<BitmapInfo> iconsToAnimate = mControllers.uiController.getAppIconsForEdu()
+                        .filter(appInfo -> !TextUtils.equals(appInfo.title, itemInfo.title))
+                        .map(appInfo -> appInfo.bitmap)
+                        .filter(bitmap -> !bitmap.isNullOrLowRes())
+                        .collect(Collectors.toList());
+                // Pick n icons at random.
+                Collections.shuffle(iconsToAnimate);
+                if (iconsToAnimate.size() > WAVE_ANIM_SLOT_MACHINE_NUM_ICONS) {
+                    iconsToAnimate = iconsToAnimate.subList(0, WAVE_ANIM_SLOT_MACHINE_NUM_ICONS);
+                }
+                Animator slotMachineAnim = predictedAppIcon.createSlotMachineAnim(iconsToAnimate);
+                if (slotMachineAnim != null) {
+                    iconAnim.play(slotMachineAnim.setDuration(WAVE_ANIM_SLOT_MACHINE_DURATION));
+                }
+            }
+
+            iconAnim.setStartDelay(WAVE_ANIM_STAGGER * i);
+            waveAnim.play(iconAnim);
+        }
+        waveAnim.setStartDelay(WAVE_ANIM_DELAY);
+        return waveAnim;
+    }
+
+    /**
+     * Callbacks for {@link TaskbarEduView} to interact with its controller.
+     */
+    class TaskbarEduCallbacks {
+        void onPageChanged(int currentPage, int pageCount) {
+            if (currentPage == 0) {
+                mTaskbarEduView.updateStartButton(R.string.taskbar_edu_close,
+                        v -> mTaskbarEduView.close(true /* animate */));
+            } else {
+                mTaskbarEduView.updateStartButton(R.string.taskbar_edu_previous,
+                        v -> mTaskbarEduView.snapToPage(currentPage - 1));
+            }
+            if (currentPage == pageCount - 1) {
+                mTaskbarEduView.updateEndButton(R.string.taskbar_edu_done,
+                        v -> mTaskbarEduView.close(true /* animate */));
+            } else {
+                mTaskbarEduView.updateEndButton(R.string.taskbar_edu_next,
+                        v -> mTaskbarEduView.snapToPage(currentPage + 1));
+            }
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java
new file mode 100644
index 0000000..5efcc4d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java
@@ -0,0 +1,79 @@
+/*
+ * 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.taskbar;
+
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_EDUCATION_DIALOG;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.PagedView;
+import com.android.launcher3.R;
+import com.android.launcher3.pageindicators.PageIndicatorDots;
+import com.android.launcher3.taskbar.TaskbarEduController.TaskbarEduCallbacks;
+import com.android.launcher3.views.ActivityContext;
+
+/** Horizontal carousel of tutorial screens for Taskbar Edu. */
+public class TaskbarEduPagedView extends PagedView<PageIndicatorDots> {
+
+    private TaskbarEduView mTaskbarEduView;
+    private TaskbarEduCallbacks mControllerCallbacks;
+
+    public TaskbarEduPagedView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+    }
+
+    void setTaskbarEduView(TaskbarEduView taskbarEduView) {
+        mTaskbarEduView = taskbarEduView;
+        mPageIndicator = taskbarEduView.findViewById(R.id.content_page_indicator);
+        initParentViews(taskbarEduView);
+    }
+
+    void setControllerCallbacks(TaskbarEduCallbacks controllerCallbacks) {
+        mControllerCallbacks = controllerCallbacks;
+        mControllerCallbacks.onPageChanged(getCurrentPage(), getPageCount());
+    }
+
+    @Override
+    protected int getChildGap() {
+        return mTaskbarEduView.getPaddingLeft() + mTaskbarEduView.getPaddingRight();
+    }
+
+    @Override
+    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        super.onScrollChanged(l, t, oldl, oldt);
+        if (mMaxScroll > 0) {
+            mPageIndicator.setScroll(l, mMaxScroll);
+        }
+    }
+
+    @Override
+    protected void notifyPageSwitchListener(int prevPage) {
+        super.notifyPageSwitchListener(prevPage);
+        mControllerCallbacks.onPageChanged(getCurrentPage(), getPageCount());
+    }
+
+    @Override
+    protected boolean canScroll(float absVScroll, float absHScroll) {
+        return AbstractFloatingView.getTopOpenViewWithType(
+                ActivityContext.lookupContext(getContext()),
+                TYPE_ALL & ~TYPE_TASKBAR_EDUCATION_DIALOG) == null;
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
new file mode 100644
index 0000000..8525427
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
@@ -0,0 +1,150 @@
+/*
+ * 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.taskbar;
+
+import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
+
+import android.animation.PropertyValuesHolder;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.Pair;
+import android.view.View;
+import android.widget.Button;
+
+import com.android.launcher3.Insettable;
+import com.android.launcher3.R;
+import com.android.launcher3.views.AbstractSlideInView;
+
+/** Education view about the Taskbar. */
+public class TaskbarEduView extends AbstractSlideInView<TaskbarActivityContext>
+        implements Insettable {
+
+    private static final int DEFAULT_OPEN_DURATION = 500;
+    private static final int DEFAULT_CLOSE_DURATION = 200;
+
+    private final Rect mInsets = new Rect();
+
+    private Button mStartButton;
+    private Button mEndButton;
+    private TaskbarEduPagedView mPagedView;
+
+    public TaskbarEduView(Context context, AttributeSet attr) {
+        this(context, attr, 0);
+    }
+
+    public TaskbarEduView(Context context, AttributeSet attrs,
+            int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    protected void init(TaskbarEduController.TaskbarEduCallbacks callbacks) {
+        if (mPagedView != null) {
+            mPagedView.setControllerCallbacks(callbacks);
+        }
+    }
+
+    @Override
+    protected void handleClose(boolean animate) {
+        handleClose(animate, DEFAULT_CLOSE_DURATION);
+    }
+
+    @Override
+    protected boolean isOfType(int type) {
+        return (type & TYPE_TASKBAR_EDUCATION_DIALOG) != 0;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mContent = findViewById(R.id.edu_view);
+        mStartButton = findViewById(R.id.edu_start_button);
+        mEndButton = findViewById(R.id.edu_end_button);
+        mPagedView = findViewById(R.id.content);
+        mPagedView.setTaskbarEduView(this);
+    }
+
+    @Override
+    public void setInsets(Rect insets) {
+        mInsets.set(insets);
+        mContent.setPadding(mContent.getPaddingStart(),
+                mContent.getPaddingTop(), mContent.getPaddingEnd(), insets.bottom);
+    }
+
+    @Override
+    protected void attachToContainer() {
+        if (mColorScrim != null) {
+            getPopupContainer().addView(mColorScrim, 0);
+        }
+        getPopupContainer().addView(this, 1);
+    }
+
+    /** Show the Education flow. */
+    public void show() {
+        attachToContainer();
+        animateOpen();
+    }
+
+    @Override
+    protected Pair<View, String> getAccessibilityTarget() {
+        return Pair.create(mContent, mIsOpen ? getContext().getString(R.string.taskbar_edu_opened)
+                : getContext().getString(R.string.taskbar_edu_closed));
+    }
+
+    @Override
+    protected int getScrimColor(Context context) {
+        return context.getResources().getColor(R.color.widgets_picker_scrim);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        int width = r - l;
+        int height = b - t;
+
+        // Lay out the content as center bottom aligned.
+        int contentWidth = mContent.getMeasuredWidth();
+        int contentLeft = (width - contentWidth - mInsets.left - mInsets.right) / 2 + mInsets.left;
+        mContent.layout(contentLeft, height - mContent.getMeasuredHeight(),
+                contentLeft + contentWidth, height);
+
+        setTranslationShift(mTranslationShift);
+    }
+
+    private void animateOpen() {
+        if (mIsOpen || mOpenCloseAnimator.isRunning()) {
+            return;
+        }
+        mIsOpen = true;
+        mOpenCloseAnimator.setValues(
+                PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
+        mOpenCloseAnimator.setInterpolator(AGGRESSIVE_EASE);
+        mOpenCloseAnimator.setDuration(DEFAULT_OPEN_DURATION).start();
+    }
+
+    void snapToPage(int page) {
+        mPagedView.snapToPage(page);
+    }
+
+    void updateStartButton(int textResId, OnClickListener onClickListener) {
+        mStartButton.setText(textResId);
+        mStartButton.setOnClickListener(onClickListener);
+    }
+
+    void updateEndButton(int textResId, OnClickListener onClickListener) {
+        mEndButton.setText(textResId);
+        mEndButton.setOnClickListener(onClickListener);
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
deleted file mode 100644
index 91cf7ef..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
+++ /dev/null
@@ -1,91 +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.taskbar;
-
-import android.view.View;
-
-import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.CellLayout;
-import com.android.launcher3.DropTarget;
-import com.android.launcher3.Hotseat;
-import com.android.launcher3.ShortcutAndWidgetContainer;
-import com.android.launcher3.dragndrop.DragController;
-import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.model.data.ItemInfo;
-
-import java.util.function.Consumer;
-
-/**
- * Works with TaskbarController to update the TaskbarView's Hotseat items.
- */
-public class TaskbarHotseatController {
-
-    private final BaseQuickstepLauncher mLauncher;
-    private final Hotseat mHotseat;
-    private final Consumer<ItemInfo[]> mTaskbarCallbacks;
-    private final int mNumHotseatIcons;
-
-    private final DragController.DragListener mDragListener = new DragController.DragListener() {
-        @Override
-        public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { }
-
-        @Override
-        public void onDragEnd() {
-            onHotseatUpdated();
-        }
-    };
-
-    public TaskbarHotseatController(
-            BaseQuickstepLauncher launcher, Consumer<ItemInfo[]> taskbarCallbacks) {
-        mLauncher = launcher;
-        mHotseat = mLauncher.getHotseat();
-        mTaskbarCallbacks = taskbarCallbacks;
-        mNumHotseatIcons = mLauncher.getDeviceProfile().numShownHotseatIcons;
-    }
-
-    protected void init() {
-        mLauncher.getDragController().addDragListener(mDragListener);
-        onHotseatUpdated();
-    }
-
-    protected void cleanup() {
-        mLauncher.getDragController().removeDragListener(mDragListener);
-    }
-
-    /**
-     * Called when any Hotseat item changes, and reports the new list of items to TaskbarController.
-     */
-    protected void onHotseatUpdated() {
-        ShortcutAndWidgetContainer shortcutsAndWidgets = mHotseat.getShortcutsAndWidgets();
-        ItemInfo[] hotseatItemInfos = new ItemInfo[mNumHotseatIcons];
-        for (int i = 0; i < shortcutsAndWidgets.getChildCount(); i++) {
-            View child = shortcutsAndWidgets.getChildAt(i);
-            Object tag = shortcutsAndWidgets.getChildAt(i).getTag();
-            if (tag instanceof ItemInfo) {
-                ItemInfo itemInfo = (ItemInfo) tag;
-                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
-                // Since the hotseat might be laid out vertically or horizontally, use whichever
-                // index is higher.
-                int index = Math.max(lp.cellX, lp.cellY);
-                if (0 <= index && index < hotseatItemInfos.length) {
-                    hotseatItemInfos[index] = itemInfo;
-                }
-            }
-        }
-
-        mTaskbarCallbacks.accept(hotseatItemInfos);
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java
deleted file mode 100644
index 683a5b9..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java
+++ /dev/null
@@ -1,163 +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.taskbar;
-
-import static android.view.View.GONE;
-import static android.view.View.VISIBLE;
-
-import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_FRAME;
-import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
-
-import android.graphics.Rect;
-import android.inputmethodservice.InputMethodService;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnLongClickListener;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.R;
-import com.android.launcher3.anim.AlphaUpdateListener;
-import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo;
-
-/**
- * Controller for taskbar icon UI
- */
-public class TaskbarIconController {
-
-    private final Rect mTempRect = new Rect();
-
-    private final TaskbarActivityContext mActivity;
-    private final TaskbarDragLayer mDragLayer;
-
-    private final TaskbarView mTaskbarView;
-    private final ImeBarView mImeBarView;
-
-    @NonNull
-    private TaskbarUIController mUIController = TaskbarUIController.DEFAULT;
-
-    TaskbarIconController(TaskbarActivityContext activity, TaskbarDragLayer dragLayer) {
-        mActivity = activity;
-        mDragLayer = dragLayer;
-        mTaskbarView = mDragLayer.findViewById(R.id.taskbar_view);
-        mImeBarView = mDragLayer.findViewById(R.id.ime_bar_view);
-    }
-
-    public void init(OnClickListener clickListener, OnLongClickListener longClickListener) {
-        mDragLayer.addOnLayoutChangeListener((v, a, b, c, d, e, f, g, h) ->
-                mUIController.alignRealHotseatWithTaskbar());
-
-        ButtonProvider buttonProvider = new ButtonProvider(mActivity);
-        mImeBarView.init(buttonProvider);
-        mTaskbarView.construct(clickListener, longClickListener, buttonProvider);
-        mTaskbarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarSize;
-
-        mDragLayer.init(new Callbacks(), mTaskbarView);
-    }
-
-    public void onDestroy() {
-        mDragLayer.onDestroy();
-    }
-
-    public void setUIController(@NonNull TaskbarUIController uiController) {
-        mUIController = uiController;
-    }
-
-    /**
-     * When in 3 button nav, the above doesn't get called since we prevent sysui nav bar from
-     * instantiating at all, which is what's responsible for sending sysui state flags over.
-     *
-     * @param vis IME visibility flag
-     */
-    public void updateImeStatus(int displayId, int vis, boolean showImeSwitcher) {
-        if (displayId != mActivity.getDisplayId() || !mActivity.canShowNavButtons()) {
-            return;
-        }
-
-        mImeBarView.setImeSwitcherVisibility(showImeSwitcher);
-        setImeIsVisible((vis & InputMethodService.IME_VISIBLE) != 0);
-    }
-
-    /**
-     * Should be called when the IME visibility changes, so we can hide/show Taskbar accordingly.
-     */
-    public void setImeIsVisible(boolean isImeVisible) {
-        mTaskbarView.setTouchesEnabled(!isImeVisible);
-        mUIController.onImeVisible(mDragLayer, isImeVisible);
-    }
-
-    /**
-     * Callbacks for {@link TaskbarDragLayer} to interact with the icon controller
-     */
-    public class Callbacks {
-
-        /**
-         * Called to update the touchable insets
-         */
-        public void updateInsetsTouchability(InsetsInfo insetsInfo) {
-            insetsInfo.touchableRegion.setEmpty();
-            if (mDragLayer.getAlpha() < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) {
-                // Let touches pass through us.
-                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
-            } else if (mImeBarView.getVisibility() == VISIBLE) {
-                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME);
-            } else if (!mUIController.isTaskbarTouchable()) {
-                // Let touches pass through us.
-                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
-            } else if (mTaskbarView.areIconsVisible()) {
-                // Buttons are visible, take over the full taskbar area
-                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME);
-            } else {
-                if (mTaskbarView.mSystemButtonContainer.getVisibility() == VISIBLE) {
-                    mDragLayer.getDescendantRectRelativeToSelf(
-                            mTaskbarView.mSystemButtonContainer, mTempRect);
-                    insetsInfo.touchableRegion.set(mTempRect);
-                }
-                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
-            }
-
-            // TaskbarContainerView provides insets to other apps based on contentInsets. These
-            // insets should stay consistent even if we expand TaskbarContainerView's bounds, e.g.
-            // to show a floating view like Folder. Thus, we set the contentInsets to be where
-            // mTaskbarView is, since its position never changes and insets rather than overlays.
-            insetsInfo.contentInsets.left = mTaskbarView.getLeft();
-            insetsInfo.contentInsets.top = mTaskbarView.getTop();
-            insetsInfo.contentInsets.right = mDragLayer.getWidth() - mTaskbarView.getRight();
-            insetsInfo.contentInsets.bottom = mDragLayer.getHeight() - mTaskbarView.getBottom();
-        }
-
-        public void onDragLayerViewRemoved() {
-            int count = mDragLayer.getChildCount();
-            // Ensure no other children present (like Folders, etc)
-            for (int i = 0; i < count; i++) {
-                View v = mDragLayer.getChildAt(i);
-                if (!((v instanceof TaskbarView) || (v instanceof ImeBarView))) {
-                    return;
-                }
-            }
-            mActivity.setTaskbarWindowFullscreen(false);
-        }
-
-        public void updateImeBarVisibilityAlpha(float alpha) {
-            if (!mActivity.canShowNavButtons()) {
-                // TODO Remove sysui IME bar for gesture nav as well
-                return;
-            }
-            mImeBarView.setAlpha(alpha);
-            mImeBarView.setVisibility(alpha == 0 ? GONE : VISIBLE);
-        }
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
new file mode 100644
index 0000000..5fc0695
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
@@ -0,0 +1,98 @@
+package com.android.launcher3.taskbar;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
+
+import android.app.KeyguardManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+/**
+ * Controller for managing keyguard state for taskbar
+ */
+public class TaskbarKeyguardController {
+
+    private static final int KEYGUARD_SYSUI_FLAGS = SYSUI_STATE_BOUNCER_SHOWING |
+            SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING | SYSUI_STATE_DEVICE_DOZING |
+            SYSUI_STATE_OVERVIEW_DISABLED | SYSUI_STATE_HOME_DISABLED |
+            SYSUI_STATE_BACK_DISABLED | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
+
+    private final TaskbarActivityContext mContext;
+    private int mKeyguardSysuiFlags;
+    private boolean mBouncerShowing;
+    private NavbarButtonsViewController mNavbarButtonsViewController;
+    private final KeyguardManager mKeyguardManager;
+    private boolean mIsScreenOff;
+
+    private final BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mIsScreenOff = true;
+        }
+    };
+
+    public TaskbarKeyguardController(TaskbarActivityContext context) {
+        mContext = context;
+        mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
+    }
+
+    public void init(NavbarButtonsViewController navbarButtonUIController) {
+        mNavbarButtonsViewController = navbarButtonUIController;
+        mContext.registerReceiver(mScreenOffReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
+    }
+
+    public void updateStateForSysuiFlags(int systemUiStateFlags) {
+        boolean bouncerShowing = (systemUiStateFlags & SYSUI_STATE_BOUNCER_SHOWING) != 0;
+        boolean keyguardShowing = (systemUiStateFlags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING)
+                != 0;
+        boolean keyguardOccluded =
+                (systemUiStateFlags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0;
+        boolean dozing = (systemUiStateFlags & SYSUI_STATE_DEVICE_DOZING) != 0;
+
+        int interestingKeyguardFlags = systemUiStateFlags & KEYGUARD_SYSUI_FLAGS;
+        if (interestingKeyguardFlags == mKeyguardSysuiFlags) {
+            return;
+        }
+        mKeyguardSysuiFlags = interestingKeyguardFlags;
+
+        mBouncerShowing = bouncerShowing;
+
+        mNavbarButtonsViewController.setKeyguardVisible(keyguardShowing || dozing,
+                keyguardOccluded);
+        updateIconsForBouncer();
+    }
+
+    public boolean isScreenOff() {
+        return mIsScreenOff;
+    }
+
+    public void setScreenOn() {
+        mIsScreenOff = false;
+    }
+
+    /**
+     * Hides/shows taskbar when keyguard is up
+     */
+    private void updateIconsForBouncer() {
+        boolean disableBack = (mKeyguardSysuiFlags & SYSUI_STATE_BACK_DISABLED) != 0;
+        boolean disableRecent = (mKeyguardSysuiFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0;
+        boolean disableHome = (mKeyguardSysuiFlags & SYSUI_STATE_HOME_DISABLED) != 0;
+        boolean onlyBackEnabled = !disableBack && disableRecent && disableHome;
+
+        boolean showBackForBouncer = onlyBackEnabled &&
+                mKeyguardManager.isDeviceSecure() &&
+                mBouncerShowing;
+        mNavbarButtonsViewController.setBackForBouncer(showBackForBouncer);
+    }
+
+    public void onDestroy() {
+        mContext.unregisterReceiver(mScreenOffReceiver);
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index d026bfb..92cee04 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -21,38 +21,65 @@
 import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
 import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
 import static com.android.launcher3.util.DisplayController.CHANGE_SUPPORTED_BOUNDS;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
 
+import android.content.ComponentCallbacks;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
 import android.hardware.display.DisplayManager;
-import android.inputmethodservice.InputMethodService;
+import android.net.Uri;
+import android.provider.Settings;
 import android.view.Display;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.Info;
+import com.android.launcher3.util.SettingsCache;
+import com.android.launcher3.util.SimpleBroadcastReceiver;
+import com.android.quickstep.RecentsActivity;
 import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TouchInteractionService;
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
+import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
 
 /**
- * Class to manager taskbar lifecycle
+ * Class to manage taskbar lifecycle
  */
 public class TaskbarManager implements DisplayController.DisplayInfoChangeListener,
         SysUINavigationMode.NavigationModeChangeListener {
 
+    private static final Uri USER_SETUP_COMPLETE_URI = Settings.Secure.getUriFor(
+            Settings.Secure.USER_SETUP_COMPLETE);
+
     private final Context mContext;
     private final DisplayController mDisplayController;
     private final SysUINavigationMode mSysUINavigationMode;
     private final TaskbarNavButtonController mNavButtonController;
+    private final SettingsCache.OnChangeListener mUserSetupCompleteListener;
+    private final ComponentCallbacks mComponentCallbacks;
+    private final SimpleBroadcastReceiver mShutdownReceiver;
+
+    // The source for this provider is set when Launcher is available
+    private final ScopedUnfoldTransitionProgressProvider mUnfoldProgressProvider =
+            new ScopedUnfoldTransitionProgressProvider();
 
     private TaskbarActivityContext mTaskbarActivityContext;
-    private BaseQuickstepLauncher mLauncher;
+    private StatefulActivity mActivity;
+    /**
+     * Cache a copy here so we can initialize state whenever taskbar is recreated, since
+     * this class does not get re-initialized w/ new taskbars.
+     */
+    private final TaskbarSharedState mSharedState = new TaskbarSharedState();
 
     private static final int CHANGE_FLAGS =
             CHANGE_ACTIVE_SCREEN | CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS;
@@ -66,9 +93,34 @@
                 service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
         mContext = service.createWindowContext(display, TYPE_APPLICATION_OVERLAY, null);
         mNavButtonController = new TaskbarNavButtonController(service);
+        mUserSetupCompleteListener = isUserSetupComplete -> recreateTaskbar();
+        mComponentCallbacks = new ComponentCallbacks() {
+            private Configuration mOldConfig = mContext.getResources().getConfiguration();
+
+            @Override
+            public void onConfigurationChanged(Configuration newConfig) {
+                int configDiff = mOldConfig.diff(newConfig);
+                int configsRequiringRecreate = ActivityInfo.CONFIG_ASSETS_PATHS
+                        | ActivityInfo.CONFIG_LAYOUT_DIRECTION;
+                if ((configDiff & configsRequiringRecreate) != 0) {
+                    // Color has changed, recreate taskbar to reload background color & icons.
+                    recreateTaskbar();
+                }
+                mOldConfig = newConfig;
+            }
+
+            @Override
+            public void onLowMemory() { }
+        };
+        mShutdownReceiver = new SimpleBroadcastReceiver(i -> destroyExistingTaskbar());
 
         mDisplayController.addChangeListener(this);
         mSysUINavigationMode.addModeChangeListener(this);
+        SettingsCache.INSTANCE.get(mContext).register(USER_SETUP_COMPLETE_URI,
+                mUserSetupCompleteListener);
+        mContext.registerComponentCallbacks(mComponentCallbacks);
+        mShutdownReceiver.register(mContext, Intent.ACTION_SHUTDOWN);
+
         recreateTaskbar();
     }
 
@@ -100,61 +152,114 @@
     }
 
     /**
-     * Sets or clears a launcher to act as taskbar callback
+     * Sets a {@link StatefulActivity} to act as taskbar callback
      */
-    public void setLauncher(@Nullable BaseQuickstepLauncher launcher) {
-        mLauncher = launcher;
+    public void setActivity(@NonNull StatefulActivity activity) {
+        mActivity = activity;
+        mUnfoldProgressProvider.setSourceProvider(getUnfoldTransitionProgressProviderForActivity(
+                activity));
+
         if (mTaskbarActivityContext != null) {
-            mTaskbarActivityContext.setUIController(mLauncher == null
-                    ? TaskbarUIController.DEFAULT
-                    : new LauncherTaskbarUIController(launcher, mTaskbarActivityContext));
+            mTaskbarActivityContext.setUIController(
+                    createTaskbarUIControllerForActivity(mActivity));
+        }
+    }
+
+    /**
+     * Returns an {@link UnfoldTransitionProgressProvider} to use while the given StatefulActivity
+     * is active.
+     */
+    private UnfoldTransitionProgressProvider getUnfoldTransitionProgressProviderForActivity(
+            StatefulActivity activity) {
+        if (activity instanceof BaseQuickstepLauncher) {
+            return ((BaseQuickstepLauncher) activity).getUnfoldTransitionProgressProvider();
+        }
+        return null;
+    }
+
+    /**
+     * Creates a {@link TaskbarUIController} to use while the given StatefulActivity is active.
+     */
+    private TaskbarUIController createTaskbarUIControllerForActivity(StatefulActivity activity) {
+        if (activity instanceof BaseQuickstepLauncher) {
+            return new LauncherTaskbarUIController((BaseQuickstepLauncher) activity);
+        }
+        if (activity instanceof RecentsActivity) {
+            return new FallbackTaskbarUIController((RecentsActivity) activity);
+        }
+        return TaskbarUIController.DEFAULT;
+    }
+
+    /**
+     * Clears a previously set {@link StatefulActivity}
+     */
+    public void clearActivity(@NonNull StatefulActivity activity) {
+        if (mActivity == activity) {
+            mActivity = null;
+            if (mTaskbarActivityContext != null) {
+                mTaskbarActivityContext.setUIController(TaskbarUIController.DEFAULT);
+            }
+            mUnfoldProgressProvider.setSourceProvider(null);
         }
     }
 
     private void recreateTaskbar() {
         destroyExistingTaskbar();
-        if (!FeatureFlags.ENABLE_TASKBAR.get()) {
+
+        DeviceProfile dp =
+                mUserUnlocked ? LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) : null;
+
+        boolean isTaskBarEnabled =
+                FeatureFlags.ENABLE_TASKBAR.get() && dp != null && dp.isTaskbarPresent;
+
+        if (!isTaskBarEnabled) {
+            SystemUiProxy.INSTANCE.get(mContext)
+                    .notifyTaskbarStatus(/* visible */ false, /* stashed */ false);
             return;
         }
-        if (!mUserUnlocked) {
-            return;
-        }
-        DeviceProfile dp = LauncherAppState.getIDP(mContext).getDeviceProfile(mContext);
-        if (!dp.isTaskbarPresent) {
-            return;
-        }
-        mTaskbarActivityContext = new TaskbarActivityContext(
-                mContext, dp.copy(mContext), mNavButtonController);
-        mTaskbarActivityContext.init();
-        if (mLauncher != null) {
+
+        mTaskbarActivityContext = new TaskbarActivityContext(mContext, dp.copy(mContext),
+                mNavButtonController, mUnfoldProgressProvider);
+
+        mTaskbarActivityContext.init(mSharedState);
+        if (mActivity != null) {
             mTaskbarActivityContext.setUIController(
-                    new LauncherTaskbarUIController(mLauncher, mTaskbarActivityContext));
+                    createTaskbarUIControllerForActivity(mActivity));
         }
     }
 
-    /**
-     * See {@link com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags}
-     * @param systemUiStateFlags The latest SystemUiStateFlags
-     */
     public void onSystemUiFlagsChanged(int systemUiStateFlags) {
-        boolean isImeVisible = (systemUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0;
+        mSharedState.sysuiStateFlags = systemUiStateFlags;
         if (mTaskbarActivityContext != null) {
-            mTaskbarActivityContext.setImeIsVisible(isImeVisible);
+            mTaskbarActivityContext.updateSysuiStateFlags(systemUiStateFlags);
         }
     }
 
     /**
-     * When in 3 button nav, the above doesn't get called since we prevent sysui nav bar from
-     * instantiating at all, which is what's responsible for sending sysui state flags over.
-     *
-     * @param vis IME visibility flag
-     * @param backDisposition Used to determine back button behavior for software keyboard
-     *                        See BACK_DISPOSITION_* constants in {@link InputMethodService}
+     * Sets the flag indicating setup UI is visible
      */
-    public void updateImeStatus(int displayId, int vis, int backDisposition,
-            boolean showImeSwitcher) {
+    public void setSetupUIVisible(boolean isVisible) {
+        mSharedState.setupUIVisible = isVisible;
         if (mTaskbarActivityContext != null) {
-            mTaskbarActivityContext.updateImeStatus(displayId, vis, showImeSwitcher);
+            mTaskbarActivityContext.setSetupUIVisible(isVisible);
+        }
+    }
+
+    public void onRotationProposal(int rotation, boolean isValid) {
+        if (mTaskbarActivityContext != null) {
+            mTaskbarActivityContext.onRotationProposal(rotation, isValid);
+        }
+    }
+
+    public void disableNavBarElements(int displayId, int state1, int state2, boolean animate) {
+        if (mTaskbarActivityContext != null) {
+            mTaskbarActivityContext.disableNavBarElements(displayId, state1, state2, animate);
+        }
+    }
+
+    public void onSystemBarAttributesChanged(int displayId, int behavior) {
+        if (mTaskbarActivityContext != null) {
+            mTaskbarActivityContext.onSystemBarAttributesChanged(displayId, behavior);
         }
     }
 
@@ -165,5 +270,13 @@
         destroyExistingTaskbar();
         mDisplayController.removeChangeListener(this);
         mSysUINavigationMode.removeModeChangeListener(this);
+        SettingsCache.INSTANCE.get(mContext).unregister(USER_SETUP_COMPLETE_URI,
+                mUserSetupCompleteListener);
+        mContext.unregisterComponentCallbacks(mComponentCallbacks);
+        mContext.unregisterReceiver(mShutdownReceiver);
+    }
+
+    public @Nullable TaskbarActivityContext getCurrentActivityContext() {
+        return mTaskbarActivityContext;
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
new file mode 100644
index 0000000..5e76b96
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
@@ -0,0 +1,189 @@
+/*
+ * 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.taskbar;
+
+import android.util.SparseArray;
+import android.view.View;
+
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.BgDataModel.FixedContainerItems;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.LauncherBindableItemsContainer;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Launcher model Callbacks for rendering taskbar.
+ */
+public class TaskbarModelCallbacks implements
+        BgDataModel.Callbacks, LauncherBindableItemsContainer {
+
+    private final SparseArray<ItemInfo> mHotseatItems = new SparseArray<>();
+    private List<ItemInfo> mPredictedItems = Collections.emptyList();
+
+    private final TaskbarActivityContext mContext;
+    private final TaskbarView mContainer;
+
+    // Initialized in init.
+    private TaskbarControllers mControllers;
+
+    private boolean mBindInProgress = false;
+
+    public TaskbarModelCallbacks(
+            TaskbarActivityContext context, TaskbarView container) {
+        mContext = context;
+        mContainer = container;
+    }
+
+    public void init(TaskbarControllers controllers) {
+        mControllers = controllers;
+    }
+
+    @Override
+    public void startBinding() {
+        mBindInProgress = true;
+        mHotseatItems.clear();
+        mPredictedItems = Collections.emptyList();
+    }
+
+    @Override
+    public void finishBindingItems(IntSet pagesBoundFirst) {
+        mBindInProgress = false;
+        commitItemsToUI();
+    }
+
+    @Override
+    public void bindAppsAdded(IntArray newScreens, ArrayList<ItemInfo> addNotAnimated,
+            ArrayList<ItemInfo> addAnimated) {
+        boolean add1 = handleItemsAdded(addNotAnimated);
+        boolean add2 = handleItemsAdded(addAnimated);
+        if (add1 || add2) {
+            commitItemsToUI();
+        }
+    }
+
+    @Override
+    public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons) {
+        if (handleItemsAdded(shortcuts)) {
+            commitItemsToUI();
+        }
+    }
+
+    private boolean handleItemsAdded(List<ItemInfo> items) {
+        boolean modified = false;
+        for (ItemInfo item : items) {
+            if (item.container == Favorites.CONTAINER_HOTSEAT) {
+                mHotseatItems.put(item.screenId, item);
+                modified = true;
+            }
+        }
+        return modified;
+    }
+
+
+    @Override
+    public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) {
+        updateWorkspaceItems(updated, mContext);
+    }
+
+    @Override
+    public void bindRestoreItemsChange(HashSet<ItemInfo> updates) {
+        updateRestoreItems(updates, mContext);
+    }
+
+    @Override
+    public void mapOverItems(ItemOperator op) {
+        final int itemCount = mContainer.getChildCount();
+        for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
+            View item = mContainer.getChildAt(itemIdx);
+            if (op.evaluate((ItemInfo) item.getTag(), item)) {
+                return;
+            }
+        }
+    }
+
+    @Override
+    public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher) {
+        if (handleItemsRemoved(matcher)) {
+            commitItemsToUI();
+        }
+    }
+
+    private boolean handleItemsRemoved(ItemInfoMatcher matcher) {
+        boolean modified = false;
+        for (int i = mHotseatItems.size() - 1; i >= 0; i--) {
+            if (matcher.matchesInfo(mHotseatItems.valueAt(i))) {
+                modified = true;
+                mHotseatItems.removeAt(i);
+            }
+        }
+        return modified;
+    }
+
+    @Override
+    public void bindItemsModified(List<ItemInfo> items) {
+        boolean removed = handleItemsRemoved(ItemInfoMatcher.ofItems(items));
+        boolean added = handleItemsAdded(items);
+        if (removed || added) {
+            commitItemsToUI();
+        }
+    }
+
+    @Override
+    public void bindExtraContainerItems(FixedContainerItems item) {
+        if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+            mPredictedItems = item.items;
+            commitItemsToUI();
+        }
+    }
+
+    private void commitItemsToUI() {
+        if (mBindInProgress) {
+            return;
+        }
+
+        ItemInfo[] hotseatItemInfos =
+                new ItemInfo[mContext.getDeviceProfile().numShownHotseatIcons];
+        int predictionSize = mPredictedItems.size();
+        int predictionNextIndex = 0;
+
+        boolean isHotseatEmpty = true;
+        for (int i = 0; i < hotseatItemInfos.length; i++) {
+            hotseatItemInfos[i] = mHotseatItems.get(i);
+            if (hotseatItemInfos[i] == null && predictionNextIndex < predictionSize) {
+                hotseatItemInfos[i] = mPredictedItems.get(predictionNextIndex);
+                hotseatItemInfos[i].screenId = i;
+                predictionNextIndex++;
+            }
+            if (hotseatItemInfos[i] != null) {
+                isHotseatEmpty = false;
+            }
+        }
+        mContainer.updateHotseatItems(hotseatItemInfos);
+
+        mControllers.taskbarStashController.updateStateForFlag(
+                TaskbarStashController.FLAG_STASHED_IN_APP_EMPTY, isHotseatEmpty);
+        mControllers.taskbarStashController.applyState();
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 3b5afad..6fbef9b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -18,11 +18,12 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import android.content.Intent;
 import android.view.inputmethod.InputMethodManager;
 
 import androidx.annotation.IntDef;
 
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.quickstep.OverviewCommandHelper;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TouchInteractionService;
@@ -44,7 +45,9 @@
             BUTTON_BACK,
             BUTTON_HOME,
             BUTTON_RECENTS,
-            BUTTON_IME_SWITCH
+            BUTTON_IME_SWITCH,
+            BUTTON_A11Y,
+            BUTTON_A11Y_LONG_CLICK
     })
 
     public @interface TaskbarButton {}
@@ -53,6 +56,8 @@
     static final int BUTTON_HOME = BUTTON_BACK << 1;
     static final int BUTTON_RECENTS = BUTTON_HOME << 1;
     static final int BUTTON_IME_SWITCH = BUTTON_RECENTS << 1;
+    static final int BUTTON_A11Y = BUTTON_IME_SWITCH << 1;
+    static final int BUTTON_A11Y_LONG_CLICK = BUTTON_A11Y << 1;
 
     private final TouchInteractionService mService;
 
@@ -74,18 +79,22 @@
             case BUTTON_IME_SWITCH:
                 showIMESwitcher();
                 break;
+            case BUTTON_A11Y:
+                notifyImeClick(false /* longClick */);
+                break;
+            case BUTTON_A11Y_LONG_CLICK:
+                notifyImeClick(true /* longClick */);
+                break;
         }
     }
 
     private void navigateHome() {
-        mService.startActivity(new Intent(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_HOME)
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+        mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_HOME);
     }
 
     private void navigateToOverview() {
-        mService.getOverviewCommandHelper()
-                .addCommand(OverviewCommandHelper.TYPE_SHOW);
+        TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
+        mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_TOGGLE);
     }
 
     private void executeBack() {
@@ -97,4 +106,13 @@
                 .showInputMethodPickerFromSystem(true /* showAuxiliarySubtypes */,
                         DEFAULT_DISPLAY);
     }
+
+    private void notifyImeClick(boolean longClick) {
+        SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
+        if (longClick) {
+            systemUiProxy.notifyAccessibilityButtonLongClicked();
+        } else {
+            systemUiProxy.notifyAccessibilityButtonClicked(mService.getDisplayId());
+        }
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
new file mode 100644
index 0000000..94a3307
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
@@ -0,0 +1,129 @@
+/*
+ * 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.taskbar;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * View that handles scrimming the taskbar and the inverted corners it draws. The scrim is used
+ * when bubbles is expanded.
+ */
+public class TaskbarScrimView extends View {
+    private final Paint mTaskbarScrimPaint;
+    private final Path mInvertedLeftCornerPath, mInvertedRightCornerPath;
+
+    private boolean mShowScrim;
+    private float mLeftCornerRadius, mRightCornerRadius;
+    private float mBackgroundHeight;
+
+    public TaskbarScrimView(Context context) {
+        this(context, null);
+    }
+
+    public TaskbarScrimView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TaskbarScrimView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public TaskbarScrimView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        mTaskbarScrimPaint = new Paint();
+        mTaskbarScrimPaint.setColor(getResources().getColor(android.R.color.system_neutral1_1000));
+        mTaskbarScrimPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
+        mTaskbarScrimPaint.setStyle(Paint.Style.FILL);
+
+        mInvertedLeftCornerPath = new Path();
+        mInvertedRightCornerPath = new Path();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        if (mShowScrim) {
+            canvas.save();
+            canvas.translate(0, canvas.getHeight() - mBackgroundHeight);
+
+            // Scrim the taskbar itself.
+            canvas.drawRect(0, 0, canvas.getWidth(), mBackgroundHeight, mTaskbarScrimPaint);
+
+            // Scrim the inverted rounded corners above the taskbar.
+            canvas.translate(0, -mLeftCornerRadius);
+            canvas.drawPath(mInvertedLeftCornerPath, mTaskbarScrimPaint);
+            canvas.translate(0, mLeftCornerRadius);
+            canvas.translate(canvas.getWidth() - mRightCornerRadius, -mRightCornerRadius);
+            canvas.drawPath(mInvertedRightCornerPath, mTaskbarScrimPaint);
+
+            canvas.restore();
+        }
+    }
+
+    /**
+     * Sets the height of the taskbar background.
+     * @param height the height of the background.
+     */
+    protected void setBackgroundHeight(float height) {
+        mBackgroundHeight = height;
+        if (mShowScrim) {
+            invalidate();
+        }
+    }
+
+    /**
+     * Sets the alpha of the taskbar scrim.
+     * @param alpha the alpha of the scrim.
+     */
+    protected void setScrimAlpha(float alpha) {
+        mShowScrim = alpha > 0f;
+        mTaskbarScrimPaint.setAlpha((int) (alpha * 255));
+        invalidate();
+    }
+
+    /**
+     * Sets the radius of the left and right corners above the taskbar.
+     * @param leftCornerRadius the radius of the left corner.
+     * @param rightCornerRadius the radius of the right corner.
+     */
+    protected void setCornerSizes(float leftCornerRadius, float rightCornerRadius) {
+        mLeftCornerRadius = leftCornerRadius;
+        mRightCornerRadius = rightCornerRadius;
+
+        Path square = new Path();
+        square.addRect(0, 0, mLeftCornerRadius, mLeftCornerRadius, Path.Direction.CW);
+        Path circle = new Path();
+        circle.addCircle(mLeftCornerRadius, 0, mLeftCornerRadius, Path.Direction.CW);
+        mInvertedLeftCornerPath.op(square, circle, Path.Op.DIFFERENCE);
+        square.reset();
+        square.addRect(0, 0, mRightCornerRadius, mRightCornerRadius, Path.Direction.CW);
+        circle.reset();
+        circle.addCircle(0, 0, mRightCornerRadius, Path.Direction.CW);
+        mInvertedRightCornerPath.op(square, circle, Path.Op.DIFFERENCE);
+
+        if (mShowScrim) {
+            invalidate();
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
new file mode 100644
index 0000000..e7e55ef
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -0,0 +1,94 @@
+/*
+ * 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.taskbar;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED;
+
+import android.animation.ObjectAnimator;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.SystemUiProxy;
+
+/**
+ * Handles properties/data collection, and passes the results to {@link TaskbarScrimView} to render.
+ */
+public class TaskbarScrimViewController {
+
+    private static final float SCRIM_ALPHA = 0.6f;
+
+    private static final Interpolator SCRIM_ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
+    private static final Interpolator SCRIM_ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
+
+    private final TaskbarActivityContext mActivity;
+    private final TaskbarScrimView mScrimView;
+
+    // Alpha property for the scrim.
+    private final AnimatedFloat mScrimAlpha = new AnimatedFloat(this::updateScrimAlpha);
+
+    // Initialized in init.
+    private TaskbarControllers mControllers;
+
+    public TaskbarScrimViewController(TaskbarActivityContext activity, TaskbarScrimView scrimView) {
+        mActivity = activity;
+        mScrimView = scrimView;
+        mScrimView.setCornerSizes(mActivity.getLeftCornerRadius(),
+                mActivity.getRightCornerRadius());
+        mScrimView.setBackgroundHeight(mActivity.getDeviceProfile().taskbarSize);
+    }
+
+    /**
+     * Initializes the controller
+     */
+    public void init(TaskbarControllers controllers) {
+        mControllers = controllers;
+    }
+
+    /**
+     * Updates the scrim state based on the flags.
+     */
+    public void updateStateForSysuiFlags(int stateFlags) {
+        final boolean bubblesExpanded = (stateFlags & SYSUI_STATE_BUBBLES_EXPANDED) != 0;
+        final boolean manageMenuExpanded =
+                (stateFlags & SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0;
+        final boolean showScrim = !mControllers.navbarButtonsViewController.isImeVisible()
+                && bubblesExpanded && mControllers.taskbarStashController.isInAppAndNotStashed();
+        final float scrimAlpha = manageMenuExpanded
+                // When manage menu shows there's the first scrim and second scrim so figure out
+                // what the total transparency would be.
+                ? (SCRIM_ALPHA + (SCRIM_ALPHA * (1 - SCRIM_ALPHA)))
+                : showScrim ? SCRIM_ALPHA : 0;
+        showScrim(showScrim, scrimAlpha);
+    }
+
+    private void showScrim(boolean showScrim, float alpha) {
+        mScrimView.setOnClickListener(showScrim ? (view) -> onClick() : null);
+        mScrimView.setClickable(showScrim);
+        ObjectAnimator anim = mScrimAlpha.animateToValue(showScrim ? alpha : 0);
+        anim.setInterpolator(showScrim ? SCRIM_ALPHA_IN : SCRIM_ALPHA_OUT);
+        anim.start();
+    }
+
+    private void updateScrimAlpha() {
+        mScrimView.setScrimAlpha(mScrimAlpha.value);
+    }
+
+    private void onClick() {
+        SystemUiProxy.INSTANCE.get(mActivity).onBackPressed();
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
new file mode 100644
index 0000000..23beef0
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
@@ -0,0 +1,27 @@
+/*
+ * 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.taskbar;
+
+/**
+ * State shared across different taskbar instance
+ */
+public class TaskbarSharedState {
+
+    public int sysuiStateFlags;
+
+    public boolean setupUIVisible = false;
+
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
new file mode 100644
index 0000000..0dd4ef1
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -0,0 +1,484 @@
+/*
+ * 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.taskbar;
+
+import static android.view.HapticFeedbackConstants.LONG_PRESS;
+
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_HIDE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.annotation.Nullable;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.view.ViewConfiguration;
+
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.SystemUiProxy;
+
+import java.util.function.IntPredicate;
+
+/**
+ * Coordinates between controllers such as TaskbarViewController and StashedHandleViewController to
+ * create a cohesive animation between stashed/unstashed states.
+ */
+public class TaskbarStashController {
+
+    public static final int FLAG_IN_APP = 1 << 0;
+    public static final int FLAG_STASHED_IN_APP_MANUAL = 1 << 1; // long press, persisted
+    public static final int FLAG_STASHED_IN_APP_PINNED = 1 << 2; // app pinning
+    public static final int FLAG_STASHED_IN_APP_EMPTY = 1 << 3; // no hotseat icons
+    public static final int FLAG_STASHED_IN_APP_SETUP = 1 << 4; // setup wizard and AllSetActivity
+    public static final int FLAG_IN_STASHED_LAUNCHER_STATE = 1 << 5;
+
+    // If we're in an app and any of these flags are enabled, taskbar should be stashed.
+    public static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_MANUAL
+            | FLAG_STASHED_IN_APP_PINNED | FLAG_STASHED_IN_APP_EMPTY | FLAG_STASHED_IN_APP_SETUP;
+
+    /**
+     * How long to stash/unstash when manually invoked via long press.
+     */
+    public static final long TASKBAR_STASH_DURATION = 300;
+
+    /**
+     * The scale TaskbarView animates to when being stashed.
+     */
+    private static final float STASHED_TASKBAR_SCALE = 0.5f;
+
+    /**
+     * How long the hint animation plays, starting on motion down.
+     */
+    private static final long TASKBAR_HINT_STASH_DURATION =
+            ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT;
+
+    /**
+     * The scale that TaskbarView animates to when hinting towards the stashed state.
+     */
+    private static final float STASHED_TASKBAR_HINT_SCALE = 0.9f;
+
+    /**
+     * The scale that the stashed handle animates to when hinting towards the unstashed state.
+     */
+    private static final float UNSTASHED_TASKBAR_HANDLE_HINT_SCALE = 1.1f;
+
+    /**
+     * The SharedPreferences key for whether user has manually stashed the taskbar.
+     */
+    private static final String SHARED_PREFS_STASHED_KEY = "taskbar_is_stashed";
+
+    /**
+     * Whether taskbar should be stashed out of the box.
+     */
+    private static final boolean DEFAULT_STASHED_PREF = false;
+
+    private final TaskbarActivityContext mActivity;
+    private final SharedPreferences mPrefs;
+    private final int mStashedHeight;
+    private final int mUnstashedHeight;
+
+    // Initialized in init.
+    private TaskbarControllers mControllers;
+    // Taskbar background properties.
+    private AnimatedFloat mTaskbarBackgroundOffset;
+    // TaskbarView icon properties.
+    private AlphaProperty mIconAlphaForStash;
+    private AnimatedFloat mIconScaleForStash;
+    private AnimatedFloat mIconTranslationYForStash;
+    // Stashed handle properties.
+    private AlphaProperty mTaskbarStashedHandleAlpha;
+    private AnimatedFloat mTaskbarStashedHandleHintScale;
+
+    /** Whether we are currently visually stashed (might change based on launcher state). */
+    private boolean mIsStashed = false;
+    private int mState;
+
+    private @Nullable AnimatorSet mAnimator;
+
+    // Evaluate whether the handle should be stashed
+    private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder(
+            flags -> {
+                boolean inApp = hasAnyFlag(flags, FLAG_IN_APP);
+                boolean stashedInApp = hasAnyFlag(flags, FLAGS_STASHED_IN_APP);
+                boolean stashedLauncherState = hasAnyFlag(flags, FLAG_IN_STASHED_LAUNCHER_STATE);
+                return (inApp && stashedInApp) || (!inApp && stashedLauncherState);
+            });
+
+    public TaskbarStashController(TaskbarActivityContext activity) {
+        mActivity = activity;
+        mPrefs = Utilities.getPrefs(mActivity);
+        final Resources resources = mActivity.getResources();
+        mStashedHeight = resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
+        mUnstashedHeight = mActivity.getDeviceProfile().taskbarSize;
+    }
+
+    public void init(TaskbarControllers controllers, TaskbarSharedState sharedState) {
+        mControllers = controllers;
+
+        TaskbarDragLayerController dragLayerController = controllers.taskbarDragLayerController;
+        mTaskbarBackgroundOffset = dragLayerController.getTaskbarBackgroundOffset();
+
+        TaskbarViewController taskbarViewController = controllers.taskbarViewController;
+        mIconAlphaForStash = taskbarViewController.getTaskbarIconAlpha().getProperty(
+                TaskbarViewController.ALPHA_INDEX_STASH);
+        mIconScaleForStash = taskbarViewController.getTaskbarIconScaleForStash();
+        mIconTranslationYForStash = taskbarViewController.getTaskbarIconTranslationYForStash();
+
+        StashedHandleViewController stashedHandleController =
+                controllers.stashedHandleViewController;
+        mTaskbarStashedHandleAlpha = stashedHandleController.getStashedHandleAlpha().getProperty(
+                StashedHandleViewController.ALPHA_INDEX_STASHED);
+        mTaskbarStashedHandleHintScale = stashedHandleController.getStashedHandleHintScale();
+
+        boolean isManuallyStashedInApp = supportsManualStashing()
+                && mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF);
+        updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp);
+        updateStateForFlag(FLAG_STASHED_IN_APP_SETUP,
+                !mActivity.isUserSetupComplete() || sharedState.setupUIVisible);
+        applyState();
+
+        SystemUiProxy.INSTANCE.get(mActivity)
+                .notifyTaskbarStatus(/* visible */ false, /* stashed */ isStashedInApp());
+    }
+
+    /**
+     * Returns whether the taskbar can visually stash into a handle based on the current device
+     * state.
+     */
+    private boolean supportsVisualStashing() {
+        return !mActivity.isThreeButtonNav();
+    }
+
+    /**
+     * Returns whether the user can manually stash the taskbar based on the current device state.
+     */
+    private boolean supportsManualStashing() {
+        return supportsVisualStashing()
+                && (!Utilities.IS_RUNNING_IN_TEST_HARNESS || supportsStashingForTests());
+    }
+
+    private boolean supportsStashingForTests() {
+        // TODO: enable this for tests that specifically check stash/unstash behavior.
+        return false;
+    }
+
+    /**
+     * Sets the flag indicating setup UI is visible
+     */
+    protected void setSetupUIVisible(boolean isVisible) {
+        updateStateForFlag(FLAG_STASHED_IN_APP_SETUP,
+                isVisible || !mActivity.isUserSetupComplete());
+        applyState();
+    }
+
+    /**
+     * Returns whether the taskbar is currently visually stashed.
+     */
+    public boolean isStashed() {
+        return mIsStashed;
+    }
+
+    /**
+     * Returns whether the taskbar should be stashed in apps (e.g. user long pressed to stash).
+     */
+    public boolean isStashedInApp() {
+        return hasAnyFlag(FLAGS_STASHED_IN_APP);
+    }
+
+    private boolean hasAnyFlag(int flagMask) {
+        return hasAnyFlag(mState, flagMask);
+    }
+
+    private boolean hasAnyFlag(int flags, int flagMask) {
+        return (flags & flagMask) != 0;
+    }
+
+
+    /**
+     * Returns whether the taskbar is currently visible and in an app.
+     */
+    public boolean isInAppAndNotStashed() {
+        return !mIsStashed && (mState & FLAG_IN_APP) != 0;
+    }
+
+    public int getContentHeight() {
+        if (isStashed()) {
+            boolean isAnimating = mAnimator != null && mAnimator.isStarted();
+            return mControllers.stashedHandleViewController.isStashedHandleVisible() || isAnimating
+                    ? mStashedHeight : 0;
+        }
+        return mUnstashedHeight;
+    }
+
+    public int getStashedHeight() {
+        return mStashedHeight;
+    }
+
+    /**
+     * Should be called when long pressing the nav region when taskbar is present.
+     * @return Whether taskbar was stashed and now is unstashed.
+     */
+    public boolean onLongPressToUnstashTaskbar() {
+        if (!isStashed()) {
+            // We only listen for long press on the nav region to unstash the taskbar. To stash the
+            // taskbar, we use an OnLongClickListener on TaskbarView instead.
+            return false;
+        }
+        if (updateAndAnimateIsManuallyStashedInApp(false)) {
+            mControllers.taskbarActivityContext.getDragLayer().performHapticFeedback(LONG_PRESS);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Updates whether we should stash the taskbar when in apps, and animates to the changed state.
+     * @return Whether we started an animation to either be newly stashed or unstashed.
+     */
+    public boolean updateAndAnimateIsManuallyStashedInApp(boolean isManuallyStashedInApp) {
+        if (!supportsManualStashing()) {
+            return false;
+        }
+        if (hasAnyFlag(FLAG_STASHED_IN_APP_MANUAL) != isManuallyStashedInApp) {
+            mPrefs.edit().putBoolean(SHARED_PREFS_STASHED_KEY, isManuallyStashedInApp).apply();
+            updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp);
+            applyState();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Create a stash animation and save to {@link #mAnimator}.
+     * @param isStashed whether it's a stash animation or an unstash animation
+     * @param duration duration of the animation
+     */
+    private void createAnimToIsStashed(boolean isStashed, long duration) {
+        if (mAnimator != null) {
+            mAnimator.cancel();
+        }
+        mAnimator = new AnimatorSet();
+
+        if (!supportsVisualStashing()) {
+            // Just hide/show the icons instead of stashing into a handle.
+            mAnimator.play(mIconAlphaForStash.animateToValue(isStashed ? 0 : 1)
+                    .setDuration(duration));
+            return;
+        }
+
+        AnimatorSet fullLengthAnimatorSet = new AnimatorSet();
+        // Not exactly half and may overlap. See [first|second]HalfDurationScale below.
+        AnimatorSet firstHalfAnimatorSet = new AnimatorSet();
+        AnimatorSet secondHalfAnimatorSet = new AnimatorSet();
+
+        final float firstHalfDurationScale;
+        final float secondHalfDurationScale;
+
+        if (isStashed) {
+            firstHalfDurationScale = 0.75f;
+            secondHalfDurationScale = 0.5f;
+            final float stashTranslation = (mUnstashedHeight - mStashedHeight) / 2f;
+
+            fullLengthAnimatorSet.playTogether(
+                    mTaskbarBackgroundOffset.animateToValue(1),
+                    mIconTranslationYForStash.animateToValue(stashTranslation)
+            );
+            firstHalfAnimatorSet.playTogether(
+                    mIconAlphaForStash.animateToValue(0),
+                    mIconScaleForStash.animateToValue(STASHED_TASKBAR_SCALE)
+            );
+            secondHalfAnimatorSet.playTogether(
+                    mTaskbarStashedHandleAlpha.animateToValue(1)
+            );
+        } else  {
+            firstHalfDurationScale = 0.5f;
+            secondHalfDurationScale = 0.75f;
+
+            fullLengthAnimatorSet.playTogether(
+                    mTaskbarBackgroundOffset.animateToValue(0),
+                    mIconScaleForStash.animateToValue(1),
+                    mIconTranslationYForStash.animateToValue(0)
+            );
+            firstHalfAnimatorSet.playTogether(
+                    mTaskbarStashedHandleAlpha.animateToValue(0)
+            );
+            secondHalfAnimatorSet.playTogether(
+                    mIconAlphaForStash.animateToValue(1)
+            );
+        }
+
+        Animator stashedHandleRevealAnim = mControllers.stashedHandleViewController
+                .createRevealAnimToIsStashed(isStashed);
+        if (stashedHandleRevealAnim != null) {
+            fullLengthAnimatorSet.play(stashedHandleRevealAnim);
+        }
+        // Return the stashed handle to its default scale in case it was changed as part of the
+        // feedforward hint. Note that the reveal animation above also visually scales it.
+        fullLengthAnimatorSet.play(mTaskbarStashedHandleHintScale.animateToValue(1f));
+
+        fullLengthAnimatorSet.setDuration(duration);
+        firstHalfAnimatorSet.setDuration((long) (duration * firstHalfDurationScale));
+        secondHalfAnimatorSet.setDuration((long) (duration * secondHalfDurationScale));
+        secondHalfAnimatorSet.setStartDelay((long) (duration * (1 - secondHalfDurationScale)));
+
+        mAnimator.playTogether(fullLengthAnimatorSet, firstHalfAnimatorSet,
+                secondHalfAnimatorSet);
+        mAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mIsStashed = isStashed;
+                onIsStashed(mIsStashed);
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mAnimator = null;
+            }
+        });
+    }
+
+    /**
+     * Creates and starts a partial stash animation, hinting at the new state that will trigger when
+     * long press is detected.
+     * @param animateForward Whether we are going towards the new stashed state or returning to the
+     *                       unstashed state.
+     */
+    public void startStashHint(boolean animateForward) {
+        if (isStashed() || !supportsManualStashing()) {
+            // Already stashed, no need to hint in that direction.
+            return;
+        }
+        mIconScaleForStash.animateToValue(
+                animateForward ? STASHED_TASKBAR_HINT_SCALE : 1)
+                .setDuration(TASKBAR_HINT_STASH_DURATION).start();
+    }
+
+    /**
+     * Creates and starts a partial unstash animation, hinting at the new state that will trigger
+     * when long press is detected.
+     * @param animateForward Whether we are going towards the new unstashed state or returning to
+     *                       the stashed state.
+     */
+    public void startUnstashHint(boolean animateForward) {
+        if (!isStashed()) {
+            // Already unstashed, no need to hint in that direction.
+            return;
+        }
+        mTaskbarStashedHandleHintScale.animateToValue(
+                animateForward ? UNSTASHED_TASKBAR_HANDLE_HINT_SCALE : 1)
+                .setDuration(TASKBAR_HINT_STASH_DURATION).start();
+    }
+
+    private void onIsStashed(boolean isStashed) {
+        mControllers.stashedHandleViewController.onIsStashed(isStashed);
+    }
+
+    public void applyState() {
+        applyState(TASKBAR_STASH_DURATION);
+    }
+
+    public void applyState(long duration) {
+        mStatePropertyHolder.setState(mState, duration, true);
+    }
+
+    public Animator applyStateWithoutStart() {
+        return applyStateWithoutStart(TASKBAR_STASH_DURATION);
+    }
+
+    public Animator applyStateWithoutStart(long duration) {
+        return mStatePropertyHolder.setState(mState, duration, false);
+    }
+
+    /** Called when some system ui state has changed. (See SYSUI_STATE_... in QuickstepContract) */
+    public void updateStateForSysuiFlags(int systemUiStateFlags) {
+        updateStateForFlag(FLAG_STASHED_IN_APP_PINNED,
+                hasAnyFlag(systemUiStateFlags, SYSUI_STATE_SCREEN_PINNING));
+        applyState();
+    }
+
+    /**
+     * Updates the proper flag to indicate whether the task bar should be stashed.
+     *
+     * Note that this only updates the flag. {@link #applyState()} needs to be called separately.
+     *
+     * @param flag The flag to update.
+     * @param enabled Whether to enable the flag: True will cause the task bar to be stashed /
+     *                unstashed.
+     */
+    public void updateStateForFlag(int flag, boolean enabled) {
+        if (enabled) {
+            mState |= flag;
+        } else {
+            mState &= ~flag;
+        }
+    }
+
+    /**
+     * Called after updateStateForFlag() and applyState() have been called.
+     * @param changedFlags The flags that have changed.
+     */
+    private void onStateChangeApplied(int changedFlags) {
+        if (hasAnyFlag(changedFlags, FLAGS_STASHED_IN_APP)) {
+            mControllers.uiController.onStashedInAppChanged();
+        }
+        if (hasAnyFlag(changedFlags, FLAGS_STASHED_IN_APP | FLAG_IN_APP)) {
+            SystemUiProxy.INSTANCE.get(mActivity)
+                    .notifyTaskbarStatus(/* visible */ hasAnyFlag(FLAG_IN_APP),
+                            /* stashed */ isStashedInApp());
+        }
+        if (hasAnyFlag(changedFlags, FLAG_STASHED_IN_APP_MANUAL)) {
+            if (hasAnyFlag(FLAG_STASHED_IN_APP_MANUAL)) {
+                mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_LONGPRESS_HIDE);
+            } else {
+                mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_LONGPRESS_SHOW);
+            }
+        }
+    }
+
+    private class StatePropertyHolder {
+        private final IntPredicate mStashCondition;
+
+        private boolean mIsStashed;
+        private int mPrevFlags;
+
+        StatePropertyHolder(IntPredicate stashCondition) {
+            mStashCondition = stashCondition;
+        }
+
+        public Animator setState(int flags, long duration, boolean start) {
+            if (mPrevFlags != flags) {
+                int changedFlags = mPrevFlags ^ flags;
+                onStateChangeApplied(changedFlags);
+                mPrevFlags = flags;
+            }
+            boolean isStashed = mStashCondition.test(flags);
+            if (mIsStashed != isStashed) {
+                mIsStashed = isStashed;
+                createAnimToIsStashed(mIsStashed, duration);
+                if (start) {
+                    mAnimator.start();
+                }
+                return mAnimator;
+            }
+            return null;
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
index a701aae..edd2a22 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
@@ -16,11 +16,8 @@
 package com.android.launcher3.taskbar;
 
 import static com.android.launcher3.LauncherState.TASKBAR;
-import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 
-import androidx.annotation.Nullable;
-
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.anim.PendingAnimation;
@@ -28,29 +25,26 @@
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.SystemUiProxy;
 
 /**
- * StateHandler to animate Taskbar according to Launcher's state machine. Does nothing if Taskbar
- * isn't present (i.e. {@link #setAnimationController} is never called).
+ * StateHandler to animate Taskbar according to Launcher's state machine.
  */
 public class TaskbarStateHandler implements StateManager.StateHandler<LauncherState> {
 
     private final BaseQuickstepLauncher mLauncher;
 
-    // Contains Taskbar-related methods and fields we should aniamte. If null, don't do anything.
-    private @Nullable TaskbarAnimationController mAnimationController = null;
+    private AnimatedFloat mNavbarButtonAlpha = new AnimatedFloat(this::updateNavbarButtonAlpha);
 
     public TaskbarStateHandler(BaseQuickstepLauncher launcher) {
         mLauncher = launcher;
     }
 
-    public void setAnimationController(TaskbarAnimationController callbacks) {
-        mAnimationController = callbacks;
-    }
-
     @Override
     public void setState(LauncherState state) {
         setState(state, PropertySetter.NO_ANIM_PROPERTY_SETTER);
+        // Force update the alpha in case it was not initialized properly
+        updateNavbarButtonAlpha();
     }
 
     @Override
@@ -59,17 +53,19 @@
         setState(toState, animation);
     }
 
-    private void setState(LauncherState toState, PropertySetter setter) {
-        if (mAnimationController == null) {
-            return;
-        }
-
+    /**
+     * Sets the provided state
+     */
+    public void setState(LauncherState toState, PropertySetter setter) {
         boolean isTaskbarVisible = (toState.getVisibleElements(mLauncher) & TASKBAR) != 0;
-        setter.setFloat(mAnimationController.getTaskbarVisibilityForLauncherState(),
-                AnimatedFloat.VALUE, isTaskbarVisible ? 1f : 0f, LINEAR);
-        setter.setFloat(mAnimationController.getTaskbarScaleForLauncherState(),
-                AnimatedFloat.VALUE, toState.getTaskbarScale(mLauncher), LINEAR);
-        setter.setFloat(mAnimationController.getTaskbarTranslationYForLauncherState(),
-                AnimatedFloat.VALUE, toState.getTaskbarTranslationY(mLauncher), ACCEL_DEACCEL);
+        // Make the nav bar visible in states that taskbar isn't visible.
+        // TODO: We should draw our own handle instead of showing the nav bar.
+        float navbarButtonAlpha = isTaskbarVisible ? 0f : 1f;
+        setter.setFloat(mNavbarButtonAlpha, AnimatedFloat.VALUE, navbarButtonAlpha, LINEAR);
+    }
+
+
+    private void updateNavbarButtonAlpha() {
+        SystemUiProxy.INSTANCE.get(mLauncher).setNavBarButtonAlpha(mNavbarButtonAlpha.value, false);
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 50adead..d8360e0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -15,6 +15,13 @@
  */
 package com.android.launcher3.taskbar;
 
+import android.graphics.Rect;
+
+import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+
+import java.util.stream.Stream;
+
 /**
  * Base class for providing different taskbar UI
  */
@@ -22,12 +29,7 @@
 
     public static final TaskbarUIController DEFAULT = new TaskbarUIController();
 
-    /**
-     * Pads the Hotseat to line up exactly with Taskbar's copy of the Hotseat.
-     */
-    public void alignRealHotseatWithTaskbar() { }
-
-    protected void onCreate() { }
+    protected void init(TaskbarControllers taskbarControllers) { }
 
     protected void onDestroy() { }
 
@@ -35,7 +37,13 @@
         return true;
     }
 
-    protected void onImeVisible(TaskbarDragLayer container, boolean isVisible) {
-        container.updateImeBarVisibilityAlpha(isVisible ? 1 : 0);
+    protected void updateContentInsets(Rect outContentInsets) { }
+
+    protected void onStashedInAppChanged() { }
+
+    public Stream<ItemInfoWithIcon> getAppIconsForEdu() {
+        return Stream.empty();
     }
+
+    public void onTaskbarIconLaunched(WorkspaceItemInfo item) { }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java
new file mode 100644
index 0000000..c785186
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java
@@ -0,0 +1,88 @@
+/*
+ * 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.taskbar;
+
+import android.view.View;
+import android.view.WindowManager;
+
+import com.android.quickstep.util.LauncherViewsMoveFromCenterTranslationApplier;
+import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator;
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
+
+/**
+ * Controls animation of taskbar icons when unfolding foldable devices
+ */
+public class TaskbarUnfoldAnimationController {
+
+    private final ScopedUnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider;
+    private final UnfoldMoveFromCenterAnimator mMoveFromCenterAnimator;
+    private final TransitionListener mTransitionListener = new TransitionListener();
+    private TaskbarViewController mTaskbarViewController;
+
+    public TaskbarUnfoldAnimationController(ScopedUnfoldTransitionProgressProvider
+            unfoldTransitionProgressProvider, WindowManager windowManager) {
+        mUnfoldTransitionProgressProvider = unfoldTransitionProgressProvider;
+        mMoveFromCenterAnimator = new UnfoldMoveFromCenterAnimator(windowManager,
+                new LauncherViewsMoveFromCenterTranslationApplier());
+    }
+
+    /**
+     * Initializes the controller
+     * @param taskbarControllers references to all other taskbar controllers
+     */
+    public void init(TaskbarControllers taskbarControllers) {
+        mTaskbarViewController = taskbarControllers.taskbarViewController;
+        mTaskbarViewController.addOneTimePreDrawListener(() ->
+                mUnfoldTransitionProgressProvider.setReadyToHandleTransition(true));
+        mUnfoldTransitionProgressProvider.addCallback(mTransitionListener);
+    }
+
+    /**
+     * Destroys the controller
+     */
+    public void onDestroy() {
+        mUnfoldTransitionProgressProvider.setReadyToHandleTransition(false);
+        mUnfoldTransitionProgressProvider.removeCallback(mTransitionListener);
+    }
+
+    private class TransitionListener implements TransitionProgressListener {
+
+        @Override
+        public void onTransitionStarted() {
+            mMoveFromCenterAnimator.updateDisplayProperties();
+            View[] icons = mTaskbarViewController.getIconViews();
+            for (View icon : icons) {
+                // TODO(b/193794563) we should re-register views if they are re-bound/re-inflated
+                //                   during the animation
+                mMoveFromCenterAnimator.registerViewForAnimation(icon);
+            }
+
+            mMoveFromCenterAnimator.onTransitionStarted();
+        }
+
+        @Override
+        public void onTransitionFinished() {
+            mMoveFromCenterAnimator.onTransitionFinished();
+            mMoveFromCenterAnimator.clearRegisteredViews();
+        }
+
+        @Override
+        public void onTransitionProgress(float progress) {
+            mMoveFromCenterAnimator.onTransitionProgress(progress);
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index c6573a6..59393d7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -15,76 +15,54 @@
  */
 package com.android.launcher3.taskbar;
 
-import static android.view.View.MeasureSpec.EXACTLY;
-import static android.view.View.MeasureSpec.makeMeasureSpec;
-
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.util.AttributeSet;
-import android.view.DragEvent;
-import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
-import android.widget.LinearLayout;
+import android.widget.FrameLayout;
 
 import androidx.annotation.LayoutRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.folder.FolderIcon;
 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.uioverrides.ApiWrapper;
 import com.android.launcher3.views.ActivityContext;
 
 /**
  * Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
  */
-public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconParent, Insettable {
+public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconParent, Insettable {
 
-    private final int mIconTouchSize;
-    private final boolean mIsRtl;
-    private final int mTouchSlop;
-    private final RectF mTempDelegateBounds = new RectF();
-    private final RectF mDelegateSlopBounds = new RectF();
     private final int[] mTempOutLocation = new int[2];
 
+    private final Rect mIconLayoutBounds = new Rect();
+    private final int mIconTouchSize;
     private final int mItemMarginLeftRight;
+    private final int mItemPadding;
 
     private final TaskbarActivityContext mActivityContext;
 
-    // Initialized in TaskbarController constructor.
+    // Initialized in init.
+    private TaskbarViewController.TaskbarViewCallbacks mControllerCallbacks;
     private View.OnClickListener mIconClickListener;
     private View.OnLongClickListener mIconLongClickListener;
 
-    LinearLayout mSystemButtonContainer;
-    LinearLayout mHotseatIconsContainer;
-
-    // Delegate touches to the closest view if within mIconTouchSize.
-    private boolean mDelegateTargeted;
-    private View mDelegateView;
     // Prevents dispatching touches to children if true
     private boolean mTouchEnabled = true;
 
-    private boolean mIsDraggingItem;
     // Only non-null when the corresponding Folder is open.
     private @Nullable FolderIcon mLeaveBehindFolderIcon;
 
-    /** Provider of buttons added to taskbar in 3 button nav */
-    private ButtonProvider mButtonProvider;
-
-    private boolean mDisableRelayout;
-    private boolean mAreHolesAllowed;
-
     public TaskbarView(@NonNull Context context) {
         this(context, null);
     }
@@ -105,85 +83,80 @@
 
         Resources resources = getResources();
         mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size);
-        mItemMarginLeftRight = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
 
-        mIsRtl = Utilities.isRtl(resources);
-        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+        int actualMargin = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
+        int actualIconSize = mActivityContext.getDeviceProfile().iconSizePx;
+
+        // We layout the icons to be of mIconTouchSize in width and height
+        mItemMarginLeftRight = actualMargin - (mIconTouchSize - actualIconSize) / 2;
+        mItemPadding = (mIconTouchSize - actualIconSize) / 2;
+
+        // Needed to draw folder leave-behind when opening one.
+        setWillNotDraw(false);
     }
 
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mSystemButtonContainer = findViewById(R.id.system_button_layout);
-        mHotseatIconsContainer = findViewById(R.id.hotseat_icons_layout);
+    protected void init(TaskbarViewController.TaskbarViewCallbacks callbacks) {
+        mControllerCallbacks = callbacks;
+        mIconClickListener = mControllerCallbacks.getIconOnClickListener();
+        mIconLongClickListener = mControllerCallbacks.getIconOnLongClickListener();
+
+        setOnLongClickListener(mControllerCallbacks.getBackgroundOnLongClickListener());
     }
 
-    protected void construct(OnClickListener clickListener, OnLongClickListener longClickListener,
-                ButtonProvider buttonProvider) {
-        mIconClickListener = clickListener;
-        mIconLongClickListener = longClickListener;
-        mButtonProvider = buttonProvider;
-
-        if (mActivityContext.canShowNavButtons()) {
-            createNavButtons();
-        } else {
-            mSystemButtonContainer.setVisibility(GONE);
+    private void removeAndRecycle(View view) {
+        removeView(view);
+        view.setOnClickListener(null);
+        view.setOnLongClickListener(null);
+        if (!(view.getTag() instanceof FolderInfo)) {
+            mActivityContext.getViewCache().recycleView(view.getSourceLayoutResId(), view);
         }
-
-        int numHotseatIcons = mActivityContext.getDeviceProfile().numShownHotseatIcons;
-        updateHotseatItems(new ItemInfo[numHotseatIcons]);
-    }
-
-    /**
-     * Enables/disables empty icons in taskbar so that the layout matches with Launcher
-     */
-    public void setHolesAllowedInLayout(boolean areHolesAllowed) {
-        if (mAreHolesAllowed != areHolesAllowed) {
-            mAreHolesAllowed = areHolesAllowed;
-            updateHotseatItemsVisibility();
-            // TODO: Add animation
-        }
-    }
-
-    private void setHolesAllowedInLayoutNoAnimation(boolean areHolesAllowed) {
-        if (mAreHolesAllowed != areHolesAllowed) {
-            mAreHolesAllowed = areHolesAllowed;
-            updateHotseatItemsVisibility();
-            onMeasure(makeMeasureSpec(getMeasuredWidth(), EXACTLY),
-                    makeMeasureSpec(getMeasuredHeight(), EXACTLY));
-            onLayout(false, getLeft(), getTop(), getRight(), getBottom());
-        }
+        view.setTag(null);
     }
 
     /**
      * Inflates/binds the Hotseat views to show in the Taskbar given their ItemInfos.
      */
     protected void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
+        int nextViewIndex = 0;
+        int numViewsAnimated = 0;
+
         for (int i = 0; i < hotseatItemInfos.length; i++) {
-            ItemInfo hotseatItemInfo = hotseatItemInfos[
-                    !mIsRtl ? i : hotseatItemInfos.length - i - 1];
-            View hotseatView = mHotseatIconsContainer.getChildAt(i);
+            ItemInfo hotseatItemInfo = hotseatItemInfos[i];
+            if (hotseatItemInfo == null) {
+                continue;
+            }
 
             // Replace any Hotseat views with the appropriate type if it's not already that type.
             final int expectedLayoutResId;
             boolean isFolder = false;
-            boolean needsReinflate = false;
-            if (hotseatItemInfo != null && hotseatItemInfo.isPredictedItem()) {
+            if (hotseatItemInfo.isPredictedItem()) {
                 expectedLayoutResId = R.layout.taskbar_predicted_app_icon;
             } else if (hotseatItemInfo instanceof FolderInfo) {
                 expectedLayoutResId = R.layout.folder_icon;
                 isFolder = true;
-                // Unlike for BubbleTextView, we can't reapply a new FolderInfo after inflation, so
-                // if the info changes we need to reinflate. This should only happen if a new folder
-                // is dragged to the position that another folder previously existed.
-                needsReinflate = hotseatView != null && hotseatView.getTag() != hotseatItemInfo;
             } else {
                 expectedLayoutResId = R.layout.taskbar_app_icon;
             }
-            if (hotseatView == null
-                    || hotseatView.getSourceLayoutResId() != expectedLayoutResId
-                    || needsReinflate) {
-                mHotseatIconsContainer.removeView(hotseatView);
+
+            View hotseatView = null;
+            while (nextViewIndex < getChildCount()) {
+                hotseatView = getChildAt(nextViewIndex);
+
+                // see if the view can be reused
+                if ((hotseatView.getSourceLayoutResId() != expectedLayoutResId)
+                        || (isFolder && (hotseatView.getTag() != hotseatItemInfo))) {
+                    // Unlike for BubbleTextView, we can't reapply a new FolderInfo after inflation,
+                    // so if the info changes we need to reinflate. This should only happen if a new
+                    // folder is dragged to the position that another folder previously existed.
+                    removeAndRecycle(hotseatView);
+                    hotseatView = null;
+                } else {
+                    // View found
+                    break;
+                }
+            }
+
+            if (hotseatView == null) {
                 if (isFolder) {
                     FolderInfo folderInfo = (FolderInfo) hotseatItemInfo;
                     FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId,
@@ -193,40 +166,68 @@
                 } else {
                     hotseatView = inflate(expectedLayoutResId);
                 }
-                int iconSize = mActivityContext.getDeviceProfile().iconSizePx;
-                LayoutParams lp = new LayoutParams(iconSize, iconSize);
-                lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
-                mHotseatIconsContainer.addView(hotseatView, i, lp);
+                LayoutParams lp = new LayoutParams(mIconTouchSize, mIconTouchSize);
+                hotseatView.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
+                addView(hotseatView, nextViewIndex, lp);
             }
 
             // Apply the Hotseat ItemInfos, or hide the view if there is none for a given index.
             if (hotseatView instanceof BubbleTextView
                     && hotseatItemInfo instanceof WorkspaceItemInfo) {
-                ((BubbleTextView) hotseatView).applyFromWorkspaceItem(
-                        (WorkspaceItemInfo) hotseatItemInfo);
-                hotseatView.setOnClickListener(mIconClickListener);
-                hotseatView.setOnLongClickListener(mIconLongClickListener);
-            } else if (isFolder) {
-                hotseatView.setOnClickListener(mIconClickListener);
-                hotseatView.setOnLongClickListener(mIconLongClickListener);
-            } else {
-                hotseatView.setOnClickListener(null);
-                hotseatView.setOnLongClickListener(null);
-                hotseatView.setTag(null);
+                BubbleTextView btv = (BubbleTextView) hotseatView;
+                WorkspaceItemInfo workspaceInfo = (WorkspaceItemInfo) hotseatItemInfo;
+
+                boolean animate = btv.shouldAnimateIconChange((WorkspaceItemInfo) hotseatItemInfo);
+                btv.applyFromWorkspaceItem(workspaceInfo, animate, numViewsAnimated);
+                if (animate) {
+                    numViewsAnimated++;
+                }
             }
-            updateHotseatItemVisibility(hotseatView);
+            setClickAndLongClickListenersForIcon(hotseatView);
+            nextViewIndex++;
+        }
+        // Remove remaining views
+        while (nextViewIndex < getChildCount()) {
+            removeAndRecycle(getChildAt(nextViewIndex));
         }
     }
 
-    protected void updateHotseatItemsVisibility() {
-        for (int i = mHotseatIconsContainer.getChildCount() - 1; i >= 0; i--) {
-            updateHotseatItemVisibility(mHotseatIconsContainer.getChildAt(i));
-        }
+    /**
+     * Sets OnClickListener and OnLongClickListener for the given view.
+     */
+    public void setClickAndLongClickListenersForIcon(View icon) {
+        icon.setOnClickListener(mIconClickListener);
+        icon.setOnLongClickListener(mIconLongClickListener);
     }
 
-    private void updateHotseatItemVisibility(View hotseatView) {
-        hotseatView.setVisibility(
-                hotseatView.getTag() != null ? VISIBLE : (mAreHolesAllowed ? INVISIBLE : GONE));
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        int count = getChildCount();
+        int spaceNeeded = count * (mItemMarginLeftRight * 2 + mIconTouchSize);
+        int navSpaceNeeded = ApiWrapper.getHotseatEndOffset(getContext());
+        boolean layoutRtl = isLayoutRtl();
+        int iconEnd = right - (right - left - spaceNeeded) / 2;
+        boolean needMoreSpaceForNav = layoutRtl ?
+                navSpaceNeeded > (iconEnd - spaceNeeded) :
+                iconEnd > (right - navSpaceNeeded);
+        if (needMoreSpaceForNav) {
+            int offset = layoutRtl ?
+                    navSpaceNeeded - (iconEnd - spaceNeeded) :
+                    (right - navSpaceNeeded) - iconEnd;
+            iconEnd += offset;
+        }
+        // Layout the children
+        mIconLayoutBounds.right = iconEnd;
+        mIconLayoutBounds.top = (bottom - top - mIconTouchSize) / 2;
+        mIconLayoutBounds.bottom = mIconLayoutBounds.top + mIconTouchSize;
+        for (int i = count; i > 0; i--) {
+            View child = getChildAt(i - 1);
+            iconEnd -= mItemMarginLeftRight;
+            int iconStart = iconEnd - mIconTouchSize;
+            child.layout(iconStart, mIconLayoutBounds.top, iconEnd, mIconLayoutBounds.bottom);
+            iconEnd = iconStart - mItemMarginLeftRight;
+        }
+        mIconLayoutBounds.left = iconEnd;
     }
 
     @Override
@@ -239,8 +240,23 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
-        boolean handled = delegateTouchIfNecessary(event);
-        return super.onTouchEvent(event) || handled;
+        if (!mTouchEnabled) {
+            return true;
+        }
+        if (mIconLayoutBounds.contains((int) event.getX(), (int) event.getY())) {
+            // Don't allow long pressing between icons.
+            return true;
+        }
+        if (mControllerCallbacks.onTouchEvent(event)) {
+            int oldAction = event.getAction();
+            try {
+                event.setAction(MotionEvent.ACTION_CANCEL);
+                return super.onTouchEvent(event);
+            } finally {
+                event.setAction(oldAction);
+            }
+        }
+        return super.onTouchEvent(event);
     }
 
     public void setTouchesEnabled(boolean touchEnabled) {
@@ -248,149 +264,30 @@
     }
 
     /**
-     * User touched the Taskbar background. Determine whether the touch is close enough to a view
-     * that we should forward the touches to it.
-     * @return Whether a delegate view was chosen and it handled the touch event.
-     */
-    private boolean delegateTouchIfNecessary(MotionEvent event) {
-        final float x = event.getX();
-        final float y = event.getY();
-        if (mDelegateView == null && event.getAction() == MotionEvent.ACTION_DOWN) {
-            View delegateView = findDelegateView(x, y);
-            if (delegateView != null) {
-                mDelegateTargeted = true;
-                mDelegateView = delegateView;
-                mDelegateSlopBounds.set(mTempDelegateBounds);
-                mDelegateSlopBounds.inset(-mTouchSlop, -mTouchSlop);
-            }
-        }
-
-        boolean sendToDelegate = mDelegateTargeted;
-        boolean inBounds = true;
-        switch (event.getAction()) {
-            case MotionEvent.ACTION_MOVE:
-                inBounds = mDelegateSlopBounds.contains(x, y);
-                break;
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                mDelegateTargeted = false;
-                break;
-        }
-
-        boolean handled = false;
-        if (sendToDelegate) {
-            if (inBounds) {
-                // Offset event coordinates to be inside the target view
-                event.setLocation(mDelegateView.getWidth() / 2f, mDelegateView.getHeight() / 2f);
-            } else {
-                // Offset event coordinates to be outside the target view (in case it does
-                // something like tracking pressed state)
-                event.setLocation(-mTouchSlop * 2, -mTouchSlop * 2);
-            }
-            handled = mDelegateView.dispatchTouchEvent(event);
-            // Cleanup if this was the last event to send to the delegate.
-            if (!mDelegateTargeted) {
-                mDelegateView = null;
-            }
-        }
-        return handled;
-    }
-
-    /**
-     * Return an item whose touch bounds contain the given coordinates,
-     * or null if no such item exists.
-     *
-     * Also sets {@link #mTempDelegateBounds} to be the touch bounds of the chosen delegate view.
-     */
-    private @Nullable View findDelegateView(float x, float y) {
-        for (int i = 0; i < getChildCount(); i++) {
-            View child = getChildAt(i);
-            if (!child.isShown() || !child.isClickable()) {
-                continue;
-            }
-            int childCenterX = child.getLeft() + child.getWidth() / 2;
-            int childCenterY = child.getTop() + child.getHeight() / 2;
-            mTempDelegateBounds.set(
-                    childCenterX - mIconTouchSize / 2f,
-                    childCenterY - mIconTouchSize / 2f,
-                    childCenterX + mIconTouchSize / 2f,
-                    childCenterY + mIconTouchSize / 2f);
-            if (mTempDelegateBounds.contains(x, y)) {
-                return child;
-            }
-        }
-        return null;
-    }
-
-    /**
      * Returns whether the given MotionEvent, *in screen coorindates*, is within any Taskbar item's
      * touch bounds.
      */
     public boolean isEventOverAnyItem(MotionEvent ev) {
         getLocationOnScreen(mTempOutLocation);
-        float xInOurCoordinates = ev.getX() - mTempOutLocation[0];
-        float yInOurCoorindates = ev.getY() - mTempOutLocation[1];
-        return findDelegateView(xInOurCoordinates, yInOurCoorindates) != null;
+        int xInOurCoordinates = (int) ev.getX() - mTempOutLocation[0];
+        int yInOurCoorindates = (int) ev.getY() - mTempOutLocation[1];
+        return isShown() && mIconLayoutBounds.contains(xInOurCoordinates, yInOurCoorindates);
+    }
+
+    public Rect getIconLayoutBounds() {
+        return mIconLayoutBounds;
     }
 
     /**
-     * Add back/home/recents buttons into a single ViewGroup that will be inserted at
-     * {@param navButtonStartIndex}
+     * Returns the app icons currently shown in the taskbar.
      */
-    private void createNavButtons() {
-        LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(
-                mActivityContext.getDeviceProfile().iconSizePx,
-                mActivityContext.getDeviceProfile().iconSizePx
-        );
-        buttonParams.gravity = Gravity.CENTER;
-
-        mSystemButtonContainer.addView(mButtonProvider.getBack(), buttonParams);
-        mSystemButtonContainer.addView(mButtonProvider.getHome(), buttonParams);
-        mSystemButtonContainer.addView(mButtonProvider.getRecents(), buttonParams);
-    }
-
-    @Override
-    public boolean onDragEvent(DragEvent event) {
-        switch (event.getAction()) {
-            case DragEvent.ACTION_DRAG_STARTED:
-                mIsDraggingItem = true;
-                AbstractFloatingView.closeAllOpenViews(mActivityContext);
-                return true;
-            case DragEvent.ACTION_DRAG_ENDED:
-                mIsDraggingItem = false;
-                break;
+    public View[] getIconViews() {
+        final int count = getChildCount();
+        View[] icons = new View[count];
+        for (int i = 0; i < count; i++) {
+            icons[i] = getChildAt(i);
         }
-        return super.onDragEvent(event);
-    }
-
-    public boolean isDraggingItem() {
-        return mIsDraggingItem;
-    }
-
-    /**
-     * @return The bounding box of where the hotseat elements are relative to this TaskbarView.
-     */
-    protected RectF getHotseatBounds() {
-        RectF result;
-        mDisableRelayout = true;
-        boolean wereHolesAllowed = mAreHolesAllowed;
-        setHolesAllowedInLayoutNoAnimation(true);
-        result = new RectF(
-                mHotseatIconsContainer.getLeft(),
-                mHotseatIconsContainer.getTop(),
-                mHotseatIconsContainer.getRight(),
-                mHotseatIconsContainer.getBottom());
-        setHolesAllowedInLayoutNoAnimation(wereHolesAllowed);
-        mDisableRelayout = false;
-
-        return result;
-    }
-
-    @Override
-    public void requestLayout() {
-        if (!mDisableRelayout) {
-            super.requestLayout();
-        }
+        return icons;
     }
 
     // FolderIconParent implemented methods.
@@ -421,7 +318,7 @@
     }
 
     private View inflate(@LayoutRes int layoutResId) {
-        return mActivityContext.getLayoutInflater().inflate(layoutResId, this, false);
+        return mActivityContext.getViewCache().getView(layoutResId, mActivityContext, this);
     }
 
     @Override
@@ -429,11 +326,8 @@
         // Ignore, we just implement Insettable to draw behind system insets.
     }
 
-    public void setIconsVisibility(boolean isVisible) {
-        mHotseatIconsContainer.setVisibility(isVisible ? VISIBLE : INVISIBLE);
-    }
-
     public boolean areIconsVisible() {
-        return mHotseatIconsContainer.getVisibility() == VISIBLE;
+        // Consider the overall visibility
+        return getVisibility() == VISIBLE;
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
new file mode 100644
index 0000000..08d2a06
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -0,0 +1,346 @@
+/*
+ * 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.taskbar;
+
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.AnimatedFloat.VALUE;
+
+import android.graphics.Rect;
+import android.util.FloatProperty;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnPreDrawListener;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.MultiValueAlpha;
+import com.android.quickstep.AnimatedFloat;
+
+/**
+ * Handles properties/data collection, then passes the results to TaskbarView to render.
+ */
+public class TaskbarViewController {
+    private static final Runnable NO_OP = () -> { };
+
+    public static final int ALPHA_INDEX_HOME = 0;
+    public static final int ALPHA_INDEX_IME = 1;
+    public static final int ALPHA_INDEX_KEYGUARD = 2;
+    public static final int ALPHA_INDEX_STASH = 3;
+    public static final int ALPHA_INDEX_RECENTS_DISABLED = 4;
+    public static final int ALPHA_INDEX_NOTIFICATION_EXPANDED = 5;
+    private static final int NUM_ALPHA_CHANNELS = 6;
+
+    private final TaskbarActivityContext mActivity;
+    private final TaskbarView mTaskbarView;
+    private final MultiValueAlpha mTaskbarIconAlpha;
+    private final AnimatedFloat mTaskbarIconScaleForStash = new AnimatedFloat(this::updateScale);
+    private final AnimatedFloat mTaskbarIconTranslationYForHome = new AnimatedFloat(
+            this::updateTranslationY);
+    private final AnimatedFloat mTaskbarIconTranslationYForStash = new AnimatedFloat(
+            this::updateTranslationY);
+    private AnimatedFloat mTaskbarNavButtonTranslationY;
+
+    private final TaskbarModelCallbacks mModelCallbacks;
+
+    // Initialized in init.
+    private TaskbarControllers mControllers;
+
+    // Animation to align icons with Launcher, created lazily. This allows the controller to be
+    // active only during the animation and does not need to worry about layout changes.
+    private AnimatorPlaybackController mIconAlignControllerLazy = null;
+    private Runnable mOnControllerPreCreateCallback = NO_OP;
+
+    public TaskbarViewController(TaskbarActivityContext activity, TaskbarView taskbarView) {
+        mActivity = activity;
+        mTaskbarView = taskbarView;
+        mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, NUM_ALPHA_CHANNELS);
+        mTaskbarIconAlpha.setUpdateVisibility(true);
+        mModelCallbacks = new TaskbarModelCallbacks(activity, mTaskbarView);
+    }
+
+    public void init(TaskbarControllers controllers) {
+        mControllers = controllers;
+        mTaskbarView.init(new TaskbarViewCallbacks());
+        mTaskbarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarSize;
+
+        mTaskbarIconScaleForStash.updateValue(1f);
+
+        mModelCallbacks.init(controllers);
+        if (mActivity.isUserSetupComplete()) {
+            // Only load the callbacks if user setup is completed
+            LauncherAppState.getInstance(mActivity).getModel().addCallbacksAndLoad(mModelCallbacks);
+        }
+        mTaskbarNavButtonTranslationY =
+                controllers.navbarButtonsViewController.getTaskbarNavButtonTranslationY();
+    }
+
+    public void onDestroy() {
+        LauncherAppState.getInstance(mActivity).getModel().removeCallbacks(mModelCallbacks);
+    }
+
+    public boolean areIconsVisible() {
+        return mTaskbarView.areIconsVisible();
+    }
+
+    public MultiValueAlpha getTaskbarIconAlpha() {
+        return mTaskbarIconAlpha;
+    }
+
+    /**
+     * Should be called when the IME visibility changes, so we can make Taskbar not steal touches.
+     */
+    public void setImeIsVisible(boolean isImeVisible) {
+        mTaskbarView.setTouchesEnabled(!isImeVisible);
+    }
+
+    /**
+     * Should be called when the notification shade is expanded, so we can hide taskbar icons as
+     * well. Note that we are animating icons to appear / disappear.
+     */
+    public void setNotificationShadeIsExpanded(boolean isNotificationShadeExpanded) {
+        mTaskbarIconAlpha.getProperty(ALPHA_INDEX_NOTIFICATION_EXPANDED)
+                .animateToValue(isNotificationShadeExpanded ? 0 : 1)
+                .start();
+    }
+
+    /**
+     * Should be called when the recents button is disabled, so we can hide taskbar icons as well.
+     */
+    public void setRecentsButtonDisabled(boolean isDisabled) {
+        // TODO: check TaskbarStashController#supportsStashing(), to stash instead of setting alpha.
+        mTaskbarIconAlpha.getProperty(ALPHA_INDEX_RECENTS_DISABLED).setValue(isDisabled ? 0 : 1);
+    }
+
+    /**
+     * Sets OnClickListener and OnLongClickListener for the given view.
+     */
+    public void setClickAndLongClickListenersForIcon(View icon) {
+        mTaskbarView.setClickAndLongClickListenersForIcon(icon);
+    }
+
+    /**
+     * Adds one time pre draw listener to the taskbar view, it is called before
+     * drawing a frame and invoked only once
+     * @param listener callback that will be invoked before drawing the next frame
+     */
+    public void addOneTimePreDrawListener(Runnable listener) {
+        mTaskbarView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
+            @Override
+            public boolean onPreDraw() {
+                final ViewTreeObserver viewTreeObserver = mTaskbarView.getViewTreeObserver();
+                if (viewTreeObserver.isAlive()) {
+                    listener.run();
+                    viewTreeObserver.removeOnPreDrawListener(this);
+                }
+                return true;
+            }
+        });
+    }
+
+    public Rect getIconLayoutBounds() {
+        return mTaskbarView.getIconLayoutBounds();
+    }
+
+    public View[] getIconViews() {
+        return mTaskbarView.getIconViews();
+    }
+
+    public AnimatedFloat getTaskbarIconScaleForStash() {
+        return mTaskbarIconScaleForStash;
+    }
+
+    public AnimatedFloat getTaskbarIconTranslationYForStash() {
+        return mTaskbarIconTranslationYForStash;
+    }
+
+    /**
+     * Applies scale properties for the entire TaskbarView (rather than individual icons).
+     */
+    private void updateScale() {
+        float scale = mTaskbarIconScaleForStash.value;
+        mTaskbarView.setScaleX(scale);
+        mTaskbarView.setScaleY(scale);
+    }
+
+    private void updateTranslationY() {
+        mTaskbarView.setTranslationY(mTaskbarIconTranslationYForHome.value
+                + mTaskbarIconTranslationYForStash.value);
+    }
+
+    /**
+     * Sets the taskbar icon alignment relative to Launcher hotseat icons
+     * @param alignmentRatio [0, 1]
+     *                       0 => not aligned
+     *                       1 => fully aligned
+     */
+    public void setLauncherIconAlignment(float alignmentRatio, DeviceProfile launcherDp) {
+        if (mIconAlignControllerLazy == null) {
+            mIconAlignControllerLazy = createIconAlignmentController(launcherDp);
+        }
+        mIconAlignControllerLazy.setPlayFraction(alignmentRatio);
+        if (alignmentRatio <= 0 || alignmentRatio >= 1) {
+            // Cleanup lazy controller so that it is created again in next animation
+            mIconAlignControllerLazy = null;
+        }
+    }
+
+    /**
+     * Creates an animation for aligning the taskbar icons with the provided Launcher device profile
+     */
+    private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp) {
+        mOnControllerPreCreateCallback.run();
+        PendingAnimation setter = new PendingAnimation(100);
+        Rect hotseatPadding = launcherDp.getHotseatLayoutPadding(mActivity);
+        float scaleUp = ((float) launcherDp.iconSizePx) / mActivity.getDeviceProfile().iconSizePx;
+        int hotseatCellSize =
+                (launcherDp.availableWidthPx - hotseatPadding.left - hotseatPadding.right)
+                        / launcherDp.numShownHotseatIcons;
+
+        int offsetY = launcherDp.getTaskbarOffsetY();
+        setter.setFloat(mTaskbarIconTranslationYForHome, VALUE, -offsetY, LINEAR);
+        setter.setFloat(mTaskbarNavButtonTranslationY, VALUE, -offsetY, LINEAR);
+
+        int collapsedHeight = mActivity.getDefaultTaskbarWindowHeight();
+        int expandedHeight = Math.max(collapsedHeight,
+                mActivity.getDeviceProfile().taskbarSize + offsetY);
+        setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight(
+                anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight));
+
+        int count = mTaskbarView.getChildCount();
+        for (int i = 0; i < count; i++) {
+            View child = mTaskbarView.getChildAt(i);
+            ItemInfo info = (ItemInfo) child.getTag();
+            setter.setFloat(child, SCALE_PROPERTY, scaleUp, LINEAR);
+
+            float childCenter = (child.getLeft() + child.getRight()) / 2;
+            float hotseatIconCenter = hotseatPadding.left + hotseatCellSize * info.screenId
+                    + hotseatCellSize / 2;
+            setter.setFloat(child, ICON_TRANSLATE_X, hotseatIconCenter - childCenter, LINEAR);
+        }
+
+        AnimatorPlaybackController controller = setter.createPlaybackController();
+        mOnControllerPreCreateCallback = () -> controller.setPlayFraction(0);
+        return controller;
+    }
+
+    public void onRotationChanged(DeviceProfile deviceProfile) {
+        if (areIconsVisible()) {
+            // We only translate on rotation when on home
+            return;
+        }
+        mTaskbarNavButtonTranslationY.updateValue(-deviceProfile.getTaskbarOffsetY());
+    }
+
+    /**
+     * Returns whether the given MotionEvent, *in screen coorindates*, is within any Taskbar item's
+     * touch bounds.
+     */
+    public boolean isEventOverAnyItem(MotionEvent ev) {
+        return mTaskbarView.isEventOverAnyItem(ev);
+    }
+
+    /**
+     * Callbacks for {@link TaskbarView} to interact with its controller.
+     */
+    public class TaskbarViewCallbacks {
+        private final float mSquaredTouchSlop = Utilities.squaredTouchSlop(mActivity);
+
+        private float mDownX, mDownY;
+        private boolean mCanceledStashHint;
+
+        public View.OnClickListener getIconOnClickListener() {
+            return mActivity::onTaskbarIconClicked;
+        }
+
+        public View.OnLongClickListener getIconOnLongClickListener() {
+            return mControllers.taskbarDragController::startDragOnLongClick;
+        }
+
+        public View.OnLongClickListener getBackgroundOnLongClickListener() {
+            return view -> mControllers.taskbarStashController
+                    .updateAndAnimateIsManuallyStashedInApp(true);
+        }
+
+        /**
+         * Get the first chance to handle TaskbarView#onTouchEvent, and return whether we want to
+         * consume the touch so TaskbarView treats it as an ACTION_CANCEL.
+         */
+        public boolean onTouchEvent(MotionEvent motionEvent) {
+            final float x = motionEvent.getRawX();
+            final float y = motionEvent.getRawY();
+            switch (motionEvent.getAction()) {
+                case MotionEvent.ACTION_DOWN:
+                    mDownX = x;
+                    mDownY = y;
+                    mControllers.taskbarStashController.startStashHint(/* animateForward = */ true);
+                    mCanceledStashHint = false;
+                    break;
+                case MotionEvent.ACTION_MOVE:
+                    if (!mCanceledStashHint
+                            && squaredHypot(mDownX - x, mDownY - y) > mSquaredTouchSlop) {
+                        mControllers.taskbarStashController.startStashHint(
+                                /* animateForward= */ false);
+                        mCanceledStashHint = true;
+                        return true;
+                    }
+                    break;
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_CANCEL:
+                    if (!mCanceledStashHint) {
+                        mControllers.taskbarStashController.startStashHint(
+                                /* animateForward= */ false);
+                    }
+                    break;
+            }
+            return false;
+        }
+    }
+
+    public static final FloatProperty<View> ICON_TRANSLATE_X =
+            new FloatProperty<View>("taskbarAligmentTranslateX") {
+
+                @Override
+                public void setValue(View view, float v) {
+                    if (view instanceof BubbleTextView) {
+                        ((BubbleTextView) view).setTranslationXForTaskbarAlignmentAnimation(v);
+                    } else if (view instanceof FolderIcon) {
+                        ((FolderIcon) view).setTranslationForTaskbarAlignmentAnimation(v);
+                    } else {
+                        view.setTranslationX(v);
+                    }
+                }
+
+                @Override
+                public Float get(View view) {
+                    if (view instanceof BubbleTextView) {
+                        return ((BubbleTextView) view)
+                                .getTranslationXForTaskbarAlignmentAnimation();
+                    } else if (view instanceof FolderIcon) {
+                        return ((FolderIcon) view).getTranslationXForTaskbarAlignmentAnimation();
+                    }
+                    return view.getTranslationX();
+                }
+            };
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButton.java b/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButton.java
new file mode 100644
index 0000000..4093097
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButton.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 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.taskbar.contextual;
+
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.view.View;
+
+/**
+ * Interface of a rotation button that interacts {@link RotationButtonController}.
+ * This interface exists because of the two different styles of rotation button in Sysui,
+ * one in contextual for 3 button nav and a floating rotation button for gestural.
+ * Keeping the interface for eventual migration of floating button, so some methods are
+ * pass through to "super" while others are trivially implemented.
+ *
+ * Changes:
+ *  * Directly use AnimatedVectorDrawable instead of KeyButtonDrawable
+ */
+public interface RotationButton {
+    default void setRotationButtonController(RotationButtonController rotationButtonController) { }
+
+    default View getCurrentView() {
+        return null;
+    }
+    default void show() { }
+    default void hide() { }
+    default boolean isVisible() {
+        return false;
+    }
+
+    default void updateIcon(int lightIconColor, int darkIconColor) { }
+    default void setOnClickListener(View.OnClickListener onClickListener) { }
+    default void setOnHoverListener(View.OnHoverListener onHoverListener) { }
+    default AnimatedVectorDrawable getImageDrawable() {
+        return null;
+    }
+    default void setDarkIntensity(float darkIntensity) { }
+    default boolean acceptRotationProposal() {
+        return getCurrentView() != null;
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButtonController.java b/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButtonController.java
new file mode 100644
index 0000000..c776f16
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButtonController.java
@@ -0,0 +1,512 @@
+/*
+ * Copyright 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.taskbar.contextual;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.annotation.ColorInt;
+import android.annotation.DrawableRes;
+import android.annotation.SuppressLint;
+import android.app.StatusBarManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.IRotationWatcher;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.View;
+import android.view.WindowInsetsController;
+import android.view.WindowManagerGlobal;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
+import com.android.internal.view.RotationPolicy;
+import com.android.launcher3.R;
+import com.android.launcher3.util.DisplayController;
+import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.shared.recents.utilities.ViewRippler;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
+
+import java.util.Optional;
+
+/**
+ * Copied over from the SysUI equivalent class. Known issues/things not ported over
+ *  * When rotation button visible and in auto-hide mode, we ask auto-hide controller to
+ *    keep the navbar around longer. Will need to implement if we use auto-hide on taskbar
+ *
+ * Contains logic that deals with showing a rotate suggestion button with animation.
+ */
+public class RotationButtonController {
+
+    private static final String TAG = "StatusBar/RotationButtonController";
+    private static final int BUTTON_FADE_IN_OUT_DURATION_MS = 100;
+    private static final int NAVBAR_HIDDEN_PENDING_ICON_TIMEOUT_MS = 20000;
+
+    private static final int NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION = 3;
+
+    private final Context mContext;
+    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
+    private final UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
+    private final ViewRippler mViewRippler = new ViewRippler();
+    private final DisplayController mDisplayController;
+    private RotationButton mRotationButton;
+
+    private int mLastRotationSuggestion;
+    private boolean mPendingRotationSuggestion;
+    private boolean mHoveringRotationSuggestion;
+    private final AccessibilityManager mAccessibilityManager;
+    private final TaskStackListenerImpl mTaskStackListener;
+    private boolean mListenersRegistered = false;
+    private boolean mIsTaskbarShowing;
+    @SuppressLint("InlinedApi")
+    private @WindowInsetsController.Behavior
+    int mBehavior = WindowInsetsController.BEHAVIOR_DEFAULT;
+    private boolean mSkipOverrideUserLockPrefsOnce;
+    private final int mLightIconColor;
+    private final int mDarkIconColor;
+    private int mIconResId = R.drawable.ic_sysbar_rotate_button_ccw_start_90;
+
+    private final Runnable mRemoveRotationProposal =
+            () -> setRotateSuggestionButtonState(false /* visible */);
+    private final Runnable mCancelPendingRotationProposal =
+            () -> mPendingRotationSuggestion = false;
+    private Animator mRotateHideAnimator;
+
+
+    private final IRotationWatcher.Stub mRotationWatcher = new IRotationWatcher.Stub() {
+        @Override
+        public void onRotationChanged(final int rotation) {
+            // We need this to be scheduled as early as possible to beat the redrawing of
+            // window in response to the orientation change.
+            mMainThreadHandler.postAtFrontOfQueue(() -> {
+                // If the screen rotation changes while locked, potentially update lock to flow with
+                // new screen rotation and hide any showing suggestions.
+                if (isRotationLocked()) {
+                    if (shouldOverrideUserLockPrefs(rotation)) {
+                        setRotationLockedAtAngle(rotation);
+                    }
+                    setRotateSuggestionButtonState(false /* visible */, true /* forced */);
+                }
+            });
+        }
+    };
+
+    /**
+     * Determines if rotation suggestions disabled2 flag exists in flag
+     * @param disable2Flags see if rotation suggestion flag exists in this flag
+     * @return whether flag exists
+     */
+    static boolean hasDisable2RotateSuggestionFlag(int disable2Flags) {
+        return (disable2Flags & StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS) != 0;
+    }
+
+    public RotationButtonController(Context context, @ColorInt int lightIconColor,
+            @ColorInt int darkIconColor) {
+        mContext = context;
+        mLightIconColor = lightIconColor;
+        mDarkIconColor = darkIconColor;
+
+        mAccessibilityManager = AccessibilityManager.getInstance(context);
+        mTaskStackListener = new TaskStackListenerImpl();
+        mDisplayController = DisplayController.INSTANCE.get(context);
+    }
+
+    public void setRotationButton(RotationButton rotationButton) {
+        mRotationButton = rotationButton;
+        mRotationButton.setRotationButtonController(this);
+        mRotationButton.setOnClickListener(this::onRotateSuggestionClick);
+        mRotationButton.setOnHoverListener(this::onRotateSuggestionHover);
+    }
+
+    public void init() {
+        registerListeners();
+        if (mContext.getDisplay().getDisplayId() != DEFAULT_DISPLAY) {
+            // Currently there is no accelerometer sensor on non-default display, disable fixed
+            // rotation for non-default display
+            onDisable2FlagChanged(StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS);
+        }
+    }
+
+    public void onDestroy() {
+        unregisterListeners();
+    }
+
+    private void registerListeners() {
+        if (mListenersRegistered) {
+            return;
+        }
+
+        mListenersRegistered = true;
+        try {
+            WindowManagerGlobal.getWindowManagerService()
+                    .watchRotation(mRotationWatcher, DEFAULT_DISPLAY);
+        } catch (IllegalArgumentException e) {
+            mListenersRegistered = false;
+            Log.w(TAG, "RegisterListeners for the display failed");
+        } catch (RemoteException e) {
+            Log.e(TAG, "RegisterListeners caught a RemoteException", e);
+            return;
+        }
+
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
+    }
+
+    void unregisterListeners() {
+        if (!mListenersRegistered) {
+            return;
+        }
+
+        mListenersRegistered = false;
+        try {
+            WindowManagerGlobal.getWindowManagerService().removeRotationWatcher(mRotationWatcher);
+        } catch (RemoteException e) {
+            Log.e(TAG, "UnregisterListeners caught a RemoteException", e);
+            return;
+        }
+
+        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
+    }
+
+    void setRotationLockedAtAngle(int rotationSuggestion) {
+        RotationPolicy.setRotationLockAtAngle(mContext, true, rotationSuggestion);
+    }
+
+    public boolean isRotationLocked() {
+        return RotationPolicy.isRotationLocked(mContext);
+    }
+
+    public void setRotateSuggestionButtonState(boolean visible) {
+        setRotateSuggestionButtonState(visible, false /* force */);
+    }
+
+    void setRotateSuggestionButtonState(final boolean visible, final boolean force) {
+        // At any point the button can become invisible because an a11y service became active.
+        // Similarly, a call to make the button visible may be rejected because an a11y service is
+        // active. Must account for this.
+        // Rerun a show animation to indicate change but don't rerun a hide animation
+        if (!visible && !mRotationButton.isVisible()) return;
+
+        final View view = mRotationButton.getCurrentView();
+        if (view == null) return;
+
+        final AnimatedVectorDrawable currentDrawable = mRotationButton.getImageDrawable();
+        if (currentDrawable == null) return;
+
+        // Clear any pending suggestion flag as it has either been nullified or is being shown
+        mPendingRotationSuggestion = false;
+        mMainThreadHandler.removeCallbacks(mCancelPendingRotationProposal);
+
+        // Handle the visibility change and animation
+        if (visible) { // Appear and change (cannot force)
+            // Stop and clear any currently running hide animations
+            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
+                mRotateHideAnimator.cancel();
+            }
+            mRotateHideAnimator = null;
+
+            // Reset the alpha if any has changed due to hide animation
+            view.setAlpha(1f);
+
+            // Run the rotate icon's animation if it has one
+            currentDrawable.reset();
+            currentDrawable.start();
+
+            // TODO(b/187754252): No idea why this doesn't work. If we remove the "false"
+            //  we see the animation show the pressed state... but it only shows the first time.
+            if (!isRotateSuggestionIntroduced()) mViewRippler.start(view);
+
+            // Set visibility unless a11y service is active.
+            mRotationButton.show();
+        } else { // Hide
+            mViewRippler.stop(); // Prevent any pending ripples, force hide or not
+
+            if (force) {
+                // If a hide animator is running stop it and make invisible
+                if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
+                    mRotateHideAnimator.pause();
+                }
+                mRotationButton.hide();
+                return;
+            }
+
+            // Don't start any new hide animations if one is running
+            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return;
+
+            ObjectAnimator fadeOut = ObjectAnimator.ofFloat(view, "alpha", 0f);
+            fadeOut.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS);
+            fadeOut.setInterpolator(LINEAR);
+            fadeOut.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mRotationButton.hide();
+                }
+            });
+
+            mRotateHideAnimator = fadeOut;
+            fadeOut.start();
+        }
+    }
+
+    void setDarkIntensity(float darkIntensity) {
+        mRotationButton.setDarkIntensity(darkIntensity);
+    }
+
+    public void onRotationProposal(int rotation, boolean isValid) {
+        int windowRotation = mDisplayController.getInfo().rotation;
+
+        if (!mRotationButton.acceptRotationProposal()) {
+            return;
+        }
+
+        // This method will be called on rotation suggestion changes even if the proposed rotation
+        // is not valid for the top app. Use invalid rotation choices as a signal to remove the
+        // rotate button if shown.
+        if (!isValid) {
+            setRotateSuggestionButtonState(false /* visible */);
+            return;
+        }
+
+        // If window rotation matches suggested rotation, remove any current suggestions
+        if (rotation == windowRotation) {
+            mMainThreadHandler.removeCallbacks(mRemoveRotationProposal);
+            setRotateSuggestionButtonState(false /* visible */);
+            return;
+        }
+
+        // Prepare to show the navbar icon by updating the icon style to change anim params
+        mLastRotationSuggestion = rotation; // Remember rotation for click
+        final boolean rotationCCW = Utilities.isRotationAnimationCCW(windowRotation, rotation);
+        if (windowRotation == Surface.ROTATION_0 || windowRotation == Surface.ROTATION_180) {
+            mIconResId = rotationCCW
+                    ? R.drawable.ic_sysbar_rotate_button_ccw_start_90
+                    : R.drawable.ic_sysbar_rotate_button_cw_start_90;
+        } else { // 90 or 270
+            mIconResId = rotationCCW
+                    ? R.drawable.ic_sysbar_rotate_button_ccw_start_0
+                    : R.drawable.ic_sysbar_rotate_button_ccw_start_0;
+        }
+        mRotationButton.updateIcon(mLightIconColor, mDarkIconColor);
+
+        if (canShowRotationButton()) {
+            // The navbar is visible / it's in visual immersive mode, so show the icon right away
+            showAndLogRotationSuggestion();
+        } else {
+            // If the navbar isn't shown, flag the rotate icon to be shown should the navbar become
+            // visible given some time limit.
+            mPendingRotationSuggestion = true;
+            mMainThreadHandler.removeCallbacks(mCancelPendingRotationProposal);
+            mMainThreadHandler.postDelayed(mCancelPendingRotationProposal,
+                    NAVBAR_HIDDEN_PENDING_ICON_TIMEOUT_MS);
+        }
+    }
+
+    public void onDisable2FlagChanged(int state2) {
+        final boolean rotateSuggestionsDisabled = hasDisable2RotateSuggestionFlag(state2);
+        if (rotateSuggestionsDisabled) onRotationSuggestionsDisabled();
+    }
+
+    public void onBehaviorChanged(int displayId, @WindowInsetsController.Behavior int behavior) {
+        if (DEFAULT_DISPLAY != displayId) {
+            return;
+        }
+
+        if (mBehavior != behavior) {
+            mBehavior = behavior;
+            showPendingRotationButtonIfNeeded();
+        }
+    }
+
+    public void onTaskBarVisibilityChange(boolean showing) {
+        if (mIsTaskbarShowing != showing) {
+            mIsTaskbarShowing = showing;
+            showPendingRotationButtonIfNeeded();
+        }
+    }
+
+    private void showPendingRotationButtonIfNeeded() {
+        if (canShowRotationButton() && mPendingRotationSuggestion) {
+            showAndLogRotationSuggestion();
+        }
+    }
+
+    /** Return true when either the task bar is visible or it's in visual immersive mode. */
+    @SuppressLint("InlinedApi")
+    private boolean canShowRotationButton() {
+        return mIsTaskbarShowing || mBehavior == WindowInsetsController.BEHAVIOR_DEFAULT;
+    }
+
+    public @DrawableRes
+    int getIconResId() {
+        return mIconResId;
+    }
+
+    public @ColorInt int getLightIconColor() {
+        return mLightIconColor;
+    }
+
+    public @ColorInt int getDarkIconColor() {
+        return mDarkIconColor;
+    }
+
+    private void onRotateSuggestionClick(View v) {
+        mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_ACCEPTED);
+        incrementNumAcceptedRotationSuggestionsIfNeeded();
+        setRotationLockedAtAngle(mLastRotationSuggestion);
+    }
+
+    private boolean onRotateSuggestionHover(View v, MotionEvent event) {
+        final int action = event.getActionMasked();
+        mHoveringRotationSuggestion = (action == MotionEvent.ACTION_HOVER_ENTER)
+                || (action == MotionEvent.ACTION_HOVER_MOVE);
+        rescheduleRotationTimeout(true /* reasonHover */);
+        return false; // Must return false so a11y hover events are dispatched correctly.
+    }
+
+    private void onRotationSuggestionsDisabled() {
+        // Immediately hide the rotate button and clear any planned removal
+        setRotateSuggestionButtonState(false /* visible */, true /* force */);
+        mMainThreadHandler.removeCallbacks(mRemoveRotationProposal);
+    }
+
+    private void showAndLogRotationSuggestion() {
+        setRotateSuggestionButtonState(true /* visible */);
+        rescheduleRotationTimeout(false /* reasonHover */);
+        mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_SHOWN);
+    }
+
+    /**
+     * Makes {@link #shouldOverrideUserLockPrefs} always return {@code false} once. It is used to
+     * avoid losing original user rotation when display rotation is changed by entering the fixed
+     * orientation overview.
+     */
+    void setSkipOverrideUserLockPrefsOnce() {
+        mSkipOverrideUserLockPrefsOnce = true;
+    }
+
+    private boolean shouldOverrideUserLockPrefs(final int rotation) {
+        if (mSkipOverrideUserLockPrefsOnce) {
+            mSkipOverrideUserLockPrefsOnce = false;
+            return false;
+        }
+        // Only override user prefs when returning to the natural rotation (normally portrait).
+        // Don't let apps that force landscape or 180 alter user lock.
+        return rotation == NATURAL_ROTATION;
+    }
+
+    private void rescheduleRotationTimeout(final boolean reasonHover) {
+        // May be called due to a new rotation proposal or a change in hover state
+        if (reasonHover) {
+            // Don't reschedule if a hide animator is running
+            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return;
+            // Don't reschedule if not visible
+            if (!mRotationButton.isVisible()) return;
+        }
+
+        // Stop any pending removal
+        mMainThreadHandler.removeCallbacks(mRemoveRotationProposal);
+        // Schedule timeout
+        mMainThreadHandler.postDelayed(mRemoveRotationProposal,
+                computeRotationProposalTimeout());
+    }
+
+    private int computeRotationProposalTimeout() {
+        return mAccessibilityManager.getRecommendedTimeoutMillis(
+                mHoveringRotationSuggestion ? 16000 : 5000,
+                AccessibilityManager.FLAG_CONTENT_CONTROLS);
+    }
+
+    private boolean isRotateSuggestionIntroduced() {
+        ContentResolver cr = mContext.getContentResolver();
+        return Settings.Secure.getInt(cr, Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED, 0)
+                >= NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION;
+    }
+
+    private void incrementNumAcceptedRotationSuggestionsIfNeeded() {
+        // Get the number of accepted suggestions
+        ContentResolver cr = mContext.getContentResolver();
+        final int numSuggestions = Settings.Secure.getInt(cr,
+                Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED, 0);
+
+        // Increment the number of accepted suggestions only if it would change intro mode
+        if (numSuggestions < NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION) {
+            Settings.Secure.putInt(cr, Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED,
+                    numSuggestions + 1);
+        }
+    }
+
+    private class TaskStackListenerImpl extends TaskStackChangeListener {
+        // Invalidate any rotation suggestion on task change or activity orientation change
+        // Note: all callbacks happen on main thread
+
+        @Override
+        public void onTaskStackChanged() {
+            setRotateSuggestionButtonState(false /* visible */);
+        }
+
+        @Override
+        public void onTaskRemoved(int taskId) {
+            setRotateSuggestionButtonState(false /* visible */);
+        }
+
+        @Override
+        public void onTaskMovedToFront(int taskId) {
+            setRotateSuggestionButtonState(false /* visible */);
+        }
+
+        @Override
+        public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) {
+            // Only hide the icon if the top task changes its requestedOrientation
+            // Launcher can alter its requestedOrientation while it's not on top, don't hide on this
+            Optional.ofNullable(ActivityManagerWrapper.getInstance())
+                    .map(ActivityManagerWrapper::getRunningTask)
+                    .ifPresent(a -> {
+                        if (a.id == taskId) setRotateSuggestionButtonState(false /* visible */);
+                    });
+        }
+    }
+
+    enum RotationButtonEvent implements UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "The rotation button was shown")
+        ROTATION_SUGGESTION_SHOWN(206),
+        @UiEvent(doc = "The rotation button was clicked")
+        ROTATION_SUGGESTION_ACCEPTED(207);
+
+        private final int mId;
+        RotationButtonEvent(int id) {
+            mId = id;
+        }
+        @Override public int getId() {
+            return mId;
+        }
+    }
+}
+
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index 76a5782..aa26645 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -17,10 +17,15 @@
 package com.android.launcher3.uioverrides;
 
 import android.app.Person;
+import android.content.Context;
 import android.content.pm.ShortcutInfo;
+import android.content.res.Resources;
 import android.view.Display;
 
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SysUINavigationMode.Mode;
 
 public class ApiWrapper {
 
@@ -37,4 +42,31 @@
     public static boolean isInternalDisplay(Display display) {
         return display.getType() == Display.TYPE_INTERNAL;
     }
+
+    /**
+     * Returns a unique ID representing the display
+     */
+    public static String getUniqueId(Display display) {
+        return display.getUniqueId();
+    }
+
+    /**
+     * Returns the minimum space that should be left empty at the end of hotseat
+     */
+    public static int getHotseatEndOffset(Context context) {
+        if (SysUINavigationMode.INSTANCE.get(context).getMode() == Mode.THREE_BUTTONS) {
+            Resources res = context.getResources();
+            /*
+            * 3 nav buttons +
+            * Little space at the end for contextual buttons +
+            * Little space between icons and nav buttons
+            */
+            return 3 * res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
+                    + res.getDimensionPixelSize(R.dimen.taskbar_contextual_button_margin)
+                    + res.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing);
+        } else {
+            return 0;
+        }
+
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 1d52315..2fa8b07 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -16,7 +16,10 @@
 
 package com.android.launcher3.uioverrides;
 
+import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
 import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
+import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
+import static com.android.launcher3.anim.Interpolators.INSTANT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL;
@@ -70,6 +73,8 @@
         getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness());
         RECENTS_GRID_PROGRESS.set(mRecentsView,
                 state.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f);
+
+        applySplitScrollOffset(state);
     }
 
     @Override
@@ -112,8 +117,19 @@
                 mRecentsView, getTaskModalnessProperty(),
                 toState.getOverviewModalness(),
                 config.getInterpolator(ANIM_OVERVIEW_MODAL, LINEAR));
-        setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS,
-                toState.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f, LINEAR);
+        boolean showAsGrid = toState.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile());
+        setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS, showAsGrid ? 1f : 0f,
+                showAsGrid ? INSTANT : FINAL_FRAME);
+
+        applySplitScrollOffset(toState);
+    }
+
+    private void applySplitScrollOffset(@NonNull final LauncherState state) {
+        if (state == OVERVIEW_SPLIT_SELECT) {
+            mRecentsView.applySplitPrimaryScrollOffset();
+        } else {
+            mRecentsView.resetSplitPrimaryScrollOffset();
+        }
     }
 
     abstract FloatProperty getTaskModalnessProperty();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java b/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java
index c115bbb..c46809a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java
@@ -17,21 +17,17 @@
 package com.android.launcher3.uioverrides;
 
 import android.annotation.TargetApi;
-import android.content.Context;
 import android.os.Build;
 import android.provider.DeviceConfig;
 
 import com.android.launcher3.config.FeatureFlags.DebugFlag;
 
-import java.util.ArrayList;
-
 @TargetApi(Build.VERSION_CODES.P)
 public class DeviceFlag extends DebugFlag {
 
     public static final String NAMESPACE_LAUNCHER = "launcher";
 
     private final boolean mDefaultValueInCode;
-    ArrayList<Runnable> mListeners;
 
     public DeviceFlag(String key, boolean defaultValue, String description) {
         super(key, getDeviceValue(key, defaultValue), description);
@@ -44,53 +40,11 @@
     }
 
     @Override
-    public void initialize(Context context) {
-        super.initialize(context);
-        if (mListeners == null) {
-            mListeners = new ArrayList<>();
-            registerDeviceConfigChangedListener(context);
-        }
-    }
-
-    @Override
-    public void addChangeListener(Context context, Runnable r) {
-        if (mListeners == null) {
-            initialize(context);
-        }
-        mListeners.add(r);
-    }
-
-    @Override
-    public void removeChangeListener(Runnable r) {
-        if (mListeners == null) {
-            return;
-        }
-        mListeners.remove(r);
-    }
-
-    @Override
     public boolean get() {
         // Override this method in order to let Robolectric ShadowDeviceFlag to stub it.
         return super.get();
     }
 
-    private void registerDeviceConfigChangedListener(Context context) {
-        DeviceConfig.addOnPropertiesChangedListener(
-                NAMESPACE_LAUNCHER,
-                context.getMainExecutor(),
-                properties -> {
-                    if (!NAMESPACE_LAUNCHER.equals(properties.getNamespace())
-                            || !properties.getKeyset().contains(key)) {
-                        return;
-                    }
-                    defaultValue = getDeviceValue(key, mDefaultValueInCode);
-                    initialize(context);
-                    for (Runnable r: mListeners) {
-                        r.run();
-                    }
-                });
-    }
-
     protected static boolean getDeviceValue(String key, boolean defaultValue) {
         return DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, key, defaultValue);
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index d839a36..ee6e8ce 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -15,6 +15,16 @@
  */
 package com.android.launcher3.uioverrides;
 
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ArgbEvaluator;
+import android.animation.Keyframe;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.BlurMaskFilter;
 import android.graphics.Canvas;
@@ -23,8 +33,10 @@
 import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.os.Process;
 import android.util.AttributeSet;
+import android.util.FloatProperty;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
 
@@ -35,6 +47,8 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
+import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.GraphicsUtils;
 import com.android.launcher3.icons.IconNormalizer;
 import com.android.launcher3.icons.LauncherIcons;
@@ -45,6 +59,10 @@
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.DoubleShadowBubbleTextView;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
 /**
  * A BubbleTextView with a ring around it's drawable
  */
@@ -53,6 +71,9 @@
     private static final int RING_SHADOW_COLOR = 0x99000000;
     private static final float RING_EFFECT_RATIO = 0.095f;
 
+    private static final long ICON_CHANGE_ANIM_DURATION = 360;
+    private static final long ICON_CHANGE_ANIM_STAGGER = 50;
+
     boolean mIsDrawingDot = false;
     private final DeviceProfile mDeviceProfile;
     private final Paint mIconRingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -67,6 +88,25 @@
     private int mPlateColor;
     boolean mDrawForDrag = false;
 
+    // Used for the "slot-machine" education animation.
+    private List<Drawable> mSlotMachineIcons;
+    private Animator mSlotMachineAnim;
+    private float mSlotMachineIconTranslationY;
+
+    private static final FloatProperty<PredictedAppIcon> SLOT_MACHINE_TRANSLATION_Y =
+            new FloatProperty<PredictedAppIcon>("slotMachineTranslationY") {
+        @Override
+        public void setValue(PredictedAppIcon predictedAppIcon, float transY) {
+            predictedAppIcon.mSlotMachineIconTranslationY = transY;
+            predictedAppIcon.invalidate();
+        }
+
+        @Override
+        public Float get(PredictedAppIcon predictedAppIcon) {
+            return predictedAppIcon.mSlotMachineIconTranslationY;
+        }
+    };
+
     public PredictedAppIcon(Context context) {
         this(context, null, 0);
     }
@@ -88,15 +128,38 @@
     @Override
     public void onDraw(Canvas canvas) {
         int count = canvas.save();
+        boolean isSlotMachineAnimRunning = mSlotMachineAnim != null;
         if (!mIsPinned) {
             drawEffect(canvas);
+            if (isSlotMachineAnimRunning) {
+                // Clip to to outside of the ring during the slot machine animation.
+                canvas.clipPath(mRingPath);
+            }
             canvas.translate(getWidth() * RING_EFFECT_RATIO, getHeight() * RING_EFFECT_RATIO);
             canvas.scale(1 - 2 * RING_EFFECT_RATIO, 1 - 2 * RING_EFFECT_RATIO);
         }
-        super.onDraw(canvas);
+        if (isSlotMachineAnimRunning) {
+            drawSlotMachineIcons(canvas);
+        } else {
+            super.onDraw(canvas);
+        }
         canvas.restoreToCount(count);
     }
 
+    private void drawSlotMachineIcons(Canvas canvas) {
+        canvas.translate((getWidth() - getIconSize()) / 2f,
+                (getHeight() - getIconSize()) / 2f + mSlotMachineIconTranslationY);
+        for (Drawable icon : mSlotMachineIcons) {
+            icon.setBounds(0, 0, getIconSize(), getIconSize());
+            icon.draw(canvas);
+            canvas.translate(0, getSlotMachineIconPlusSpacingSize());
+        }
+    }
+
+    private float getSlotMachineIconPlusSpacingSize() {
+        return getIconSize() + getOutlineOffsetY();
+    }
+
     @Override
     protected void drawDotIfNecessary(Canvas canvas) {
         mIsDrawingDot = true;
@@ -109,9 +172,17 @@
     }
 
     @Override
-    public void applyFromWorkspaceItem(WorkspaceItemInfo info) {
-        super.applyFromWorkspaceItem(info);
-        mPlateColor = ColorUtils.setAlphaComponent(mDotParams.color, 200);
+    public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean animate, int staggerIndex) {
+        // Create the slot machine animation first, since it uses the current icon to start.
+        Animator slotMachineAnim = animate
+                ? createSlotMachineAnim(Collections.singletonList(info.bitmap), false)
+                : null;
+        super.applyFromWorkspaceItem(info, animate, staggerIndex);
+        int oldPlateColor = mPlateColor;
+        int newPlateColor = ColorUtils.setAlphaComponent(mDotParams.color, 200);
+        if (!animate) {
+            mPlateColor = newPlateColor;
+        }
         if (mIsPinned) {
             setContentDescription(info.contentDescription);
         } else {
@@ -119,6 +190,76 @@
                     getContext().getString(R.string.hotseat_prediction_content_description,
                             info.contentDescription));
         }
+
+        if (animate) {
+            ValueAnimator plateColorAnim = ValueAnimator.ofObject(new ArgbEvaluator(),
+                    oldPlateColor, newPlateColor);
+            plateColorAnim.addUpdateListener(valueAnimator -> {
+                mPlateColor = (int) valueAnimator.getAnimatedValue();
+                invalidate();
+            });
+            AnimatorSet changeIconAnim = new AnimatorSet();
+            if (slotMachineAnim != null) {
+                changeIconAnim.play(slotMachineAnim);
+            }
+            changeIconAnim.play(plateColorAnim);
+            changeIconAnim.setStartDelay(staggerIndex * ICON_CHANGE_ANIM_STAGGER);
+            changeIconAnim.setDuration(ICON_CHANGE_ANIM_DURATION).start();
+        }
+    }
+
+    /**
+     * Returns an Animator that translates the given icons in a "slot-machine" fashion, beginning
+     * and ending with the original icon.
+     */
+    public @Nullable Animator createSlotMachineAnim(List<BitmapInfo> iconsToAnimate) {
+        return createSlotMachineAnim(iconsToAnimate, true);
+    }
+
+    /**
+     * Returns an Animator that translates the given icons in a "slot-machine" fashion, beginning
+     * with the original icon, then cycling through the given icons, optionally ending back with
+     * the original icon.
+     * @param endWithOriginalIcon Whether we should land back on the icon we started with, rather
+     *                            than the last item in iconsToAnimate.
+     */
+    public @Nullable Animator createSlotMachineAnim(List<BitmapInfo> iconsToAnimate,
+            boolean endWithOriginalIcon) {
+        if (mIsPinned || iconsToAnimate == null || iconsToAnimate.isEmpty()) {
+            return null;
+        }
+        if (mSlotMachineAnim != null) {
+            mSlotMachineAnim.end();
+        }
+
+        // Bookend the other animating icons with the original icon on both ends.
+        mSlotMachineIcons = new ArrayList<>(iconsToAnimate.size() + 2);
+        mSlotMachineIcons.add(getIcon());
+        iconsToAnimate.stream()
+                .map(iconInfo -> iconInfo.newThemedIcon(mContext))
+                .forEach(mSlotMachineIcons::add);
+        if (endWithOriginalIcon) {
+            mSlotMachineIcons.add(getIcon());
+        }
+
+        float finalTrans = -getSlotMachineIconPlusSpacingSize() * (mSlotMachineIcons.size() - 1);
+        Keyframe[] keyframes = new Keyframe[] {
+                Keyframe.ofFloat(0f, 0f),
+                Keyframe.ofFloat(0.82f, finalTrans - getOutlineOffsetY() / 2f), // Overshoot
+                Keyframe.ofFloat(1f, finalTrans) // Ease back into the final position
+        };
+        keyframes[1].setInterpolator(ACCEL_DEACCEL);
+        keyframes[2].setInterpolator(ACCEL_DEACCEL);
+
+        mSlotMachineAnim = ObjectAnimator.ofPropertyValuesHolder(this,
+                PropertyValuesHolder.ofKeyframe(SLOT_MACHINE_TRANSLATION_Y, keyframes));
+        mSlotMachineAnim.addListener(AnimatorListeners.forEndCallback(() -> {
+            mSlotMachineIcons = null;
+            mSlotMachineAnim = null;
+            mSlotMachineIconTranslationY = 0;
+            invalidate();
+        }));
+        return mSlotMachineAnim;
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index ec9893c..9050ddc 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -52,10 +52,10 @@
 import com.android.launcher3.appprediction.PredictionRowView;
 import com.android.launcher3.hybridhotseat.HotseatPredictionController;
 import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.logging.StatsLogManager.StatsLogger;
 import com.android.launcher3.model.BgDataModel.FixedContainerItems;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
 import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory;
@@ -68,7 +68,9 @@
 import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarTouchController;
+import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.OnboardingPrefs;
+import com.android.launcher3.util.PendingRequestArgs;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
@@ -84,7 +86,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.List;
 import java.util.Objects;
 import java.util.stream.Stream;
 
@@ -107,7 +108,8 @@
     }
 
     @Override
-    protected void logAppLaunch(ItemInfo info, InstanceId instanceId) {
+    public void logAppLaunch(StatsLogManager statsLogManager, ItemInfo info,
+            InstanceId instanceId) {
         // If the app launch is from any of the surfaces in AllApps then add the InstanceId from
         // LiveSearchManager to recreate the AllApps session on the server side.
         if (mAllAppsSessionLogId != null && ALL_APPS.equals(
@@ -115,8 +117,7 @@
             instanceId = mAllAppsSessionLogId;
         }
 
-        StatsLogger logger = getStatsLogManager()
-                .logger().withItemInfo(info).withInstanceId(instanceId);
+        StatsLogger logger = statsLogManager.logger().withItemInfo(info).withInstanceId(instanceId);
 
         if (mAllAppsPredictions != null
                 && (info.itemType == ITEM_TYPE_APPLICATION
@@ -140,6 +141,15 @@
     }
 
     @Override
+    protected void completeAddShortcut(Intent data, int container, int screenId, int cellX,
+            int cellY, PendingRequestArgs args) {
+        if (container == CONTAINER_HOTSEAT) {
+            mHotseatPredictionController.onDeferredDrop(cellX, cellY);
+        }
+        super.completeAddShortcut(data, container, screenId, cellX, cellY, args);
+    }
+
+    @Override
     protected LauncherAccessibilityDelegate createAccessibilityDelegate() {
         return new QuickstepAccessibilityDelegate(this);
     }
@@ -166,7 +176,11 @@
     public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
         // Only pause is taskbar controller is not present
         mHotseatPredictionController.setPauseUIUpdate(getTaskbarUIController() == null);
-        return super.startActivitySafely(v, intent, item);
+        boolean started = super.startActivitySafely(v, intent, item);
+        if (getTaskbarUIController() == null && !started) {
+            mHotseatPredictionController.setPauseUIUpdate(false);
+        }
+        return started;
     }
 
     @Override
@@ -231,12 +245,9 @@
     }
 
     @Override
-    public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) {
-        super.bindWorkspaceItemsChanged(updated);
-        if (getTaskbarUIController() != null && updated.stream()
-                .filter(w -> w.container == CONTAINER_HOTSEAT).findFirst().isPresent()) {
-            getTaskbarUIController().onHotseatUpdated();
-        }
+    public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher) {
+        super.bindWorkspaceComponentsRemoved(matcher);
+        mHotseatPredictionController.onModelItemsRemoved(matcher);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 996d36a..1f744e1 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -18,13 +18,11 @@
 import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
 import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS;
 import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
-import static com.android.launcher3.LauncherState.SPLIT_PLACHOLDER_VIEW;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
 import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
 import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
 import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
-import static com.android.quickstep.views.SplitPlaceholderView.ALPHA_FLOAT;
 import static com.android.quickstep.views.TaskView.FLAG_UPDATE_ALL;
 
 import android.annotation.TargetApi;
@@ -106,16 +104,10 @@
         float clearAllButtonAlpha = state.areElementsVisible(mLauncher, CLEAR_ALL_BUTTON) ? 1 : 0;
         propertySetter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
                 clearAllButtonAlpha, LINEAR);
-        float overviewButtonAlpha = state.areElementsVisible(mLauncher, OVERVIEW_ACTIONS)
-                && mRecentsView.shouldShowOverviewActionsForState(state) ? 1 : 0;
+        float overviewButtonAlpha = state.areElementsVisible(mLauncher, OVERVIEW_ACTIONS) ? 1 : 0;
         propertySetter.setFloat(mLauncher.getActionsView().getVisibilityAlpha(),
                 MultiValueAlpha.VALUE, overviewButtonAlpha, config.getInterpolator(
                         ANIM_OVERVIEW_ACTIONS_FADE, LINEAR));
-
-        float splitPlaceholderAlpha = state.areElementsVisible(mLauncher, SPLIT_PLACHOLDER_VIEW) ?
-                0.85f : 0;
-        propertySetter.setFloat(mRecentsView.getSplitPlaceholder(), ALPHA_FLOAT,
-                splitPlaceholderAlpha, LINEAR);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
deleted file mode 100644
index d14e8ef..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
+++ /dev/null
@@ -1,52 +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.uioverrides.plugins;
-
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-
-import android.content.Context;
-import android.os.Looper;
-
-import com.android.launcher3.Utilities;
-import com.android.systemui.shared.plugins.PluginInitializer;
-
-public class PluginInitializerImpl implements PluginInitializer {
-    @Override
-    public Looper getBgLooper() {
-        return MODEL_EXECUTOR.getLooper();
-    }
-
-    @Override
-    public void onPluginManagerInit() {
-    }
-
-    @Override
-    public String[] getWhitelistedPlugins(Context context) {
-        return new String[0];
-    }
-
-    @Override
-    public PluginEnablerImpl getPluginEnabler(Context context) {
-        return new PluginEnablerImpl(context);
-    }
-
-    @Override
-    public void handleWtfs() {
-    }
-
-    public boolean isDebuggable() {
-        return Utilities.IS_DEBUG_DEVICE;
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
index 2e422b7..df0ac7c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
@@ -16,21 +16,29 @@
 
 import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
 
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
+import android.app.NotificationManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
 
+import com.android.launcher3.Utilities;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.shared.plugins.PluginActionManager;
+import com.android.systemui.shared.plugins.PluginInstance;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.shared.plugins.PluginManagerImpl;
 import com.android.systemui.shared.plugins.PluginPrefs;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 
 public class PluginManagerWrapper {
@@ -46,21 +54,36 @@
 
     private PluginManagerWrapper(Context c) {
         mContext = c;
-        PluginInitializerImpl pluginInitializer  = new PluginInitializerImpl();
-        mPluginManager = new PluginManagerImpl(c, pluginInitializer);
-        mPluginEnabler = pluginInitializer.getPluginEnabler(c);
+        mPluginEnabler = new PluginEnablerImpl(c);
+        List<String> privilegedPlugins = Collections.emptyList();
+        PluginInstance.Factory instanceFactory = new PluginInstance.Factory(
+                getClass().getClassLoader(), new PluginInstance.InstanceFactory<>(),
+                new PluginInstance.VersionChecker(), privilegedPlugins,
+                Utilities.IS_DEBUG_DEVICE);
+        PluginActionManager.Factory instanceManagerFactory = new PluginActionManager.Factory(
+                c, c.getPackageManager(), c.getMainExecutor(), MODEL_EXECUTOR,
+                c.getSystemService(NotificationManager.class), mPluginEnabler,
+                privilegedPlugins, instanceFactory);
+
+        mPluginManager = new PluginManagerImpl(c, instanceManagerFactory,
+                Utilities.IS_DEBUG_DEVICE,
+                Optional.ofNullable(Thread.getDefaultUncaughtExceptionHandler()), mPluginEnabler,
+                new PluginPrefs(c), privilegedPlugins);
     }
 
     public PluginEnablerImpl getPluginEnabler() {
         return mPluginEnabler;
     }
 
-    public void addPluginListener(PluginListener<? extends Plugin> listener, Class<?> pluginClass) {
+    /** */
+    public <T extends Plugin> void addPluginListener(
+            PluginListener<T> listener, Class<T> pluginClass) {
         addPluginListener(listener, pluginClass, false);
     }
 
-    public void addPluginListener(PluginListener<? extends Plugin> listener, Class<?> pluginClass,
-            boolean allowMultiple) {
+    /** */
+    public <T extends Plugin> void addPluginListener(
+            PluginListener<T> listener, Class<T> pluginClass, boolean allowMultiple) {
         mPluginManager.addPluginListener(listener, pluginClass, allowMultiple);
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index f8c9fd1..c554fd0 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -69,6 +69,11 @@
     }
 
     @Override
+    public boolean isTaskbarStashed() {
+        return true;
+    }
+
+    @Override
     protected float getDepthUnchecked(Context context) {
         // The scrim fades in at approximately 50% of the swipe gesture.
         // This means that the depth should be greater than 1, in order to fully zoom out.
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index fe5a347..4984b95 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -23,6 +23,7 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.views.RecentsView;
@@ -89,6 +90,10 @@
 
     @Override
     public int getWorkspaceScrimColor(Launcher launcher) {
+        DeviceProfile dp = launcher.getDeviceProfile();
+        if (dp.isTaskbarPresentInApps) {
+            return launcher.getColor(R.color.taskbar_background);
+        }
         return Color.TRANSPARENT;
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 8c128c8..d396018 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -28,7 +28,6 @@
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
 import com.android.launcher3.Workspace;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.Themes;
 import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.util.LayoutUtils;
@@ -83,16 +82,6 @@
     }
 
     @Override
-    public float getTaskbarScale(Launcher launcher) {
-        return 1f;
-    }
-
-    @Override
-    public float getTaskbarTranslationY(Launcher launcher) {
-        return 0f;
-    }
-
-    @Override
     public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
         return new PageAlphaProvider(DEACCEL_2) {
             @Override
@@ -108,13 +97,18 @@
     }
 
     @Override
+    public boolean isTaskbarStashed() {
+        return true;
+    }
+
+    @Override
     public int getWorkspaceScrimColor(Launcher launcher) {
         return Themes.getAttrColor(launcher, R.attr.overviewScrimColor);
     }
 
     @Override
     public boolean displayOverviewTasksAsGrid(DeviceProfile deviceProfile) {
-        return deviceProfile.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get();
+        return deviceProfile.overviewShowAsGrid;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
index 6968494..d0d7f31 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
@@ -43,8 +43,7 @@
     @Override
     public float getSplitSelectTranslation(Launcher launcher) {
         RecentsView recentsView = launcher.getOverviewPanel();
-        int splitPosition = recentsView.getSplitPlaceholder().getSplitController()
-                .getActiveSplitPositionOption().mStagePosition;
+        int splitPosition = recentsView.getSplitPlaceholder().getActiveSplitStagePosition();
         if (!recentsView.shouldShiftThumbnailsForSplitSelect(splitPosition)) {
             return 0f;
         }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 283743d..ef6f53e 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -24,7 +24,7 @@
 import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
 import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
-import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
+import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
 
 import android.animation.ObjectAnimator;
@@ -38,11 +38,11 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.util.VibratorWrapper;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.util.MotionPauseDetector;
 import com.android.quickstep.util.OverviewToHomeAnim;
+import com.android.quickstep.util.VibratorWrapper;
 import com.android.quickstep.views.RecentsView;
 
 /**
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 40c3e02..ff3c517 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -41,7 +41,7 @@
 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
 import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
-import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
+import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
 import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
 import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
@@ -67,14 +67,15 @@
 import com.android.launcher3.touch.BaseSwipeDetector;
 import com.android.launcher3.touch.BothAxesSwipeDetector;
 import com.android.launcher3.util.TouchController;
-import com.android.launcher3.util.VibratorWrapper;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.util.MotionPauseDetector;
+import com.android.quickstep.util.VibratorWrapper;
 import com.android.quickstep.util.WorkspaceRevealAnim;
 import com.android.quickstep.views.LauncherRecentsView;
+import com.android.quickstep.views.RecentsView;
 
 /**
  * Handles quick switching to a recent task from the home screen. To give as much flexibility to
@@ -398,6 +399,14 @@
             nonOverviewAnim.setFloatValues(startProgress, endProgress);
             mNonOverviewAnim.dispatchOnStart();
         }
+        if (targetState == QUICK_SWITCH) {
+            // Navigating to quick switch, add scroll feedback since the first time is not
+            // considered a scroll by the RecentsView.
+            VibratorWrapper.INSTANCE.get(mLauncher).vibrate(
+                    RecentsView.SCROLL_VIBRATION_PRIMITIVE,
+                    RecentsView.SCROLL_VIBRATION_PRIMITIVE_SCALE,
+                    RecentsView.SCROLL_VIBRATION_FALLBACK);
+        }
 
         nonOverviewAnim.setDuration(Math.max(xDuration, yDuration));
         mNonOverviewAnim.setEndAction(() -> onAnimationToStateCompleted(targetState));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 180af0b..308bca6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -22,6 +22,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.os.SystemClock;
+import android.os.VibrationEffect;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.animation.Interpolator;
@@ -34,7 +35,6 @@
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.touch.BaseSwipeDetector;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
@@ -42,6 +42,7 @@
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.util.VibratorWrapper;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 
@@ -56,6 +57,12 @@
     private static final long MIN_TASK_DISMISS_ANIMATION_DURATION = 300;
     private static final long MAX_TASK_DISMISS_ANIMATION_DURATION = 600;
 
+    public static final int TASK_DISMISS_VIBRATION_PRIMITIVE =
+            Utilities.ATLEAST_R ? VibrationEffect.Composition.PRIMITIVE_TICK : -1;
+    public static final float TASK_DISMISS_VIBRATION_PRIMITIVE_SCALE = 1f;
+    public static final VibrationEffect TASK_DISMISS_VIBRATION_FALLBACK =
+            VibratorWrapper.EFFECT_TEXTURE_TICK;
+
     protected final T mActivity;
     private final SingleAxisSwipeDetector mDetector;
     private final RecentsView mRecentsView;
@@ -77,6 +84,8 @@
 
     private TaskView mTaskBeingDragged;
 
+    private boolean mIsDismissHapticRunning = false;
+
     public TaskViewTouchController(T activity) {
         mActivity = activity;
         mRecentsView = activity.getOverviewPanel();
@@ -158,26 +167,21 @@
                         mTaskBeingDragged = view;
                         int upDirection = mRecentsView.getPagedOrientationHandler()
                                 .getUpDirection(mIsRtl);
-                        if (!SysUINavigationMode.getMode(mActivity).hasGestures || (
-                                mActivity.getDeviceProfile().isTablet
-                                        && FeatureFlags.ENABLE_OVERVIEW_GRID.get())) {
-                            // Don't allow swipe down to open if we don't support swipe up
-                            // to enter overview, or when grid layout is enabled.
-                            directionsToDetectScroll = upDirection;
-                            mAllowGoingUp = true;
-                            mAllowGoingDown = false;
-                        } else {
-                            // The task can be dragged up to dismiss it,
-                            // and down to open if it's the current page.
-                            mAllowGoingUp = true;
-                            if (i == mRecentsView.getCurrentPage()) {
-                                mAllowGoingDown = true;
-                                directionsToDetectScroll = DIRECTION_BOTH;
-                            } else {
-                                mAllowGoingDown = false;
-                                directionsToDetectScroll = upDirection;
-                            }
-                        }
+
+                        // The task can be dragged up to dismiss it
+                        mAllowGoingUp = true;
+
+                        // The task can be dragged down to open it if:
+                        // - It's the current page
+                        // - We support gestures to enter overview
+                        // - It's the focused task if in grid view
+                        // - The task is snapped
+                        mAllowGoingDown = i == mRecentsView.getCurrentPage()
+                                && SysUINavigationMode.getMode(mActivity).hasGestures
+                                && (!mRecentsView.showAsGrid() || mTaskBeingDragged.isFocusedTask())
+                                && mRecentsView.isTaskInExpectedScrollPosition(i);
+
+                        directionsToDetectScroll = mAllowGoingDown ? DIRECTION_BOTH : upDirection;
                         break;
                     }
                 }
@@ -233,7 +237,8 @@
         if (goingUp) {
             currentInterpolator = Interpolators.LINEAR;
             pa = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged,
-                    true /* animateTaskView */, true /* removeTask */, maxDuration);
+                    true /* animateTaskView */, true /* removeTask */, maxDuration,
+                    false /* dismissingForSplitSelection*/);
 
             mEndDisplacement = -secondaryTaskDimension;
         } else {
@@ -339,10 +344,10 @@
             fling = false;
         }
         PagedOrientationHandler orientationHandler = mRecentsView.getPagedOrientationHandler();
+        boolean goingUp = orientationHandler.isGoingUp(velocity, mIsRtl);
         float progress = mCurrentAnimation.getProgressFraction();
         float interpolatedProgress = mCurrentAnimation.getInterpolatedProgress();
         if (fling) {
-            boolean goingUp = orientationHandler.isGoingUp(velocity, mIsRtl);
             goingToEnd = goingUp == mCurrentAnimationIsGoingUp;
         } else {
             goingToEnd = interpolatedProgress > SUCCESS_TRANSITION_PROGRESS;
@@ -362,6 +367,11 @@
         mCurrentAnimation.startWithVelocity(mActivity, goingToEnd,
                 velocity * orientationHandler.getSecondaryTranslationDirectionFactor(),
                 mEndDisplacement, animationDuration);
+        if (goingUp && goingToEnd && !mIsDismissHapticRunning) {
+            VibratorWrapper.INSTANCE.get(mActivity).vibrate(TASK_DISMISS_VIBRATION_PRIMITIVE,
+                    TASK_DISMISS_VIBRATION_PRIMITIVE_SCALE, TASK_DISMISS_VIBRATION_FALLBACK);
+            mIsDismissHapticRunning = true;
+        }
     }
 
     private void clearState() {
@@ -369,5 +379,6 @@
         mDetector.setDetectableScrollConditions(0, false);
         mTaskBeingDragged = null;
         mCurrentAnimation = null;
+        mIsDismissHapticRunning = false;
     }
 }
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index ac1772c..3ab73bb 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -36,7 +36,6 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
-import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
 import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
 import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
 import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
@@ -46,6 +45,7 @@
 import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_CANCELED;
 import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
+import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
 import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
@@ -90,15 +90,16 @@
 import com.android.launcher3.tracing.InputConsumerProto;
 import com.android.launcher3.tracing.SwipeHandlerProto;
 import com.android.launcher3.util.TraceHelper;
-import com.android.launcher3.util.VibratorWrapper;
 import com.android.launcher3.util.WindowBounds;
 import com.android.quickstep.BaseActivityInterface.AnimationFactory;
 import com.android.quickstep.GestureState.GestureEndTarget;
+import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.util.InputConsumerProxy;
 import com.android.quickstep.util.InputProxyHandlerFactory;
+import com.android.quickstep.util.LauncherSplitScreenListener;
 import com.android.quickstep.util.MotionPauseDetector;
 import com.android.quickstep.util.ProtoTracer;
 import com.android.quickstep.util.RecentsOrientedState;
@@ -106,7 +107,8 @@
 import com.android.quickstep.util.StaggeredWorkspaceAnim;
 import com.android.quickstep.util.SurfaceTransactionApplier;
 import com.android.quickstep.util.SwipePipToHomeAnimator;
-import com.android.quickstep.util.TransformParams;
+import com.android.quickstep.util.TaskViewSimulator;
+import com.android.quickstep.util.VibratorWrapper;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -212,6 +214,8 @@
 
     public static final long RECENTS_ATTACH_DURATION = 300;
 
+    private static final float MAX_QUICK_SWITCH_RECENTS_SCALE_PROGRESS = 0.07f;
+
     /**
      * Used as the page index for logging when we return to the last task at the end of the gesture.
      */
@@ -220,7 +224,7 @@
     protected final TaskAnimationManager mTaskAnimationManager;
 
     // Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
-    private RunningWindowAnim mRunningWindowAnim;
+    private RunningWindowAnim[] mRunningWindowAnim;
     // Possible second animation running at the same time as mRunningWindowAnim
     private Animator mParallelRunningAnim;
     private boolean mIsMotionPaused;
@@ -251,22 +255,33 @@
 
     private SwipePipToHomeAnimator mSwipePipToHomeAnimator;
     protected boolean mIsSwipingPipToHome;
+    // TODO(b/195473090) no split PIP for now, remove once we have more clarity
+    //  can try to have RectFSpringAnim evaluate multiple rects at once
+    private final SwipePipToHomeAnimator[] mSwipePipToHomeAnimators =
+            new SwipePipToHomeAnimator[2];
+
+    // Interpolate RecentsView scale from start of quick switch scroll until this scroll threshold
+    private final float mQuickSwitchScaleScrollThreshold;
 
     public AbsSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
             TaskAnimationManager taskAnimationManager, GestureState gestureState,
             long touchTimeMs, boolean continuingLastGesture,
             InputConsumerController inputConsumer) {
-        super(context, deviceState, gestureState, new TransformParams());
+        super(context, deviceState, gestureState);
         mActivityInterface = gestureState.getActivityInterface();
         mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
         mInputConsumerProxy =
-                new InputConsumerProxy(inputConsumer, () -> {
+                new InputConsumerProxy(context,
+                        () -> mRecentsView.getPagedViewOrientedState().getRecentsActivityRotation(),
+                        inputConsumer, () -> {
                     endRunningWindowAnim(mGestureState.getEndTarget() == HOME /* cancel */);
                     endLauncherTransitionController();
                 }, new InputProxyHandlerFactory(mActivityInterface, mGestureState));
         mTaskAnimationManager = taskAnimationManager;
         mTouchTimeMs = touchTimeMs;
         mContinuingLastGesture = continuingLastGesture;
+        mQuickSwitchScaleScrollThreshold = context.getResources().getDimension(
+                R.dimen.quick_switch_scaling_scroll_threshold);
 
         initAfterSubclassConstructor();
         initStateCallbacks();
@@ -419,7 +434,8 @@
         // RecentsView never updates the display rotation until swipe-up, force update
         // RecentsOrientedState before passing to TaskViewSimulator.
         mRecentsView.updateRecentsRotation();
-        mTaskViewSimulator.setOrientationState(mRecentsView.getPagedViewOrientedState());
+        runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
+                .setOrientationState(mRecentsView.getPagedViewOrientedState()));
 
         // If we've already ended the gesture and are going home, don't prepare recents UI,
         // as that will set the state as BACKGROUND_APP, overriding the animation to NORMAL.
@@ -512,7 +528,21 @@
     }
 
     protected void notifyGestureAnimationStartToRecents() {
-        mRecentsView.onGestureAnimationStart(mGestureState.getRunningTask());
+        ActivityManager.RunningTaskInfo[] runningTasks;
+        if (mIsSwipeForStagedSplit) {
+            int[] splitTaskIds =
+                    LauncherSplitScreenListener.INSTANCE.getNoCreate().getRunningSplitTaskIds();
+            runningTasks = new ActivityManager.RunningTaskInfo[splitTaskIds.length];
+            for (int i = 0; i < splitTaskIds.length; i++) {
+                int taskId = splitTaskIds[i];
+                ActivityManager.RunningTaskInfo rti = new ActivityManager.RunningTaskInfo();
+                rti.taskId = taskId;
+                runningTasks[i] = rti;
+            }
+        } else {
+            runningTasks = new ActivityManager.RunningTaskInfo[]{mGestureState.getRunningTask()};
+        }
+        mRecentsView.onGestureAnimationStart(runningTasks);
     }
 
     private void launcherFrameDrawn() {
@@ -539,7 +569,7 @@
             @Override
             public void onMotionPauseDetected() {
                 mHasMotionEverBeenPaused = true;
-                maybeUpdateRecentsAttachedState();
+                maybeUpdateRecentsAttachedState(true/* animate */, true/* moveFocusedTask */);
                 performHapticFeedback();
             }
 
@@ -550,18 +580,24 @@
         };
     }
 
-    public void maybeUpdateRecentsAttachedState() {
+    private void maybeUpdateRecentsAttachedState() {
         maybeUpdateRecentsAttachedState(true /* animate */);
     }
 
+    private void maybeUpdateRecentsAttachedState(boolean animate) {
+        maybeUpdateRecentsAttachedState(animate, false /* moveFocusedTask */);
+    }
+
     /**
      * Determines whether to show or hide RecentsView. The window is always
      * synchronized with its corresponding TaskView in RecentsView, so if
      * RecentsView is shown, it will appear to be attached to the window.
      *
      * Note this method has no effect unless the navigation mode is NO_BUTTON.
+     * @param animate whether to animate when attaching RecentsView
+     * @param moveFocusedTask whether to move focused task to front when attaching
      */
-    private void maybeUpdateRecentsAttachedState(boolean animate) {
+    private void maybeUpdateRecentsAttachedState(boolean animate, boolean moveFocusedTask) {
         if (!mDeviceState.isFullyGesturalNavMode() || mRecentsView == null) {
             return;
         }
@@ -580,6 +616,12 @@
         } else {
             recentsAttachedToAppWindow = mHasMotionEverBeenPaused || mIsLikelyToStartNewTask;
         }
+        if (moveFocusedTask && !mAnimationFactory.hasRecentsEverAttachedToAppWindow()
+                && recentsAttachedToAppWindow) {
+            // Only move focused task if RecentsView has never been attached before, to avoid
+            // TaskView jumping to new position as we move the tasks.
+            mRecentsView.moveFocusedTaskToFront();
+        }
         mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate);
 
         // Reapply window transform throughout the attach animation, as the animation affects how
@@ -587,15 +629,15 @@
         if (animate) {
             ValueAnimator reapplyWindowTransformAnim = ValueAnimator.ofFloat(0, 1);
             reapplyWindowTransformAnim.addUpdateListener(anim -> {
-                if (mRunningWindowAnim == null) {
-                    applyWindowTransform();
+                if (mRunningWindowAnim == null || mRunningWindowAnim.length == 0) {
+                    applyScrollAndTransform();
                 }
             });
             reapplyWindowTransformAnim.setDuration(RECENTS_ATTACH_DURATION).start();
             mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED,
                     reapplyWindowTransformAnim::cancel);
         } else {
-            applyWindowTransform();
+            applyScrollAndTransform();
         }
     }
 
@@ -636,8 +678,13 @@
 
     private void onAnimatorPlaybackControllerCreated(AnimatorControllerWithResistance anim) {
         mLauncherTransitionController = anim;
-        mLauncherTransitionController.getNormalController().dispatchOnStart();
-        updateLauncherTransitionProgress();
+        mStateCallback.runOnceAtState(STATE_GESTURE_STARTED, () -> {
+            // Wait until the gesture is started (touch slop was passed) to start in sync with
+            // mWindowTransitionController. This ensures we don't hide the taskbar background when
+            // long pressing to stash it, for instance.
+            mLauncherTransitionController.getNormalController().dispatchOnStart();
+            updateLauncherTransitionProgress();
+        });
     }
 
     public Intent getLaunchIntent() {
@@ -659,7 +706,7 @@
         }
 
         updateSysUiFlags(mCurrentShift.value);
-        applyWindowTransform();
+        applyScrollAndTransform();
 
         updateLauncherTransitionProgress();
     }
@@ -669,7 +716,8 @@
                 || !canCreateNewOrUpdateExistingLauncherTransitionController()) {
             return;
         }
-        mLauncherTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor);
+        mLauncherTransitionController.setProgress(
+                Math.max(mCurrentShift.value, getScaleProgressDueToScroll()), mDragLengthFactor);
     }
 
     /**
@@ -704,24 +752,25 @@
     @Override
     public void onRecentsAnimationStart(RecentsAnimationController controller,
             RecentsAnimationTargets targets) {
+        super.onRecentsAnimationStart(controller, targets);
         ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targets.apps.length);
+        mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(targets);
         mRecentsAnimationController = controller;
         mRecentsAnimationTargets = targets;
-        mTransformParams.setTargetSet(mRecentsAnimationTargets);
-        RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(
-                mGestureState.getRunningTaskId());
-
-        if (runningTaskTarget != null) {
-            mTaskViewSimulator.setPreview(runningTaskTarget);
-        }
 
         // Only initialize the device profile, if it has not been initialized before, as in some
         // configurations targets.homeContentInsets may not be correct.
         if (mActivity == null) {
-            DeviceProfile dp = mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile();
-            if (targets.minimizedHomeBounds != null && runningTaskTarget != null) {
+            RemoteAnimationTargetCompat primaryTaskTarget = targets.apps[0];
+            // orientation state is independent of which remote target handle we use since both
+            // should be pointing to the same one. Just choose index 0 for now since that works for
+            // both split and non-split
+            RecentsOrientedState orientationState = mRemoteTargetHandles[0].getTaskViewSimulator()
+                    .getOrientationState();
+            DeviceProfile dp = orientationState.getLauncherDeviceProfile();
+            if (targets.minimizedHomeBounds != null && primaryTaskTarget != null) {
                 Rect overviewStackBounds = mActivityInterface
-                        .getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
+                        .getOverviewWindowBounds(targets.minimizedHomeBounds, primaryTaskTarget);
                 dp = dp.getMultiWindowProfile(mContext,
                         new WindowBounds(overviewStackBounds, targets.homeContentInsets));
             } else {
@@ -731,7 +780,7 @@
             dp.updateInsets(targets.homeContentInsets);
             dp.updateIsSeascape(mContext);
             initTransitionEndpoints(dp);
-            mTaskViewSimulator.getOrientationState().setMultiWindowMode(dp.isMultiWindowMode);
+            orientationState.setMultiWindowMode(dp.isMultiWindowMode);
         }
 
         // Notify when the animation starts
@@ -742,6 +791,8 @@
             mRecentsAnimationStartCallbacks.clear();
         }
 
+        TaskViewUtils.setSplitAuxiliarySurfacesShown(mRecentsAnimationTargets.nonApps, false);
+
         // Only add the callback to enable the input consumer after we actually have the controller
         mStateCallback.runOnceAtState(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
                 mRecentsAnimationController::enableInputConsumer);
@@ -756,6 +807,10 @@
         mActivityInitListener.unregister();
         mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
 
+        if (mRecentsAnimationTargets != null) {
+            TaskViewUtils.setSplitAuxiliarySurfacesShown(mRecentsAnimationTargets.nonApps, true);
+        }
+
         // Defer clearing the controller and the targets until after we've updated the state
         mRecentsAnimationController = null;
         mRecentsAnimationTargets = null;
@@ -845,9 +900,17 @@
     private void endRunningWindowAnim(boolean cancel) {
         if (mRunningWindowAnim != null) {
             if (cancel) {
-                mRunningWindowAnim.cancel();
+                for (RunningWindowAnim r : mRunningWindowAnim) {
+                    if (r != null) {
+                        r.cancel();
+                    }
+                }
             } else {
-                mRunningWindowAnim.end();
+                for (RunningWindowAnim r : mRunningWindowAnim) {
+                    if (r != null) {
+                        r.end();
+                    }
+                }
             }
         }
         if (mParallelRunningAnim != null) {
@@ -861,6 +924,9 @@
         // Fast-finish the attaching animation if it's still running.
         maybeUpdateRecentsAttachedState(false);
         final GestureEndTarget endTarget = mGestureState.getEndTarget();
+        // Wait until the given View (if supplied) draws before resuming the last task.
+        View postResumeLastTask = mActivityInterface.onSettledOnEndTarget(endTarget);
+
         if (endTarget != NEW_TASK) {
             InteractionJankMonitorWrapper.cancel(
                     InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
@@ -869,11 +935,13 @@
             InteractionJankMonitorWrapper.cancel(
                     InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
         }
+
         switch (endTarget) {
             case HOME:
                 mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
                 // Notify swipe-to-home (recents animation) is finished
                 SystemUiProxy.INSTANCE.get(mContext).notifySwipeToHomeFinished();
+                LauncherSplitScreenListener.INSTANCE.getNoCreate().notifySwipingToHome();
                 break;
             case RECENTS:
                 mStateCallback.setState(STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
@@ -883,7 +951,14 @@
                 mStateCallback.setState(STATE_START_NEW_TASK | STATE_CAPTURE_SCREENSHOT);
                 break;
             case LAST_TASK:
-                mStateCallback.setState(STATE_RESUME_LAST_TASK);
+                if (postResumeLastTask != null) {
+                    ViewUtils.postFrameDrawn(postResumeLastTask,
+                            () -> mStateCallback.setState(STATE_RESUME_LAST_TASK));
+                } else {
+                    mStateCallback.setState(STATE_RESUME_LAST_TASK);
+                }
+                TaskViewUtils.setSplitAuxiliarySurfacesShown(
+                        mRecentsAnimationTargets.nonApps, true);
                 break;
         }
         ActiveGestureLog.INSTANCE.addLog("onSettledOnEndTarget " + endTarget);
@@ -976,6 +1051,7 @@
                 isFling, isCancel);
         // Set the state, but don't notify until the animation completes
         mGestureState.setEndTarget(endTarget, false /* isAtomic */);
+        mAnimationFactory.setEndTarget(endTarget);
 
         float endShift = endTarget.isLauncher ? 1 : 0;
         final float startShift;
@@ -1020,9 +1096,6 @@
             if (mRecentsView != null) {
                 int nearestPage = mRecentsView.getDestinationPage();
                 boolean isScrolling = false;
-                // Update page scroll before snapping to page to make sure we snapped to the
-                // position calculated with target gesture in mind.
-                mRecentsView.updateScrollSynchronously();
                 if (mRecentsView.getNextPage() != nearestPage) {
                     // We shouldn't really scroll to the next page when swiping up to recents.
                     // Only allow settling on the next page if it's nearest to the center.
@@ -1128,8 +1201,15 @@
                     mActivityRestartListener);
 
             mParallelRunningAnim = mActivityInterface.getParallelAnimationToLauncher(
-                    mGestureState.getEndTarget(), duration);
+                    mGestureState.getEndTarget(), duration,
+                    mTaskAnimationManager.getCurrentCallbacks());
             if (mParallelRunningAnim != null) {
+                mParallelRunningAnim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mParallelRunningAnim = null;
+                    }
+                });
                 mParallelRunningAnim.start();
             }
         }
@@ -1145,21 +1225,24 @@
             boolean isTranslucent = runningTaskTarget != null && runningTaskTarget.isTranslucent;
             boolean appCanEnterPip = !mDeviceState.isPipActive()
                     && runningTaskTarget != null
+                    && runningTaskTarget.allowEnterPip
                     && runningTaskTarget.taskInfo.pictureInPictureParams != null
                     && runningTaskTarget.taskInfo.pictureInPictureParams.isAutoEnterEnabled();
             HomeAnimationFactory homeAnimFactory =
                     createHomeAnimationFactory(cookies, duration, isTranslucent, appCanEnterPip,
                             runningTaskTarget);
             mIsSwipingPipToHome = homeAnimFactory.supportSwipePipToHome() && appCanEnterPip;
-            final RectFSpringAnim windowAnim;
+            final RectFSpringAnim[] windowAnim;
             if (mIsSwipingPipToHome) {
                 mSwipePipToHomeAnimator = createWindowAnimationToPip(
                         homeAnimFactory, runningTaskTarget, start);
-                windowAnim = mSwipePipToHomeAnimator;
+                mSwipePipToHomeAnimators[0] = mSwipePipToHomeAnimator;
+                windowAnim = mSwipePipToHomeAnimators;
             } else {
                 mSwipePipToHomeAnimator = null;
                 windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
-                windowAnim.addAnimatorListener(new AnimationSuccessListener() {
+
+                windowAnim[0].addAnimatorListener(new AnimationSuccessListener() {
                     @Override
                     public void onAnimationSuccess(Animator animator) {
                         if (mRecentsAnimationController == null) {
@@ -1173,14 +1256,22 @@
                     }
                 });
             }
-            windowAnim.start(mContext, velocityPxPerMs);
-            mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
+            mRunningWindowAnim = new RunningWindowAnim[windowAnim.length];
+            for (int i = 0, windowAnimLength = windowAnim.length; i < windowAnimLength; i++) {
+                RectFSpringAnim windowAnimation = windowAnim[i];
+                if (windowAnimation == null) {
+                    continue;
+                }
+                windowAnimation.start(mContext, velocityPxPerMs);
+                mRunningWindowAnim[i] = RunningWindowAnim.wrap(windowAnimation);
+            }
             homeAnimFactory.setSwipeVelocity(velocityPxPerMs.y);
             homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y);
             mLauncherTransitionController = null;
 
             if (mRecentsView != null) {
-                mRecentsView.onPrepareGestureEndAnimation(null, mGestureState.getEndTarget());
+                mRecentsView.onPrepareGestureEndAnimation(null, mGestureState.getEndTarget(),
+                        getRemoteTaskViewSimulators());
             }
         } else {
             AnimatorSet animatorSet = new AnimatorSet();
@@ -1222,24 +1313,42 @@
             animatorSet.play(windowAnim);
             if (mRecentsView != null) {
                 mRecentsView.onPrepareGestureEndAnimation(
-                        animatorSet, mGestureState.getEndTarget());
+                        animatorSet, mGestureState.getEndTarget(),
+                        getRemoteTaskViewSimulators());
             }
             animatorSet.setDuration(duration).setInterpolator(interpolator);
             animatorSet.start();
-            mRunningWindowAnim = RunningWindowAnim.wrap(animatorSet);
+            mRunningWindowAnim = new RunningWindowAnim[]{RunningWindowAnim.wrap(animatorSet)};
         }
     }
 
+    private int calculateWindowRotation(RemoteAnimationTargetCompat runningTaskTarget,
+            RecentsOrientedState orientationState) {
+        if (runningTaskTarget.rotationChange != 0
+                && TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
+            return Math.abs(runningTaskTarget.rotationChange) == ROTATION_90
+                    ? ROTATION_270 : ROTATION_90;
+        } else {
+            return orientationState.getDisplayRotation();
+        }
+    }
+
+    /**
+     * TODO(b/195473090) handle multiple task simulators (if needed) for PIP
+     */
     private SwipePipToHomeAnimator createWindowAnimationToPip(HomeAnimationFactory homeAnimFactory,
             RemoteAnimationTargetCompat runningTaskTarget, float startProgress) {
         // Directly animate the app to PiP (picture-in-picture) mode
         final ActivityManager.RunningTaskInfo taskInfo = mGestureState.getRunningTask();
-        final RecentsOrientedState orientationState = mTaskViewSimulator.getOrientationState();
-        final int windowRotation = orientationState.getDisplayRotation();
+        final RecentsOrientedState orientationState = mRemoteTargetHandles[0].getTaskViewSimulator()
+                .getOrientationState();
+        final int windowRotation = calculateWindowRotation(runningTaskTarget, orientationState);
         final int homeRotation = orientationState.getRecentsActivityRotation();
 
-        final Matrix homeToWindowPositionMap = new Matrix();
-        final RectF startRect = updateProgressForStartRect(homeToWindowPositionMap, startProgress);
+        final Matrix[] homeToWindowPositionMaps = new Matrix[mRemoteTargetHandles.length];
+        final RectF startRect = updateProgressForStartRect(homeToWindowPositionMaps,
+                startProgress)[0];
+        final Matrix homeToWindowPositionMap = homeToWindowPositionMaps[0];
         // Move the startRect to Launcher space as floatingIconView runs in Launcher
         final Matrix windowToHomePositionMap = new Matrix();
         homeToWindowPositionMap.invert(windowToHomePositionMap);
@@ -1268,7 +1377,7 @@
         // is not ROTATION_0 (which implies the rotation is turned on in launcher settings).
         if (homeRotation == ROTATION_0
                 && (windowRotation == ROTATION_90 || windowRotation == ROTATION_270)) {
-            builder.setFromRotation(mTaskViewSimulator, windowRotation,
+            builder.setFromRotation(mRemoteTargetHandles[0].getTaskViewSimulator(), windowRotation,
                     taskInfo.displayCutoutInsets);
         }
         final SwipePipToHomeAnimator swipePipToHomeAnimator = builder.build();
@@ -1298,7 +1407,7 @@
                 mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
             }
         });
-        setupWindowAnimation(swipePipToHomeAnimator);
+        setupWindowAnimation(new RectFSpringAnim[]{swipePipToHomeAnimator});
         return swipePipToHomeAnimator;
     }
 
@@ -1325,19 +1434,19 @@
      * @param homeAnimationFactory The home animation factory.
      */
     @Override
-    protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
+    protected RectFSpringAnim[] createWindowAnimationToHome(float startProgress,
             HomeAnimationFactory homeAnimationFactory) {
-        RectFSpringAnim anim =
+        RectFSpringAnim[] anim =
                 super.createWindowAnimationToHome(startProgress, homeAnimationFactory);
         setupWindowAnimation(anim);
         return anim;
     }
 
-    private void setupWindowAnimation(RectFSpringAnim anim) {
-        anim.addOnUpdateListener((v, r, p) -> {
+    private void setupWindowAnimation(RectFSpringAnim[] anims) {
+        anims[0].addOnUpdateListener((v, r, p) -> {
             updateSysUiFlags(Math.max(p, mCurrentShift.value));
         });
-        anim.addAnimatorListener(new AnimationSuccessListener() {
+        anims[0].addAnimatorListener(new AnimationSuccessListener() {
             @Override
             public void onAnimationSuccess(Animator animator) {
                 if (mRecentsView != null) {
@@ -1349,7 +1458,7 @@
             }
         });
         if (mRecentsAnimationTargets != null) {
-            mRecentsAnimationTargets.addReleaseCheck(anim);
+            mRecentsAnimationTargets.addReleaseCheck(anims[0]);
         }
     }
 
@@ -1360,7 +1469,9 @@
             mActivity.clearRunOnceOnStartCallback();
             resetLauncherListeners();
         }
-        if (mGestureState.getEndTarget() != null && !mGestureState.isRunningAnimationToLauncher()) {
+        if (mGestureState.isRecentsAnimationRunning() && mGestureState.getEndTarget() != null
+                && !mGestureState.getEndTarget().isLauncher) {
+            // Continued quick switch.
             cancelCurrentAnimation();
         } else {
             mStateCallback.setStateOnUiThread(STATE_FINISH_WITH_NO_END);
@@ -1492,6 +1603,10 @@
         boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted;
         mActivityInterface.onTransitionCancelled(wasVisible, mGestureState.getEndTarget());
 
+        if (mRecentsAnimationTargets != null) {
+            TaskViewUtils.setSplitAuxiliarySurfacesShown(mRecentsAnimationTargets.nonApps, true);
+        }
+
         // Leave the pending invisible flag, as it may be used by wallpaper open animation.
         if (mActivity != null) {
             mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
@@ -1593,7 +1708,7 @@
      * if applicable. This should happen before {@link #finishRecentsControllerToHome(Runnable)}.
      */
     private void maybeFinishSwipePipToHome() {
-        if (mIsSwipingPipToHome && mSwipePipToHomeAnimator != null) {
+        if (mIsSwipingPipToHome && mSwipePipToHomeAnimators[0] != null) {
             SystemUiProxy.INSTANCE.get(mContext).stopSwipePipToHome(
                     mSwipePipToHomeAnimator.getComponentName(),
                     mSwipePipToHomeAnimator.getDestinationBounds(),
@@ -1634,8 +1749,8 @@
      * depend on proper class initialization.
      */
     protected void initAfterSubclassConstructor() {
-        initTransitionEndpoints(
-                mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile());
+        initTransitionEndpoints(mRemoteTargetHandles[0].getTaskViewSimulator()
+                        .getOrientationState().getLauncherDeviceProfile());
     }
 
     protected void performHapticFeedback() {
@@ -1652,7 +1767,8 @@
 
     protected void linkRecentsViewScroll() {
         SurfaceTransactionApplier.create(mRecentsView, applier -> {
-            mTransformParams.setSyncTransactionApplier(applier);
+            runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
+                            .setSyncTransactionApplier(applier));
             runOnRecentsAnimationStart(() ->
                     mRecentsAnimationTargets.addReleaseCheck(applier));
         });
@@ -1679,8 +1795,13 @@
                 mGestureState.updateLastStartedTaskId(taskId);
                 boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
                         .contains(taskId);
+                boolean isOldTaskSplit = LauncherSplitScreenListener.INSTANCE.getNoCreate()
+                        .getRunningSplitTaskIds().length > 0;
                 nextTask.launchTask(success -> {
                     resultCallback.accept(success);
+                    if (isOldTaskSplit) {
+                        SystemUiProxy.INSTANCE.getNoCreate().exitSplitScreen(taskId);
+                    }
                     if (success) {
                         if (hasTaskPreviouslyAppeared) {
                             onRestartPreviouslyAppearedTask();
@@ -1725,6 +1846,9 @@
 
     @Override
     public void onRecentsAnimationFinished(RecentsAnimationController controller) {
+        if (!controller.getFinishTargetIsLauncher()) {
+            TaskViewUtils.setSplitAuxiliarySurfacesShown(mRecentsAnimationTargets.nonApps, true);
+        }
         mRecentsAnimationController = null;
         mRecentsAnimationTargets = null;
         if (mRecentsView != null) {
@@ -1775,21 +1899,59 @@
     /**
      * Applies the transform on the recents animation
      */
-    protected void applyWindowTransform() {
-        if (mWindowTransitionController != null) {
-            mWindowTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor);
-        }
+    protected void applyScrollAndTransform() {
         // No need to apply any transform if there is ongoing swipe-pip-to-home animator since
         // that animator handles the leash solely.
-        if (mRecentsAnimationTargets != null && !mIsSwipingPipToHome) {
-            if (mRecentsViewScrollLinked && mRecentsView != null) {
-                mTaskViewSimulator.setScroll(mRecentsView.getScrollOffset());
+        boolean notSwipingPipToHome = mRecentsAnimationTargets != null && !mIsSwipingPipToHome;
+        boolean setRecentsScroll = mRecentsViewScrollLinked && mRecentsView != null;
+        for (RemoteTargetHandle remoteHandle : mRemoteTargetHandles) {
+            AnimatorControllerWithResistance playbackController =
+                    remoteHandle.getPlaybackController();
+            if (playbackController != null) {
+                playbackController.setProgress(Math.max(mCurrentShift.value,
+                        getScaleProgressDueToScroll()), mDragLengthFactor);
             }
-            mTaskViewSimulator.apply(mTransformParams);
+
+            if (notSwipingPipToHome) {
+                TaskViewSimulator taskViewSimulator = remoteHandle.getTaskViewSimulator();
+                if (setRecentsScroll) {
+                    taskViewSimulator.setScroll(mRecentsView.getScrollOffset());
+                }
+                taskViewSimulator.apply(remoteHandle.getTransformParams());
+            }
         }
         ProtoTracer.INSTANCE.get(mContext).scheduleFrameUpdate();
     }
 
+    // Scaling of RecentsView during quick switch based on amount of recents scroll
+    private float getScaleProgressDueToScroll() {
+        if (mActivity == null || !mActivity.getDeviceProfile().isTablet || mRecentsView == null
+                || !mRecentsViewScrollLinked) {
+            return 0;
+        }
+
+        float scrollOffset = Math.abs(mRecentsView.getScrollOffset(mRecentsView.getCurrentPage()));
+        int maxScrollOffset = mRecentsView.getPagedOrientationHandler().getPrimaryValue(
+                mRecentsView.getLastComputedTaskSize().width(),
+                mRecentsView.getLastComputedTaskSize().height());
+        maxScrollOffset += mRecentsView.getPageSpacing();
+
+        float maxScaleProgress =
+                MAX_QUICK_SWITCH_RECENTS_SCALE_PROGRESS * mRecentsView.getMaxScaleForFullScreen();
+        float scaleProgress = maxScaleProgress;
+
+        if (scrollOffset < mQuickSwitchScaleScrollThreshold) {
+            scaleProgress = Utilities.mapToRange(scrollOffset, 0, mQuickSwitchScaleScrollThreshold,
+                    0, maxScaleProgress, ACCEL_DEACCEL);
+        } else if (scrollOffset > (maxScrollOffset - mQuickSwitchScaleScrollThreshold)) {
+            scaleProgress = Utilities.mapToRange(scrollOffset,
+                    (maxScrollOffset - mQuickSwitchScaleScrollThreshold), maxScrollOffset,
+                    maxScaleProgress, 0, ACCEL_DEACCEL);
+        }
+
+        return scaleProgress;
+    }
+
     /**
      * Used for winscope tracing, see launcher_trace.proto
      * @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
@@ -1811,7 +1973,6 @@
     }
 
     public interface Factory {
-
         AbsSwipeUpHandler newHandler(GestureState gestureState, long touchTimeMs);
     }
 }
diff --git a/quickstep/src/com/android/quickstep/AnimatedFloat.java b/quickstep/src/com/android/quickstep/AnimatedFloat.java
index f7e8781..95c8710 100644
--- a/quickstep/src/com/android/quickstep/AnimatedFloat.java
+++ b/quickstep/src/com/android/quickstep/AnimatedFloat.java
@@ -53,6 +53,16 @@
         mUpdateCallback = updateCallback;
     }
 
+    /**
+     * Returns an animation from the current value to the given value.
+     */
+    public ObjectAnimator animateToValue(float end) {
+        return animateToValue(value, end);
+    }
+
+    /**
+     * Returns an animation from the given start value to the given end value.
+     */
     public ObjectAnimator animateToValue(float start, float end) {
         cancelAnimation();
         mValueAnimator = ObjectAnimator.ofFloat(this, VALUE, start, end);
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index fac4d52..cf06036 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -40,6 +40,7 @@
 import android.os.Build;
 import android.view.Gravity;
 import android.view.MotionEvent;
+import android.view.View;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
@@ -48,10 +49,10 @@
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.taskbar.TaskbarUIController;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.WindowBounds;
 import com.android.launcher3.views.ScrimView;
@@ -77,12 +78,14 @@
 
     public final boolean rotationSupportedByActivity;
 
-    private final STATE_TYPE mOverviewState, mBackgroundState;
+    private final STATE_TYPE mBackgroundState;
+
+    private STATE_TYPE mTargetState;
 
     protected BaseActivityInterface(boolean rotationSupportedByActivity,
             STATE_TYPE overviewState, STATE_TYPE backgroundState) {
         this.rotationSupportedByActivity = rotationSupportedByActivity;
-        mOverviewState = overviewState;
+        mTargetState = overviewState;
         mBackgroundState = backgroundState;
     }
 
@@ -136,6 +139,9 @@
         return null;
     }
 
+    @Nullable
+    public abstract TaskbarUIController getTaskbarController();
+
     public final boolean isResumed() {
         ACTIVITY_TYPE activity = getCreatedActivity();
         return activity != null && activity.hasBeenResumed();
@@ -204,40 +210,24 @@
     /**
      * Calculates the taskView size for the provided device configuration.
      */
-    public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect,
-            PagedOrientationHandler orientedState) {
+    public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect) {
         Resources res = context.getResources();
-        if (dp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
+        if (dp.overviewShowAsGrid) {
             Rect gridRect = new Rect();
             calculateGridSize(context, dp, gridRect);
 
-            int verticalMargin = res.getDimensionPixelSize(
-                    R.dimen.overview_grid_focus_vertical_margin);
-            float taskHeight = gridRect.height() - verticalMargin * 2;
-
             PointF taskDimension = getTaskDimension(context, dp);
-            float scale = taskHeight / Math.max(taskDimension.x, taskDimension.y);
+            float scale = gridRect.height() / taskDimension.y;
             int outWidth = Math.round(scale * taskDimension.x);
             int outHeight = Math.round(scale * taskDimension.y);
 
-            int gravity = Gravity.CENTER_VERTICAL;
-            gravity |= orientedState.getRecentsRtlSetting(res) ? Gravity.RIGHT : Gravity.LEFT;
+            int gravity = Gravity.CENTER;
             Gravity.apply(gravity, outWidth, outHeight, gridRect, outRect);
         } else {
             int taskMargin = dp.overviewTaskMarginPx;
-            int proactiveRowAndMargin;
-            if (!TaskView.SHOW_PROACTIVE_ACTIONS || dp.isVerticalBarLayout()) {
-                // In Vertical Bar Layout the proactive row doesn't have its own space, it's inside
-                // the actions row.
-                proactiveRowAndMargin = 0;
-            } else {
-                proactiveRowAndMargin = res.getDimensionPixelSize(
-                        R.dimen.overview_proactive_row_height)
-                        + res.getDimensionPixelSize(R.dimen.overview_proactive_row_bottom_margin);
-            }
             calculateTaskSizeInternal(context, dp,
                     dp.overviewTaskThumbnailTopMarginPx,
-                    proactiveRowAndMargin + getOverviewActionsHeight(context, dp),
+                    getOverviewActionsHeight(context, dp),
                     res.getDimensionPixelSize(R.dimen.overview_minimum_next_prev_size) + taskMargin,
                     outRect);
         }
@@ -278,19 +268,35 @@
     public static void getTaskDimension(Context context, DeviceProfile dp, PointF out) {
         if (dp.isMultiWindowMode) {
             WindowBounds bounds = SplitScreenBounds.INSTANCE.getSecondaryWindowBounds(context);
-            if (TaskView.CLIP_STATUS_AND_NAV_BARS) {
-                out.x = bounds.availableSize.x;
-                out.y = bounds.availableSize.y;
-            } else {
-                out.x = bounds.availableSize.x + bounds.insets.left + bounds.insets.right;
-                out.y = bounds.availableSize.y + bounds.insets.top + bounds.insets.bottom;
+            out.x = bounds.availableSize.x;
+            out.y = bounds.availableSize.y;
+            if (!TaskView.clipLeft(dp)) {
+                out.x += bounds.insets.left;
             }
-        } else if (TaskView.CLIP_STATUS_AND_NAV_BARS) {
-            out.x = dp.availableWidthPx;
-            out.y = dp.availableHeightPx;
+            if (!TaskView.clipRight(dp)) {
+                out.x += bounds.insets.right;
+            }
+            if (!TaskView.clipTop(dp)) {
+                out.y += bounds.insets.top;
+            }
+            if (!TaskView.clipBottom(dp)) {
+                out.y += bounds.insets.bottom;
+            }
         } else {
             out.x = dp.widthPx;
             out.y = dp.heightPx;
+            if (TaskView.clipLeft(dp)) {
+                out.x -= dp.getInsets().left;
+            }
+            if (TaskView.clipRight(dp)) {
+                out.x -= dp.getInsets().right;
+            }
+            if (TaskView.clipTop(dp)) {
+                out.y -= dp.getInsets().top;
+            }
+            if (TaskView.clipBottom(dp)) {
+                out.y -= Math.max(dp.getInsets().bottom, dp.taskbarSize);
+            }
         }
     }
 
@@ -299,13 +305,13 @@
      */
     public final void calculateGridSize(Context context, DeviceProfile dp, Rect outRect) {
         Resources res = context.getResources();
-        int topMargin = res.getDimensionPixelSize(R.dimen.overview_grid_top_margin);
-        int bottomMargin = res.getDimensionPixelSize(R.dimen.overview_grid_bottom_margin);
+        Rect insets = dp.getInsets();
+        int topMargin = dp.overviewTaskThumbnailTopMarginPx;
+        int bottomMargin = getOverviewActionsHeight(context, dp);
         int sideMargin = res.getDimensionPixelSize(R.dimen.overview_grid_side_margin);
 
-        Rect insets = dp.getInsets();
         outRect.set(0, 0, dp.widthPx, dp.heightPx);
-        outRect.inset(Math.max(insets.left, sideMargin), Math.max(insets.top, topMargin),
+        outRect.inset(Math.max(insets.left, sideMargin), insets.top + topMargin,
                 Math.max(insets.right, sideMargin), Math.max(insets.bottom, bottomMargin));
     }
 
@@ -318,18 +324,17 @@
         Rect gridRect = new Rect();
         calculateGridSize(context, dp, gridRect);
 
-        int rowSpacing = res.getDimensionPixelSize(R.dimen.overview_grid_row_spacing);
-        float rowHeight = (gridRect.height() - rowSpacing) / 2f;
+        float rowHeight =
+                (gridRect.height() + dp.overviewTaskThumbnailTopMarginPx - dp.overviewRowSpacing)
+                        / 2f;
 
         PointF taskDimension = getTaskDimension(context, dp);
-        float scale = (rowHeight - dp.overviewTaskThumbnailTopMarginPx) / Math.max(
-                taskDimension.x, taskDimension.y);
+        float scale = (rowHeight - dp.overviewTaskThumbnailTopMarginPx) / taskDimension.y;
         int outWidth = Math.round(scale * taskDimension.x);
         int outHeight = Math.round(scale * taskDimension.y);
 
         int gravity = Gravity.TOP;
         gravity |= orientedState.getRecentsRtlSetting(res) ? Gravity.RIGHT : Gravity.LEFT;
-        gridRect.inset(0, dp.overviewTaskThumbnailTopMarginPx, 0, 0);
         Gravity.apply(gravity, outWidth, outHeight, gridRect, outRect);
     }
 
@@ -358,7 +363,8 @@
      * an optional additional animation with the same duration.
      */
     public @Nullable Animator getParallelAnimationToLauncher(
-            GestureState.GestureEndTarget endTarget, long duration) {
+            GestureState.GestureEndTarget endTarget, long duration,
+            RecentsAnimationCallbacks callbacks) {
         if (endTarget == RECENTS) {
             ACTIVITY_TYPE activity = getCreatedActivity();
             if (activity == null) {
@@ -385,6 +391,15 @@
      */
     public abstract STATE_TYPE stateFromGestureEndTarget(GestureState.GestureEndTarget endTarget);
 
+    /**
+     * Called when the animation to the target has finished, but right before updating the state.
+     * @return A View that needs to draw before ending the recents animation to LAST_TASK.
+     * (This is a hack to ensure Taskbar draws its background first to avoid flickering.)
+     */
+    public @Nullable View onSettledOnEndTarget(GestureState.GestureEndTarget endTarget) {
+        return null;
+    }
+
     public interface AnimationFactory {
 
         void createActivityInterface(long transitionLength);
@@ -399,6 +414,13 @@
         default boolean isRecentsAttachedToAppWindow() {
             return false;
         }
+
+        default boolean hasRecentsEverAttachedToAppWindow() {
+            return false;
+        }
+
+        /** Called when the gesture ends and we know what state it is going towards */
+        default void setEndTarget(GestureState.GestureEndTarget endTarget) { }
     }
 
     class DefaultAnimationFactory implements AnimationFactory {
@@ -408,6 +430,7 @@
         private final Consumer<AnimatorControllerWithResistance> mCallback;
 
         private boolean mIsAttachedToWindow;
+        private boolean mHasEverAttachedToWindow;
 
         DefaultAnimationFactory(Consumer<AnimatorControllerWithResistance> callback) {
             mCallback = callback;
@@ -435,7 +458,7 @@
 
             // Since we are changing the start position of the UI, reapply the state, at the end
             controller.setEndAction(() -> mActivity.getStateManager().goToState(
-                    controller.getInterpolatedProgress() > 0.5 ? mOverviewState : mBackgroundState,
+                    controller.getInterpolatedProgress() > 0.5 ? mTargetState : mBackgroundState,
                     false));
 
             RecentsView recentsView = mActivity.getOverviewPanel();
@@ -461,6 +484,9 @@
             }
             mIsAttachedToWindow = attached;
             RecentsView recentsView = mActivity.getOverviewPanel();
+            if (attached) {
+                mHasEverAttachedToWindow = true;
+            }
             Animator fadeAnim = mActivity.getStateManager()
                     .createStateElementAnimation(INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0);
 
@@ -490,6 +516,16 @@
             return mIsAttachedToWindow;
         }
 
+        @Override
+        public boolean hasRecentsEverAttachedToAppWindow() {
+            return mHasEverAttachedToWindow;
+        }
+
+        @Override
+        public void setEndTarget(GestureState.GestureEndTarget endTarget) {
+            mTargetState = stateFromGestureEndTarget(endTarget);
+        }
+
         protected void createBackgroundToOverviewAnim(ACTIVITY_TYPE activity, PendingAnimation pa) {
             //  Scale down recents from being full screen to being in overview.
             RecentsView recentsView = activity.getOverviewPanel();
@@ -498,9 +534,4 @@
             pa.addFloat(recentsView, FULLSCREEN_PROGRESS, 1, 0, LINEAR);
         }
     }
-
-    /** Called when OverviewService is bound to this process */
-    void onOverviewServiceBound() {
-        // Do nothing
-    }
 }
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index 7fb8e16..ffdfa43 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -21,6 +21,8 @@
 import static com.android.quickstep.fallback.RecentsState.DEFAULT;
 import static com.android.quickstep.fallback.RecentsState.HOME;
 
+import android.animation.Animator;
+import android.animation.AnimatorSet;
 import android.content.Context;
 import android.graphics.Rect;
 import android.view.MotionEvent;
@@ -29,7 +31,9 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.taskbar.FallbackTaskbarUIController;
 import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.quickstep.GestureState.GestureEndTarget;
 import com.android.quickstep.fallback.RecentsState;
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
@@ -57,7 +61,7 @@
     @Override
     public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect,
             PagedOrientationHandler orientationHandler) {
-        calculateTaskSize(context, dp, outRect, orientationHandler);
+        calculateTaskSize(context, dp, outRect);
         if (dp.isVerticalBarLayout()
                 && SysUINavigationMode.INSTANCE.get(context).getMode() != NO_BUTTON) {
             return dp.isSeascape() ? outRect.left : (dp.widthPx - outRect.right);
@@ -102,6 +106,15 @@
         return RecentsActivity.ACTIVITY_TRACKER.getCreatedActivity();
     }
 
+    @Override
+    public FallbackTaskbarUIController getTaskbarController() {
+        RecentsActivity activity = getCreatedActivity();
+        if (activity == null) {
+            return null;
+        }
+        return activity.getTaskbarUIController();
+    }
+
     @Nullable
     @Override
     public RecentsView getVisibleRecentsView() {
@@ -182,7 +195,7 @@
     }
 
     @Override
-    public RecentsState stateFromGestureEndTarget(GestureState.GestureEndTarget endTarget) {
+    public RecentsState stateFromGestureEndTarget(GestureEndTarget endTarget) {
         switch (endTarget) {
             case RECENTS:
                 return DEFAULT;
@@ -203,6 +216,28 @@
     }
 
     @Override
+    public @Nullable Animator getParallelAnimationToLauncher(GestureEndTarget endTarget,
+            long duration, RecentsAnimationCallbacks callbacks) {
+        FallbackTaskbarUIController uiController = getTaskbarController();
+        Animator superAnimator = super.getParallelAnimationToLauncher(
+                endTarget, duration, callbacks);
+        if (uiController == null) {
+            return superAnimator;
+        }
+        RecentsState toState = stateFromGestureEndTarget(endTarget);
+        Animator taskbarAnimator = uiController.createAnimToRecentsState(toState, duration);
+        if (taskbarAnimator == null) {
+            return superAnimator;
+        }
+        if (superAnimator == null) {
+            return taskbarAnimator;
+        }
+        AnimatorSet animatorSet = new AnimatorSet();
+        animatorSet.playTogether(superAnimator, taskbarAnimator);
+        return animatorSet;
+    }
+
+    @Override
     protected int getOverviewScrimColorForState(RecentsActivity activity, RecentsState state) {
         return state.getScrimColor(activity);
     }
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index e2f198c..c1b45e0 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -28,6 +28,7 @@
 
 import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
+import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
@@ -101,7 +102,9 @@
 
         mRunningOverHome = ActivityManagerWrapper.isHomeTask(mGestureState.getRunningTask());
         if (mRunningOverHome) {
-            mTransformParams.setHomeBuilderProxy(this::updateHomeActivityTransformDuringSwipeUp);
+            runActionOnRemoteHandles(remoteTargetHandle ->
+                    remoteTargetHandle.getTransformParams().setHomeBuilderProxy(
+                    FallbackSwipeHandler.this::updateHomeActivityTransformDuringSwipeUp));
         }
     }
 
@@ -109,7 +112,9 @@
     protected void initTransitionEndpoints(DeviceProfile dp) {
         super.initTransitionEndpoints(dp);
         if (mRunningOverHome) {
-            mMaxLauncherScale = 1 / mTaskViewSimulator.getFullScreenScale();
+            // Full screen scale should be independent of remote target handle
+            mMaxLauncherScale = 1 / mRemoteTargetHandles[0].getTaskViewSimulator()
+                    .getFullScreenScale();
         }
     }
 
@@ -174,7 +179,8 @@
     protected void notifyGestureAnimationStartToRecents() {
         if (mRunningOverHome) {
             if (SysUINavigationMode.getMode(mContext).hasGestures) {
-                mRecentsView.onGestureAnimationStartOnHome(mGestureState.getRunningTask());
+                mRecentsView.onGestureAnimationStartOnHome(
+                        new ActivityManager.RunningTaskInfo[]{mGestureState.getRunningTask()});
             }
         } else {
             super.notifyGestureAnimationStartToRecents();
@@ -202,19 +208,24 @@
                 mHomeAlpha = new AnimatedFloat();
                 mHomeAlpha.value = Utilities.boundToRange(1 - mCurrentShift.value, 0, 1);
                 mVerticalShiftForScale.value = mCurrentShift.value;
-                mTransformParams.setHomeBuilderProxy(
-                        this::updateHomeActivityTransformDuringHomeAnim);
+                runActionOnRemoteHandles(remoteTargetHandle ->
+                        remoteTargetHandle.getTransformParams().setHomeBuilderProxy(
+                                FallbackHomeAnimationFactory.this
+                                        ::updateHomeActivityTransformDuringHomeAnim));
             } else {
                 mHomeAlpha = new AnimatedFloat(this::updateHomeAlpha);
                 mHomeAlpha.value = 0;
-
-                mHomeAlphaParams.setHomeBuilderProxy(
-                        this::updateHomeActivityTransformDuringHomeAnim);
+                runActionOnRemoteHandles(remoteTargetHandle ->
+                        remoteTargetHandle.getTransformParams().setHomeBuilderProxy(
+                                FallbackHomeAnimationFactory.this
+                                        ::updateHomeActivityTransformDuringHomeAnim));
             }
 
             mRecentsAlpha.value = 1;
-            mTransformParams.setBaseBuilderProxy(
-                    this::updateRecentsActivityTransformDuringHomeAnim);
+            runActionOnRemoteHandles(remoteTargetHandle ->
+                    remoteTargetHandle.getTransformParams().setHomeBuilderProxy(
+                            FallbackHomeAnimationFactory.this
+                                    ::updateRecentsActivityTransformDuringHomeAnim));
         }
 
         @NonNull
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index e3ae361..99f1dc7 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -122,10 +122,6 @@
     public static final int STATE_RECENTS_ANIMATION_ENDED =
             getFlagForIndex("STATE_RECENTS_ANIMATION_ENDED");
 
-    // Called when we create an overscroll window when swiping right to left on the most recent app
-    public static final int STATE_OVERSCROLL_WINDOW_CREATED =
-            getFlagForIndex("STATE_OVERSCROLL_WINDOW_CREATED");
-
     // Called when RecentsView stops scrolling and settles on a TaskView.
     public static final int STATE_RECENTS_SCROLLING_FINISHED =
             getFlagForIndex("STATE_RECENTS_SCROLLING_FINISHED");
@@ -346,8 +342,8 @@
      * @return whether the recents animation is started but not yet ended
      */
     public boolean isRecentsAnimationRunning() {
-        return mStateCallback.hasStates(STATE_RECENTS_ANIMATION_INITIALIZED) &&
-                !mStateCallback.hasStates(STATE_RECENTS_ANIMATION_ENDED);
+        return mStateCallback.hasStates(STATE_RECENTS_ANIMATION_STARTED)
+                && !mStateCallback.hasStates(STATE_RECENTS_ANIMATION_ENDED);
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java
index 0b2a057..3580ee5 100644
--- a/quickstep/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/src/com/android/quickstep/InputConsumer.java
@@ -39,6 +39,7 @@
     int TYPE_OVERSCROLL = 1 << 9;
     int TYPE_SYSUI_OVERLAY = 1 << 10;
     int TYPE_ONE_HANDED = 1 << 11;
+    int TYPE_TASKBAR_STASH = 1 << 12;
 
     String[] NAMES = new String[] {
            "TYPE_NO_OP",                    // 0
@@ -53,6 +54,7 @@
             "TYPE_OVERSCROLL",              // 9
             "TYPE_SYSUI_OVERLAY",           // 10
             "TYPE_ONE_HANDED",              // 11
+            "TYPE_TASKBAR_STASH",           // 12
     };
 
     InputConsumer NO_OP = () -> TYPE_NO_OP;
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index fb1391a..aa9435b 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -25,10 +25,12 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.content.Context;
 import android.graphics.Rect;
 import android.view.MotionEvent;
+import android.view.View;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
@@ -71,7 +73,7 @@
     @Override
     public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect,
             PagedOrientationHandler orientationHandler) {
-        calculateTaskSize(context, dp, outRect, orientationHandler);
+        calculateTaskSize(context, dp, outRect);
         if (dp.isVerticalBarLayout() && SysUINavigationMode.getMode(context) != Mode.NO_BUTTON) {
             return dp.isSeascape() ? outRect.left : (dp.widthPx - outRect.right);
         } else {
@@ -131,6 +133,18 @@
                         new ClampedDepthProperty(fromDepthRatio, toDepthRatio),
                         fromDepthRatio, toDepthRatio, LINEAR);
 
+                pa.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        LauncherTaskbarUIController taskbarUIController =
+                                activity.getTaskbarUIController();
+                        if (taskbarUIController != null) {
+                            // Launcher's ScrimView will draw the background throughout the gesture.
+                            taskbarUIController.forceHideBackground(true);
+                        }
+                    }
+                });
+
             }
         };
 
@@ -173,7 +187,8 @@
     }
 
     @Nullable
-    private LauncherTaskbarUIController getTaskbarController() {
+    @Override
+    public LauncherTaskbarUIController getTaskbarController() {
         BaseQuickstepLauncher launcher = getCreatedActivity();
         if (launcher == null) {
             return null;
@@ -189,7 +204,7 @@
                 launcher != null && launcher.getStateManager().getState().overviewUi
                         ? launcher.getOverviewPanel() : null;
         if (recentsView == null || (!launcher.hasBeenResumed()
-                && recentsView.getRunningTaskId() == -1)) {
+                && recentsView.getRunningTaskViewId() == -1)) {
             // If live tile has ended, return null.
             return null;
         }
@@ -288,25 +303,23 @@
         } else {
             om.hideOverlay(150);
         }
-    }
-
-    @Override
-    void onOverviewServiceBound() {
-        final BaseQuickstepLauncher activity = getCreatedActivity();
-        if (activity == null) return;
-        activity.getAppTransitionManager().registerRemoteTransitions();
+        LauncherTaskbarUIController taskbarController = getTaskbarController();
+        if (taskbarController != null) {
+            taskbarController.hideEdu();
+        }
     }
 
     @Override
     public @Nullable Animator getParallelAnimationToLauncher(GestureEndTarget endTarget,
-            long duration) {
+            long duration, RecentsAnimationCallbacks callbacks) {
         LauncherTaskbarUIController uiController = getTaskbarController();
-        Animator superAnimator = super.getParallelAnimationToLauncher(endTarget, duration);
-        if (uiController == null) {
+        Animator superAnimator = super.getParallelAnimationToLauncher(
+                endTarget, duration, callbacks);
+        if (uiController == null || callbacks == null) {
             return superAnimator;
         }
         LauncherState toState = stateFromGestureEndTarget(endTarget);
-        Animator taskbarAnimator = uiController.createAnimToLauncher(toState, duration);
+        Animator taskbarAnimator = uiController.createAnimToLauncher(toState, callbacks, duration);
         if (superAnimator == null) {
             return taskbarAnimator;
         } else {
@@ -353,4 +366,16 @@
                 return NORMAL;
         }
     }
+
+    @Override
+    public View onSettledOnEndTarget(@Nullable GestureEndTarget endTarget) {
+        View superRet = super.onSettledOnEndTarget(endTarget);
+        LauncherTaskbarUIController taskbarUIController = getTaskbarController();
+        if (taskbarUIController != null) {
+            // Start drawing taskbar's background again since launcher might stop drawing.
+            taskbarUIController.forceHideBackground(false);
+            return taskbarUIController.getRootView();
+        }
+        return superRet;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 19cad53..0181cd7 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -106,9 +106,11 @@
         boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow();
 
         mActivity.getRootView().setForceHideBackArrow(true);
-        mActivity.setHintUserWillBeActive();
+        if (!TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
+            mActivity.setHintUserWillBeActive();
+        }
 
-        if (!canUseWorkspaceView || appCanEnterPip) {
+        if (!canUseWorkspaceView || appCanEnterPip || mIsSwipeForStagedSplit) {
             return new LauncherHomeAnimationFactory();
         }
         if (workspaceView instanceof LauncherAppWidgetHostView) {
@@ -135,6 +137,12 @@
             // opaque until it is ready.
             private boolean mIsFloatingIconReady = false;
 
+            @Nullable
+            @Override
+            protected View getViewIgnoredInWorkspaceRevealAnimation() {
+                return workspaceView;
+            }
+
             @Override
             public RectF getWindowTargetRect() {
                 super.getWindowTargetRect();
@@ -179,14 +187,16 @@
         final float floatingWidgetAlpha = isTargetTranslucent ? 0 : 1;
         RectF backgroundLocation = new RectF();
         Rect crop = new Rect();
-        mTaskViewSimulator.getCurrentCropRect().roundOut(crop);
+        // We can assume there is only one remote target here because staged split never animates
+        // into the app icon, only into the homescreen
+        mRemoteTargetHandles[0].getTaskViewSimulator().getCurrentCropRect().roundOut(crop);
         Size windowSize = new Size(crop.width(), crop.height());
         int fallbackBackgroundColor =
                 FloatingWidgetView.getDefaultBackgroundColor(mContext, runningTaskTarget);
         FloatingWidgetView floatingWidgetView = FloatingWidgetView.getFloatingWidgetView(mActivity,
                 hostView, backgroundLocation, windowSize,
-                mTaskViewSimulator.getCurrentCornerRadius(), isTargetTranslucent,
-                fallbackBackgroundColor);
+                mRemoteTargetHandles[0].getTaskViewSimulator().getCurrentCornerRadius(),
+                isTargetTranslucent, fallbackBackgroundColor);
 
         return new FloatingViewHomeAnimationFactory(floatingWidgetView) {
 
diff --git a/quickstep/src/com/android/quickstep/OrientationRectF.java b/quickstep/src/com/android/quickstep/OrientationRectF.java
new file mode 100644
index 0000000..59a202c
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/OrientationRectF.java
@@ -0,0 +1,104 @@
+/*
+ * 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;
+
+import static com.android.launcher3.states.RotationHelper.deltaRotation;
+import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
+
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.util.Log;
+import android.view.MotionEvent;
+
+public class OrientationRectF extends RectF {
+
+    private static final String TAG = "OrientationRectF";
+    private static final boolean DEBUG = false;
+
+    private final int mRotation;
+    private final float mHeight;
+    private final float mWidth;
+
+    private final Matrix mTmpMatrix = new Matrix();
+    private final float[] mTmpPoint = new float[2];
+
+    public OrientationRectF(float left, float top, float right, float bottom, int rotation) {
+        super(left, top, right, bottom);
+        mRotation = rotation;
+        mHeight = bottom;
+        mWidth = right;
+    }
+
+    @Override
+    public String toString() {
+        String s = super.toString();
+        s += " rotation: " + mRotation;
+        return s;
+    }
+
+    @Override
+    public boolean contains(float x, float y) {
+        // Mark bottom right as included in the Rect (copied from Rect src, added "=" in "<=")
+        return left < right && top < bottom  // check for empty first
+                && x >= left && x <= right && y >= top && y <= bottom;
+    }
+
+    public boolean applyTransformFromRotation(MotionEvent event, int currentRotation,
+            boolean forceTransform) {
+        return applyTransform(event, deltaRotation(currentRotation, mRotation), forceTransform);
+    }
+
+    public boolean applyTransformToRotation(MotionEvent event, int currentRotation,
+            boolean forceTransform) {
+        return applyTransform(event, deltaRotation(mRotation, currentRotation), forceTransform);
+    }
+
+    private boolean applyTransform(MotionEvent event, int deltaRotation, boolean forceTransform) {
+        mTmpMatrix.reset();
+        postDisplayRotation(deltaRotation, mHeight, mWidth, mTmpMatrix);
+        if (forceTransform) {
+            if (DEBUG) {
+                Log.d(TAG, "Transforming rotation due to forceTransform, "
+                        + "deltaRotation: " + deltaRotation
+                        + "mRotation: " + mRotation
+                        + " this: " + this);
+            }
+            event.applyTransform(mTmpMatrix);
+            return true;
+        }
+        mTmpPoint[0] = event.getX();
+        mTmpPoint[1] = event.getY();
+        mTmpMatrix.mapPoints(mTmpPoint);
+
+        if (DEBUG) {
+            Log.d(TAG, "original: " + event.getX() + ", " + event.getY()
+                    + " new: " + mTmpPoint[0] + ", " + mTmpPoint[1]
+                    + " rect: " + this + " forceTransform: " + forceTransform
+                    + " contains: " + contains(mTmpPoint[0], mTmpPoint[1])
+                    + " this: " + this);
+        }
+
+        if (contains(mTmpPoint[0], mTmpPoint[1])) {
+            event.applyTransform(mTmpMatrix);
+            return true;
+        }
+        return false;
+    }
+
+    int getRotation() {
+        return mRotation;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
index 35a851a..ecff4f1 100644
--- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
@@ -22,11 +22,7 @@
 import static android.view.MotionEvent.ACTION_POINTER_DOWN;
 import static android.view.MotionEvent.ACTION_UP;
 
-import static com.android.launcher3.states.RotationHelper.deltaRotation;
-import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
-
 import android.content.res.Resources;
-import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.RectF;
 import android.util.Log;
@@ -44,8 +40,8 @@
 
 /**
  * Maintains state for supporting nav bars and tracking their gestures in multiple orientations.
- * See {@link OrientationRectF#applyTransform(MotionEvent, boolean)} for transformation of
- * MotionEvents from one orientation's coordinate space to another's.
+ * See {@link OrientationRectF#applyTransformToRotation(MotionEvent, int, boolean)} for
+ * transformation of MotionEvents from one orientation's coordinate space to another's.
  *
  * This class only supports single touch/pointer gesture tracking for touches started in a supported
  * nav bar region.
@@ -95,9 +91,6 @@
 
     private static final int QUICKSTEP_ROTATION_UNINITIALIZED = -1;
 
-    private final Matrix mTmpMatrix = new Matrix();
-    private final float[] mTmpPoint = new float[2];
-
     private final Map<CurrentDisplay, OrientationRectF> mSwipeTouchRegions =
             new HashMap<CurrentDisplay, OrientationRectF>();
     private final RectF mAssistantLeftRegion = new RectF();
@@ -365,7 +358,7 @@
                 if (mLastRectTouched == null) {
                     return;
                 }
-                mLastRectTouched.applyTransform(event, true);
+                mLastRectTouched.applyTransformFromRotation(event, mCurrentDisplay.rotation, true);
                 break;
             }
             case ACTION_CANCEL:
@@ -373,7 +366,7 @@
                 if (mLastRectTouched == null) {
                     return;
                 }
-                mLastRectTouched.applyTransform(event, true);
+                mLastRectTouched.applyTransformFromRotation(event, mCurrentDisplay.rotation, true);
                 mLastRectTouched = null;
                 break;
             }
@@ -387,14 +380,14 @@
                     if (rect == null) {
                         continue;
                     }
-                    if (rect.applyTransform(event, false)) {
+                    if (rect.applyTransformFromRotation(event, mCurrentDisplay.rotation, false)) {
                         mLastRectTouched = rect;
-                        mActiveTouchRotation = rect.mRotation;
+                        mActiveTouchRotation = rect.getRotation();
                         if (mEnableMultipleRegions
                                 && mCurrentDisplay.rotation == mActiveTouchRotation) {
                             // TODO(b/154580671) might make this block unnecessary
                             // Start a touch session for the default nav region for the display
-                            mQuickStepStartingRotation = mLastRectTouched.mRotation;
+                            mQuickStepStartingRotation = mLastRectTouched.getRotation();
                             resetSwipeRegions();
                         }
                         if (DEBUG) {
@@ -423,65 +416,4 @@
         pw.println("  mNavBarLargerGesturalHeight=" + mNavBarLargerGesturalHeight);
         pw.println("  mOneHandedModeRegion=" + mOneHandedModeRegion);
     }
-
-    private class OrientationRectF extends RectF {
-
-        private int mRotation;
-        private float mHeight;
-        private float mWidth;
-
-        OrientationRectF(float left, float top, float right, float bottom, int rotation) {
-            super(left, top, right, bottom);
-            this.mRotation = rotation;
-            mHeight = bottom;
-            mWidth = right;
-        }
-
-        @Override
-        public String toString() {
-            String s = super.toString();
-            s += " rotation: " + mRotation;
-            return s;
-        }
-
-        @Override
-        public boolean contains(float x, float y) {
-            // Mark bottom right as included in the Rect (copied from Rect src, added "=" in "<=")
-            return left < right && top < bottom  // check for empty first
-                    && x >= left && x <= right && y >= top && y <= bottom;
-        }
-
-        boolean applyTransform(MotionEvent event, boolean forceTransform) {
-            mTmpMatrix.reset();
-            postDisplayRotation(deltaRotation(mCurrentDisplay.rotation, mRotation),
-                    mHeight, mWidth, mTmpMatrix);
-            if (forceTransform) {
-                if (DEBUG) {
-                    Log.d(TAG, "Transforming rotation due to forceTransform, "
-                            + "mCurrentRotation: " + mCurrentDisplay.rotation
-                            + "mRotation: " + mRotation
-                            + " this: " + this);
-                }
-                event.transform(mTmpMatrix);
-                return true;
-            }
-            mTmpPoint[0] = event.getX();
-            mTmpPoint[1] = event.getY();
-            mTmpMatrix.mapPoints(mTmpPoint);
-
-            if (DEBUG) {
-                Log.d(TAG, "original: " + event.getX() + ", " + event.getY()
-                        + " new: " + mTmpPoint[0] + ", " + mTmpPoint[1]
-                        + " rect: " + this + " forceTransform: " + forceTransform
-                        + " contains: " + contains(mTmpPoint[0], mTmpPoint[1])
-                        + " this: " + this);
-            }
-
-            if (contains(mTmpPoint[0], mTmpPoint[1])) {
-                event.transform(mTmpMatrix);
-                return true;
-            }
-            return false;
-        }
-    }
 }
diff --git a/quickstep/src/com/android/quickstep/OverscrollPluginFactory.java b/quickstep/src/com/android/quickstep/OverscrollPluginFactory.java
deleted file mode 100644
index 4c261ab..0000000
--- a/quickstep/src/com/android/quickstep/OverscrollPluginFactory.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
-
-import com.android.launcher3.R;
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.ResourceBasedOverride;
-import com.android.systemui.plugins.OverscrollPlugin;
-
-/**
- * Resource overrideable factory for forcing a local overscroll plugin.
- * Override {@link R.string#overscroll_plugin_factory_class} to set a different class.
- */
-public class OverscrollPluginFactory implements ResourceBasedOverride {
-    public static final MainThreadInitializedObject<OverscrollPluginFactory> INSTANCE = forOverride(
-            OverscrollPluginFactory.class,
-            R.string.overscroll_plugin_factory_class);
-
-    /**
-     * Get the plugin that is defined locally in launcher, as opposed to a dynamic side loaded one.
-     */
-    public OverscrollPlugin getLocalOverscrollPlugin() {
-        return null;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 5d1f908..b232464 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -32,6 +32,7 @@
 import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.util.RunnableList;
 import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
+import com.android.quickstep.util.LauncherSplitScreenListener;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -49,6 +50,7 @@
     public static final int TYPE_SHOW_NEXT_FOCUS = 2;
     public static final int TYPE_HIDE = 3;
     public static final int TYPE_TOGGLE = 4;
+    public static final int TYPE_HOME = 5;
 
     private static final String TRANSITION_NAME = "Transition:toOverview";
 
@@ -154,6 +156,11 @@
                 // already hidden
                 return true;
             }
+            if (cmd.type == TYPE_HOME) {
+                mService.startActivity(mOverviewComponentObserver.getHomeIntent());
+                LauncherSplitScreenListener.INSTANCE.getNoCreate().notifySwipingToHome();
+                return true;
+            }
         } else {
             switch (cmd.type) {
                 case TYPE_SHOW:
@@ -168,6 +175,10 @@
                 }
                 case TYPE_TOGGLE:
                     return launchTask(recents, getNextTask(recents), cmd);
+                case TYPE_HOME:
+                    recents.startHome();
+                    LauncherSplitScreenListener.INSTANCE.getNoCreate().notifySwipingToHome();
+                    return true;
             }
         }
 
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index 39af0db..95ab62f 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -2,10 +2,10 @@
 
 import android.app.Activity;
 import android.content.Context;
+import android.graphics.Rect;
 import android.os.Bundle;
 
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.testing.TestInformationHandler;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.PagedOrientationHandler;
@@ -21,7 +21,7 @@
     }
 
     @Override
-    public Bundle call(String method) {
+    public Bundle call(String method, String arg) {
         final Bundle response = new Bundle();
         switch (method) {
             case TestProtocol.REQUEST_ALL_APPS_TO_OVERVIEW_SWIPE_HEIGHT: {
@@ -53,20 +53,30 @@
                         Bundle::putInt, PortraitStatesTouchController::getHotseatTop);
             }
 
-            case TestProtocol.REQUEST_OVERVIEW_SHARE_ENABLED: {
-                response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
-                        FeatureFlags.ENABLE_OVERVIEW_SHARE.get());
+            case TestProtocol.REQUEST_GET_FOCUSED_TASK_HEIGHT_FOR_TABLET: {
+                if (!mDeviceProfile.isTablet) {
+                    return null;
+                }
+                Rect focusedTaskRect = new Rect();
+                LauncherActivityInterface.INSTANCE.calculateTaskSize(mContext, mDeviceProfile,
+                        focusedTaskRect);
+                response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, focusedTaskRect.height());
                 return response;
             }
 
-            case TestProtocol.REQUEST_OVERVIEW_CONTENT_PUSH_ENABLED: {
-                response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
-                        FeatureFlags.ENABLE_OVERVIEW_CONTENT_PUSH.get());
+            case TestProtocol.REQUEST_GET_GRID_TASK_SIZE_RECT_FOR_TABLET: {
+                if (!mDeviceProfile.isTablet) {
+                    return null;
+                }
+                Rect gridTaskRect = new Rect();
+                LauncherActivityInterface.INSTANCE.calculateGridTaskSize(mContext, mDeviceProfile,
+                        gridTaskRect, PagedOrientationHandler.PORTRAIT);
+                response.putParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD, gridTaskRect);
                 return response;
             }
         }
 
-        return super.call(method);
+        return super.call(method, arg);
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 9dfcd12..ad7e4df 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -22,8 +22,8 @@
 import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_DURATION;
 import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
 import static com.android.launcher3.Utilities.createHomeIntent;
-import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
 import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
@@ -60,6 +60,8 @@
 import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.taskbar.FallbackTaskbarUIController;
+import com.android.launcher3.taskbar.TaskbarManager;
 import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.util.ActivityTracker;
 import com.android.launcher3.util.RunnableList;
@@ -73,9 +75,9 @@
 import com.android.quickstep.fallback.RecentsState;
 import com.android.quickstep.util.RecentsAtomicAnimationFactory;
 import com.android.quickstep.util.SplitSelectStateController;
+import com.android.quickstep.util.TISBindHelper;
 import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.SplitPlaceholderView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
@@ -102,6 +104,9 @@
     private ScrimView mScrimView;
     private FallbackRecentsView mFallbackRecentsView;
     private OverviewActionsView mActionsView;
+    private TISBindHelper mTISBindHelper;
+    private @Nullable TaskbarManager mTaskbarManager;
+    private @Nullable FallbackTaskbarUIController mTaskbarUIController;
 
     private Configuration mOldConfig;
 
@@ -122,13 +127,25 @@
         mActionsView = findViewById(R.id.overview_actions_view);
         SYSUI_PROGRESS.set(getRootView().getSysUiScrim(), 0f);
 
-        SplitPlaceholderView splitPlaceholderView = findViewById(R.id.split_placeholder);
-        splitPlaceholderView.init(
-                new SplitSelectStateController(mUiHandler, SystemUiProxy.INSTANCE.get(this))
-        );
-
+        SplitSelectStateController controller =
+                new SplitSelectStateController(mHandler, SystemUiProxy.INSTANCE.get(this));
         mDragLayer.recreateControllers();
-        mFallbackRecentsView.init(mActionsView, splitPlaceholderView);
+        mFallbackRecentsView.init(mActionsView, controller);
+
+        mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
+    }
+
+    private void onTISConnected(TouchInteractionService.TISBinder binder) {
+        mTaskbarManager = binder.getTaskbarManager();
+        mTaskbarManager.setActivity(this);
+    }
+
+    public void setTaskbarUIController(FallbackTaskbarUIController taskbarUIController) {
+        mTaskbarUIController = taskbarUIController;
+    }
+
+    public FallbackTaskbarUIController getTaskbarUIController() {
+        return mTaskbarUIController;
     }
 
     @Override
@@ -290,7 +307,7 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mStateManager = new StateManager<>(this, RecentsState.DEFAULT);
+        mStateManager = new StateManager<>(this, RecentsState.BG_LAUNCHER);
 
         mOldConfig = new Configuration(getResources().getConfiguration());
         initDeviceProfile();
@@ -350,6 +367,11 @@
         super.onDestroy();
         ACTIVITY_TRACKER.onActivityDestroyed(this);
         mActivityLaunchAnimationRunner = null;
+
+        mTISBindHelper.onDestroy();
+        if (mTaskbarManager != null) {
+            mTaskbarManager.clearActivity(this);
+        }
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index a21c714..e948221 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -16,9 +16,11 @@
 package com.android.quickstep;
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
 
 import android.graphics.Rect;
 import android.util.ArraySet;
+import android.view.RemoteAnimationTarget;
 
 import androidx.annotation.BinderThread;
 import androidx.annotation.UiThread;
@@ -29,6 +31,7 @@
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
+import java.util.Arrays;
 import java.util.Set;
 
 /**
@@ -39,6 +42,7 @@
         com.android.systemui.shared.system.RecentsAnimationListener {
 
     private final Set<RecentsAnimationListener> mListeners = new ArraySet<>();
+    private final SystemUiProxy mSystemUiProxy;
     private final boolean mAllowMinimizeSplitScreen;
 
     // TODO(141886704): Remove these references when they are no longer needed
@@ -46,7 +50,9 @@
 
     private boolean mCancelled;
 
-    public RecentsAnimationCallbacks(boolean allowMinimizeSplitScreen) {
+    public RecentsAnimationCallbacks(SystemUiProxy systemUiProxy,
+            boolean allowMinimizeSplitScreen) {
+        mSystemUiProxy = systemUiProxy;
         mAllowMinimizeSplitScreen = allowMinimizeSplitScreen;
     }
 
@@ -89,8 +95,19 @@
             RemoteAnimationTargetCompat[] appTargets,
             RemoteAnimationTargetCompat[] wallpaperTargets,
             Rect homeContentInsets, Rect minimizedHomeBounds) {
+        // Convert appTargets to type RemoteAnimationTarget for all apps except Home app
+        RemoteAnimationTarget[] nonHomeApps = Arrays.stream(appTargets)
+                .filter(remoteAnimationTarget ->
+                        remoteAnimationTarget.activityType != ACTIVITY_TYPE_HOME)
+                .map(RemoteAnimationTargetCompat::unwrap)
+                .toArray(RemoteAnimationTarget[]::new);
+
+        RemoteAnimationTarget[] nonAppTargets =
+                mSystemUiProxy.onGoingToRecentsLegacy(mCancelled, nonHomeApps);
+
         RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets,
-                wallpaperTargets, homeContentInsets, minimizedHomeBounds);
+                wallpaperTargets, RemoteAnimationTargetCompat.wrap(nonAppTargets),
+                homeContentInsets, minimizedHomeBounds);
         mController = new RecentsAnimationController(animationController,
                 mAllowMinimizeSplitScreen, this::onAnimationFinished);
 
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 53b6675..f343485 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -46,6 +46,8 @@
     private boolean mUseLauncherSysBarFlags = false;
     private boolean mSplitScreenMinimized = false;
     private boolean mFinishRequested = false;
+    // Only valid when mFinishRequested == true.
+    private boolean mFinishTargetIsLauncher;
     private RunnableList mPendingFinishCallbacks = new RunnableList();
 
     public RecentsAnimationController(RecentsAnimationControllerCompat controller,
@@ -145,6 +147,7 @@
 
         // Finish not yet requested
         mFinishRequested = true;
+        mFinishTargetIsLauncher = toRecents;
         mOnFinishedListener.accept(this);
         mPendingFinishCallbacks.add(callback);
         UI_HELPER_EXECUTOR.execute(() -> {
@@ -217,4 +220,12 @@
     public RecentsAnimationControllerCompat getController() {
         return mController;
     }
+
+    /**
+     * RecentsAnimationListeners can check this in onRecentsAnimationFinished() to determine whether
+     * the animation was finished to launcher vs an app.
+     */
+    public boolean getFinishTargetIsLauncher() {
+        return mFinishTargetIsLauncher;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 444d77a..e2441ed 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -18,6 +18,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.content.Intent.ACTION_USER_UNLOCKED;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
 import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
@@ -59,7 +60,6 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
-import android.util.Log;
 import android.view.MotionEvent;
 import android.view.Surface;
 
@@ -67,7 +67,6 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
 import com.android.launcher3.util.DisplayController.Info;
@@ -85,7 +84,6 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.stream.Collectors;
 
 /**
  * Manages the state of the system during a swipe up gesture.
@@ -147,7 +145,7 @@
         mContext = context;
         mDisplayController = DisplayController.INSTANCE.get(context);
         mSysUiNavMode = SysUINavigationMode.INSTANCE.get(context);
-        mDisplayId = mDisplayController.getInfo().id;
+        mDisplayId = DEFAULT_DISPLAY;
         mIsOneHandedModeSupported = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
         runOnDestroy(() -> mDisplayController.removeChangeListener(this));
         mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(context);
@@ -399,14 +397,6 @@
     }
 
     /**
-     * @return the packages of gesture-blocked activities.
-     */
-    public List<String> getGestureBlockedActivityPackages() {
-        return mGestureBlockedActivities.stream().map(ComponentName::getPackageName)
-                .collect(Collectors.toList());
-    }
-
-    /**
      * Updates the system ui state flags from SystemUI.
      */
     public void setSystemUiFlags(int stateFlags) {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index 3861bab..b6d9016 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -31,9 +31,9 @@
     public final Rect minimizedHomeBounds;
 
     public RecentsAnimationTargets(RemoteAnimationTargetCompat[] apps,
-            RemoteAnimationTargetCompat[] wallpapers, Rect homeContentInsets,
-            Rect minimizedHomeBounds) {
-        super(apps, wallpapers, new RemoteAnimationTargetCompat[0], MODE_CLOSING);
+            RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps,
+            Rect homeContentInsets, Rect minimizedHomeBounds) {
+        super(apps, wallpapers, nonApps, MODE_CLOSING);
         this.homeContentInsets = homeContentInsets;
         this.minimizedHomeBounds = minimizedHomeBounds;
     }
diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
index c032889..b20d488 100644
--- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
@@ -77,8 +77,12 @@
      * Gets the navigation bar remote animation target if exists.
      */
     public RemoteAnimationTargetCompat getNavBarRemoteAnimationTarget() {
+        return getNonAppTargetOfType(TYPE_NAVIGATION_BAR);
+    }
+
+    public RemoteAnimationTargetCompat getNonAppTargetOfType(int type) {
         for (RemoteAnimationTargetCompat target : nonApps) {
-            if (target.windowType == TYPE_NAVIGATION_BAR) {
+            if (target.windowType == type) {
                 return target;
             }
         }
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
new file mode 100644
index 0000000..825abed
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -0,0 +1,186 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.quickstep.util.AnimatorControllerWithResistance;
+import com.android.quickstep.util.LauncherSplitScreenListener;
+import com.android.quickstep.util.TaskViewSimulator;
+import com.android.quickstep.util.TransformParams;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+/**
+ * Glues together the necessary components to animate a remote target using a
+ * {@link TaskViewSimulator}
+ */
+public class RemoteTargetGluer {
+    private static final String TAG = "RemoteTargetGluer";
+
+    private RemoteTargetHandle[] mRemoteTargetHandles;
+    private SplitConfigurationOptions.StagedSplitBounds mStagedSplitBounds;
+
+    /**
+     * Use this constructor if remote targets are split-screen independent
+     */
+    public RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy,
+            RemoteAnimationTargets targets) {
+        mRemoteTargetHandles = createHandles(context, sizingStrategy, targets.apps.length);
+    }
+
+    /**
+     * Use this constructor if you want the number of handles created to match the number of active
+     * running tasks
+     */
+    public RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy) {
+        int[] splitIds = LauncherSplitScreenListener.INSTANCE.getNoCreate()
+                .getRunningSplitTaskIds();
+        mRemoteTargetHandles = createHandles(context, sizingStrategy, splitIds.length == 2 ? 2 : 1);
+    }
+
+    private RemoteTargetHandle[] createHandles(Context context,
+            BaseActivityInterface sizingStrategy, int numHandles) {
+        RemoteTargetHandle[] handles = new RemoteTargetHandle[numHandles];
+        for (int i = 0; i < numHandles; i++) {
+            TaskViewSimulator tvs = new TaskViewSimulator(context, sizingStrategy);
+            TransformParams transformParams = new TransformParams();
+            handles[i] = new RemoteTargetHandle(tvs, transformParams);
+        }
+        return handles;
+    }
+
+    /**
+     * Pairs together {@link TaskViewSimulator}s and {@link TransformParams} into a
+     * {@link RemoteTargetHandle}
+     * Assigns only the apps associated with {@param targets} into their own TaskViewSimulators.
+     * Length of targets.apps should match that of {@link #mRemoteTargetHandles}.
+     *
+     * If split screen may be active when this is called, you might want to use
+     * {@link #assignTargetsForSplitScreen(RemoteAnimationTargets)}
+     */
+    public RemoteTargetHandle[] assignTargets(RemoteAnimationTargets targets) {
+        for (int i = 0; i < mRemoteTargetHandles.length; i++) {
+            RemoteAnimationTargetCompat primaryTaskTarget = targets.apps[i];
+            mRemoteTargetHandles[i].mTransformParams.setTargetSet(
+                    createRemoteAnimationTargetsForTarget(primaryTaskTarget, targets));
+            mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(primaryTaskTarget, null);
+        }
+        return mRemoteTargetHandles;
+    }
+
+    /**
+     * Similar to {@link #assignTargets(RemoteAnimationTargets)}, except this matches the
+     * apps in targets.apps to that of the split screened tasks. If split screen is active, then
+     * {@link #mRemoteTargetHandles} index 0 will be the left/top task, index one right/bottom
+     */
+    public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets) {
+        int[] splitIds = LauncherSplitScreenListener.INSTANCE.getNoCreate()
+                .getRunningSplitTaskIds();
+        Log.d(TAG, "splitIds length: " + splitIds.length
+                + " targetAppsLength: " + targets.apps.length
+                + " remoteHandlesLength: " + mRemoteTargetHandles.length);
+        if (splitIds.length == 0 && mRemoteTargetHandles.length > 1) {
+            // There's a chance that between the creation of this class and assigning targets,
+            // LauncherSplitScreenListener may have received callback that removes split
+            mRemoteTargetHandles = new RemoteTargetHandle[]{mRemoteTargetHandles[0]};
+            Log.w(TAG, "splitTaskIds changed between creation and assignment");
+        }
+
+        RemoteAnimationTargetCompat primaryTaskTarget;
+        RemoteAnimationTargetCompat secondaryTaskTarget;
+        if (mRemoteTargetHandles.length == 1) {
+            // If we're not in split screen, the splitIds count doesn't really matter since we
+            // should always hit this case. Right now there's no use case for multiple app targets
+            // without being in split screen
+            primaryTaskTarget = targets.apps[0];
+            mRemoteTargetHandles[0].mTransformParams.setTargetSet(targets);
+            mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(primaryTaskTarget, null);
+        } else {
+            // split screen
+            primaryTaskTarget = targets.findTask(splitIds[0]);
+            secondaryTaskTarget = targets.findTask(splitIds[1]);
+
+            mStagedSplitBounds = new SplitConfigurationOptions.StagedSplitBounds(
+                    primaryTaskTarget.screenSpaceBounds,
+                    secondaryTaskTarget.screenSpaceBounds);
+            mRemoteTargetHandles[0].mTransformParams.setTargetSet(
+                    createRemoteAnimationTargetsForTarget(primaryTaskTarget, targets));
+            mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(primaryTaskTarget,
+                    mStagedSplitBounds);
+
+            mRemoteTargetHandles[1].mTransformParams.setTargetSet(
+                    createRemoteAnimationTargetsForTarget(secondaryTaskTarget, targets));
+            mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(secondaryTaskTarget,
+                    mStagedSplitBounds);
+        }
+        return mRemoteTargetHandles;
+    }
+
+    private RemoteAnimationTargets createRemoteAnimationTargetsForTarget(
+            RemoteAnimationTargetCompat target,
+            RemoteAnimationTargets targets) {
+        return new RemoteAnimationTargets(new RemoteAnimationTargetCompat[]{target},
+                targets.wallpapers, targets.nonApps, targets.targetMode);
+    }
+
+    public RemoteTargetHandle[] getRemoteTargetHandles() {
+        return mRemoteTargetHandles;
+    }
+
+    public SplitConfigurationOptions.StagedSplitBounds getStagedSplitBounds() {
+        return mStagedSplitBounds;
+    }
+
+    /**
+     * Container to keep together all the associated objects whose properties need to be updated to
+     * animate a single remote app target
+     */
+    public static class RemoteTargetHandle {
+        private final TaskViewSimulator mTaskViewSimulator;
+        private final TransformParams mTransformParams;
+        @Nullable
+        private AnimatorControllerWithResistance mPlaybackController;
+
+        public RemoteTargetHandle(TaskViewSimulator taskViewSimulator,
+                TransformParams transformParams) {
+            mTransformParams = transformParams;
+            mTaskViewSimulator = taskViewSimulator;
+        }
+
+        public TaskViewSimulator getTaskViewSimulator() {
+            return mTaskViewSimulator;
+        }
+
+        public TransformParams getTransformParams() {
+            return mTransformParams;
+        }
+
+        @Nullable
+        public AnimatorControllerWithResistance getPlaybackController() {
+            return mPlaybackController;
+        }
+
+        public void setPlaybackController(
+                @Nullable AnimatorControllerWithResistance playbackController) {
+            mPlaybackController = playbackController;
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index 678b176..35efddf 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -15,6 +15,7 @@
  */
 package com.android.quickstep;
 
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Surface.ROTATION_0;
 
 import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
@@ -25,7 +26,6 @@
 
 import android.content.Context;
 import android.content.res.Resources;
-import android.util.Log;
 import android.view.MotionEvent;
 import android.view.OrientationEventListener;
 
@@ -35,7 +35,6 @@
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.quickstep.util.RecentsOrientedState;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -146,10 +145,10 @@
         mDisplayController = DisplayController.INSTANCE.get(mContext);
         Resources resources = mContext.getResources();
         mSysUiNavMode = SysUINavigationMode.INSTANCE.get(mContext);
-        mDisplayId = mDisplayController.getInfo().id;
+        mDisplayId = DEFAULT_DISPLAY;
 
         mOrientationTouchTransformer = new OrientationTouchTransformer(resources, mMode,
-                () -> QuickStepContract.getWindowCornerRadius(resources));
+                () -> QuickStepContract.getWindowCornerRadius(mContext));
 
         // Register for navigation mode changes
         SysUINavigationMode.Mode newMode = mSysUiNavMode.addModeChangeListener(this);
diff --git a/quickstep/src/com/android/quickstep/SimpleOrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/SimpleOrientationTouchTransformer.java
new file mode 100644
index 0000000..f474796
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/SimpleOrientationTouchTransformer.java
@@ -0,0 +1,54 @@
+/*
+ * 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;
+
+import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
+import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
+import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
+
+import android.content.Context;
+import android.view.MotionEvent;
+
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.MainThreadInitializedObject;
+
+public class SimpleOrientationTouchTransformer implements
+        DisplayController.DisplayInfoChangeListener {
+
+    public static final MainThreadInitializedObject<SimpleOrientationTouchTransformer> INSTANCE =
+            new MainThreadInitializedObject<>(SimpleOrientationTouchTransformer::new);
+
+    private OrientationRectF mOrientationRectF;
+
+    public SimpleOrientationTouchTransformer(Context context) {
+        DisplayController.INSTANCE.get(context).addChangeListener(this);
+        onDisplayInfoChanged(context, DisplayController.INSTANCE.get(context).getInfo(),
+                CHANGE_ALL);
+    }
+
+    @Override
+    public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) {
+        if ((flags & (CHANGE_ROTATION | CHANGE_ACTIVE_SCREEN)) == 0) {
+            return;
+        }
+        mOrientationRectF = new OrientationRectF(0, 0, info.currentSize.y, info.currentSize.x,
+                info.rotation);
+    }
+
+    public void transform(MotionEvent ev, int rotation) {
+        mOrientationRectF.applyTransformToRotation(ev, rotation, true /* forceTransform */);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index 4495455..f64d506 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_SELECT;
 import static com.android.launcher3.config.FeatureFlags.PROTOTYPE_APP_CLOSE;
 
 import android.animation.Animator;
@@ -36,8 +37,10 @@
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.util.AppCloseConfig;
+import com.android.quickstep.util.LauncherSplitScreenListener;
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.util.RectFSpringAnim2;
 import com.android.quickstep.util.TaskViewSimulator;
@@ -46,18 +49,22 @@
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
 
-public abstract class SwipeUpAnimationLogic {
+import java.util.Arrays;
+import java.util.function.Consumer;
+
+public abstract class SwipeUpAnimationLogic implements
+        RecentsAnimationCallbacks.RecentsAnimationListener{
 
     protected static final Rect TEMP_RECT = new Rect();
+    protected final RemoteTargetGluer mTargetGluer;
 
     protected DeviceProfile mDp;
 
     protected final Context mContext;
     protected final RecentsAnimationDeviceState mDeviceState;
     protected final GestureState mGestureState;
-    protected final TaskViewSimulator mTaskViewSimulator;
 
-    protected final TransformParams mTransformParams;
+    protected RemoteTargetHandle[] mRemoteTargetHandles;
 
     // Shift in the range of [0, 1].
     // 0 => preview snapShot is completely visible, and hotseat is completely translated down
@@ -70,37 +77,48 @@
     // How much further we can drag past recents, as a factor of mTransitionDragLength.
     protected float mDragLengthFactor = 1;
 
-    protected AnimatorControllerWithResistance mWindowTransitionController;
+    protected boolean mIsSwipeForStagedSplit;
 
     public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState,
-            GestureState gestureState, TransformParams transformParams) {
+            GestureState gestureState) {
         mContext = context;
         mDeviceState = deviceState;
         mGestureState = gestureState;
-        mTaskViewSimulator = new TaskViewSimulator(context, gestureState.getActivityInterface());
-        mTransformParams = transformParams;
 
-        mTaskViewSimulator.getOrientationState().update(
-                mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
-                mDeviceState.getRotationTouchHelper().getDisplayRotation());
+        mIsSwipeForStagedSplit = ENABLE_SPLIT_SELECT.get() &&
+                LauncherSplitScreenListener.INSTANCE.getNoCreate()
+                        .getRunningSplitTaskIds().length > 1;
+
+        mTargetGluer = new RemoteTargetGluer(mContext, mGestureState.getActivityInterface());
+        mRemoteTargetHandles = mTargetGluer.getRemoteTargetHandles();
+        runActionOnRemoteHandles(remoteTargetHandle ->
+                remoteTargetHandle.getTaskViewSimulator().getOrientationState().update(
+                        mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
+                        mDeviceState.getRotationTouchHelper().getDisplayRotation()
+                ));
     }
 
     protected void initTransitionEndpoints(DeviceProfile dp) {
         mDp = dp;
-
-        mTaskViewSimulator.setDp(dp);
         mTransitionDragLength = mGestureState.getActivityInterface().getSwipeUpDestinationAndLength(
-                dp, mContext, TEMP_RECT,
-                mTaskViewSimulator.getOrientationState().getOrientationHandler());
+                dp, mContext, TEMP_RECT, mRemoteTargetHandles[0].getTaskViewSimulator()
+                        .getOrientationState().getOrientationHandler());
         mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
 
-        PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2);
-        mTaskViewSimulator.addAppToOverviewAnim(pa, LINEAR);
-        AnimatorPlaybackController normalController = pa.createPlaybackController();
-        mWindowTransitionController = AnimatorControllerWithResistance.createForRecents(
-                normalController, mContext, mTaskViewSimulator.getOrientationState(),
-                mDp, mTaskViewSimulator.recentsViewScale, AnimatedFloat.VALUE,
-                mTaskViewSimulator.recentsViewSecondaryTranslation, AnimatedFloat.VALUE);
+        for (RemoteTargetHandle remoteHandle : mRemoteTargetHandles) {
+            PendingAnimation pendingAnimation = new PendingAnimation(mTransitionDragLength * 2);
+            TaskViewSimulator taskViewSimulator = remoteHandle.getTaskViewSimulator();
+            taskViewSimulator.setDp(dp);
+            taskViewSimulator.addAppToOverviewAnim(pendingAnimation, LINEAR);
+            AnimatorPlaybackController playbackController =
+                    pendingAnimation.createPlaybackController();
+
+            remoteHandle.setPlaybackController(AnimatorControllerWithResistance.createForRecents(
+                    playbackController, mContext, taskViewSimulator.getOrientationState(),
+                    mDp, taskViewSimulator.recentsViewScale, AnimatedFloat.VALUE,
+                    taskViewSimulator.recentsViewSecondaryTranslation, AnimatedFloat.VALUE
+            ));
+        }
     }
 
     @UiThread
@@ -125,7 +143,9 @@
     public abstract void updateFinalShift();
 
     protected PagedOrientationHandler getOrientationHandler() {
-        return mTaskViewSimulator.getOrientationState().getOrientationHandler();
+        // OrientationHandler should be independent of remote target, can directly take one
+        return mRemoteTargetHandles[0].getTaskViewSimulator()
+                .getOrientationState().getOrientationHandler();
     }
 
     protected abstract class HomeAnimationFactory {
@@ -207,16 +227,35 @@
      * @param startProgress The progress of {@link #mCurrentShift} to start thw window from.
      * @return {@link RectF} represents the bounds as starting point in window space.
      */
-    protected RectF updateProgressForStartRect(Matrix outMatrix, float startProgress) {
+    protected RectF[] updateProgressForStartRect(Matrix[] outMatrix, float startProgress) {
         mCurrentShift.updateValue(startProgress);
-        mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress));
-        RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
+        RectF[] startRects = new RectF[mRemoteTargetHandles.length];
+        for (int i = 0, mRemoteTargetHandlesLength = mRemoteTargetHandles.length;
+                i < mRemoteTargetHandlesLength; i++) {
+            RemoteTargetHandle remoteHandle = mRemoteTargetHandles[i];
+            TaskViewSimulator tvs = remoteHandle.getTaskViewSimulator();
+            tvs.apply(remoteHandle.getTransformParams().setProgress(startProgress));
 
-        mTaskViewSimulator.applyWindowToHomeRotation(outMatrix);
+            startRects[i] = new RectF(tvs.getCurrentCropRect());
+            outMatrix[i] = new Matrix();
+            tvs.applyWindowToHomeRotation(outMatrix[i]);
+            tvs.getCurrentMatrix().mapRect(startRects[i]);
+        }
+        return startRects;
+    }
 
-        final RectF startRect = new RectF(cropRectF);
-        mTaskViewSimulator.getCurrentMatrix().mapRect(startRect);
-        return startRect;
+    /** Helper to avoid writing some for-loops to iterate over {@link #mRemoteTargetHandles} */
+    protected void runActionOnRemoteHandles(Consumer<RemoteTargetHandle> consumer) {
+        for (RemoteTargetHandle handle : mRemoteTargetHandles) {
+            consumer.accept(handle);
+        }
+    }
+
+    /** @return only the TaskViewSimulators from {@link #mRemoteTargetHandles} */
+    protected TaskViewSimulator[] getRemoteTaskViewSimulators() {
+        return Arrays.stream(mRemoteTargetHandles)
+                .map(remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator())
+                .toArray(TaskViewSimulator[]::new);
     }
 
     /**
@@ -224,14 +263,28 @@
      * @param startProgress The progress of {@link #mCurrentShift} to start the window from.
      * @param homeAnimationFactory The home animation factory.
      */
-    protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
+    protected RectFSpringAnim[] createWindowAnimationToHome(float startProgress,
             HomeAnimationFactory homeAnimationFactory) {
+        // TODO(b/195473584) compute separate end targets for different staged split
         final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
+        RectFSpringAnim[] out = new RectFSpringAnim[mRemoteTargetHandles.length];
+        Matrix[] homeToWindowPositionMap = new Matrix[mRemoteTargetHandles.length];
+        RectF[] startRects = updateProgressForStartRect(homeToWindowPositionMap, startProgress);
+        for (int i = 0, mRemoteTargetHandlesLength = mRemoteTargetHandles.length;
+                i < mRemoteTargetHandlesLength; i++) {
+            RemoteTargetHandle remoteHandle = mRemoteTargetHandles[i];
+            out[i] = getWindowAnimationToHomeInternal(homeAnimationFactory,
+                    targetRect, remoteHandle.getTransformParams(),
+                    remoteHandle.getTaskViewSimulator(), startRects[i], homeToWindowPositionMap[i]);
+        }
+        return out;
+    }
 
-        Matrix homeToWindowPositionMap = new Matrix();
-        final RectF startRect = updateProgressForStartRect(homeToWindowPositionMap, startProgress);
-        RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
-
+    private RectFSpringAnim getWindowAnimationToHomeInternal(
+            HomeAnimationFactory homeAnimationFactory, RectF targetRect,
+            TransformParams transformParams, TaskViewSimulator taskViewSimulator,
+            RectF startRect, Matrix homeToWindowPositionMap) {
+        RectF cropRectF = new RectF(taskViewSimulator.getCurrentCropRect());
         // Move the startRect to Launcher space as floatingIconView runs in Launcher
         Matrix windowToHomePositionMap = new Matrix();
         homeToWindowPositionMap.invert(windowToHomePositionMap);
@@ -240,17 +293,18 @@
         RectFSpringAnim anim;
         if (PROTOTYPE_APP_CLOSE.get()) {
             anim = new RectFSpringAnim2(startRect, targetRect, mContext,
-                    mTaskViewSimulator.getCurrentCornerRadius(),
+                    taskViewSimulator.getCurrentCornerRadius(),
                     homeAnimationFactory.getEndRadius(cropRectF));
         } else {
-            anim = new RectFSpringAnim(startRect, targetRect, mContext);
+            anim = new RectFSpringAnim(startRect, targetRect, mContext, mDp);
         }
         homeAnimationFactory.setAnimation(anim);
 
         SpringAnimationRunner runner = new SpringAnimationRunner(
-                homeAnimationFactory, cropRectF, homeToWindowPositionMap);
-        anim.addOnUpdateListener(runner);
+                homeAnimationFactory, cropRectF, homeToWindowPositionMap,
+                transformParams, taskViewSimulator);
         anim.addAnimatorListener(runner);
+        anim.addOnUpdateListener(runner);
         return anim;
     }
 
@@ -262,6 +316,7 @@
 
         final RectF mWindowCurrentRect = new RectF();
         final Matrix mHomeToWindowPositionMap;
+        private final TransformParams mLocalTransformParams;
         final HomeAnimationFactory mAnimationFactory;
 
         final AnimatorPlaybackController mHomeAnim;
@@ -271,17 +326,19 @@
         final float mEndRadius;
 
         SpringAnimationRunner(HomeAnimationFactory factory, RectF cropRectF,
-                Matrix homeToWindowPositionMap) {
+                Matrix homeToWindowPositionMap, TransformParams transformParams,
+                TaskViewSimulator taskViewSimulator) {
             mAnimationFactory = factory;
             mHomeAnim = factory.createActivityAnimationToHome();
             mCropRectF = cropRectF;
             mHomeToWindowPositionMap = homeToWindowPositionMap;
+            mLocalTransformParams = transformParams;
 
             cropRectF.roundOut(mCropRect);
 
             // End on a "round-enough" radius so that the shape reveal doesn't have to do too much
             // rounding at the end of the animation.
-            mStartRadius = mTaskViewSimulator.getCurrentCornerRadius();
+            mStartRadius = taskViewSimulator.getCurrentCornerRadius();
             mEndRadius = factory.getEndRadius(cropRectF);
         }
 
@@ -300,10 +357,11 @@
             if (mAnimationFactory.keepWindowOpaque()) {
                 alpha = 1f;
             }
-            mTransformParams
+            mLocalTransformParams
                     .setTargetAlpha(alpha)
                     .setCornerRadius(cornerRadius);
-            mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
+            mLocalTransformParams.applySurfaceParams(mLocalTransformParams
+                    .createSurfaceParams(this));
             mAnimationFactory.update(config, currentRect, progress,
                     mMatrix.mapRadius(cornerRadius));
         }
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index d040904..d9319a9 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -33,6 +33,8 @@
 import android.os.UserHandle;
 import android.util.Log;
 import android.view.MotionEvent;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 
 import com.android.launcher3.util.MainThreadInitializedObject;
@@ -51,6 +53,8 @@
 import com.android.wm.shell.startingsurface.IStartingWindowListener;
 import com.android.wm.shell.transition.IShellTransitions;
 
+import java.util.ArrayList;
+
 /**
  * Holds the reference to SystemUI.
  */
@@ -78,6 +82,7 @@
     private ISplitScreenListener mPendingSplitScreenListener;
     private IStartingWindowListener mPendingStartingWindowListener;
     private ISmartspaceCallback mPendingSmartspaceCallback;
+    private final ArrayList<RemoteTransitionCompat> mPendingRemoteTransitions = new ArrayList<>();
 
     // Used to dedupe calls to SystemUI
     private int mLastShelfHeight;
@@ -85,6 +90,7 @@
     private float mLastNavButtonAlpha;
     private boolean mLastNavButtonAnimate;
     private boolean mHasNavButtonAlphaBeenSet = false;
+    private Runnable mPendingSetNavButtonAlpha = null;
 
     // TODO(141886704): Find a way to remove this
     private int mLastSystemUiStateFlags;
@@ -157,6 +163,15 @@
             setSmartspaceCallback(mPendingSmartspaceCallback);
             mPendingSmartspaceCallback = null;
         }
+        for (int i = mPendingRemoteTransitions.size() - 1; i >= 0; --i) {
+            registerRemoteTransition(mPendingRemoteTransitions.get(i));
+        }
+        mPendingRemoteTransitions.clear();
+
+        if (mPendingSetNavButtonAlpha != null) {
+            mPendingSetNavButtonAlpha.run();
+            mPendingSetNavButtonAlpha = null;
+        }
     }
 
     public void clearProxy() {
@@ -240,14 +255,18 @@
         boolean changed = Float.compare(alpha, mLastNavButtonAlpha) != 0
                 || animate != mLastNavButtonAnimate
                 || !mHasNavButtonAlphaBeenSet;
-        if (mSystemUiProxy != null && changed) {
-            mLastNavButtonAlpha = alpha;
-            mLastNavButtonAnimate = animate;
-            mHasNavButtonAlphaBeenSet = true;
-            try {
-                mSystemUiProxy.setNavBarButtonAlpha(alpha, animate);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setNavBarButtonAlpha", e);
+        if (changed) {
+            if (mSystemUiProxy == null) {
+                mPendingSetNavButtonAlpha = () -> setNavBarButtonAlpha(alpha, animate);
+            } else {
+                mLastNavButtonAlpha = alpha;
+                mLastNavButtonAnimate = animate;
+                mHasNavButtonAlphaBeenSet = true;
+                try {
+                    mSystemUiProxy.setNavBarButtonAlpha(alpha, animate);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed call setNavBarButtonAlpha", e);
+                }
             }
         }
     }
@@ -346,7 +365,7 @@
             try {
                 mSystemUiProxy.setSplitScreenMinimized(minimized);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed call stopScreenPinning", e);
+                Log.w(TAG, "Failed call setSplitScreenMinimized", e);
             }
         }
     }
@@ -388,6 +407,34 @@
     }
 
     @Override
+    public void notifyTaskbarStatus(boolean visible, boolean stashed) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.notifyTaskbarStatus(visible, stashed);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call notifyTaskbarStatus with arg: " +
+                        visible + ", " + stashed, e);
+            }
+        }
+    }
+
+    /**
+     * NOTE: If called to suspend, caller MUST call this method to also un-suspend
+     * @param suspend should be true to stop auto-hide, false to resume normal behavior
+     */
+    @Override
+    public void notifyTaskbarAutohideSuspend(boolean suspend) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.notifyTaskbarAutohideSuspend(suspend);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call notifyTaskbarAutohideSuspend with arg: " +
+                        suspend, e);
+            }
+        }
+    }
+
+    @Override
     public void handleImageBundleAsScreenshot(Bundle screenImageBundle, Rect locationInScreen,
             Insets visibleInsets, Task.TaskKey task) {
         if (mSystemUiProxy != null) {
@@ -508,10 +555,17 @@
         }
     }
 
-    public void exitSplitScreen() {
+    /**
+     * To be called whenever the user exits out of split screen apps (either by launching another
+     * app or by swiping home)
+     * @param topTaskId The taskId of the new app that was launched. System will then move this task
+     *                  to the front of what the user sees while removing all other split stages.
+     *                  If swiping to home (or there is no task to put at the top), can pass in -1.
+     */
+    public void exitSplitScreen(int topTaskId) {
         if (mSplitScreen != null) {
             try {
-                mSplitScreen.exitSplitScreen();
+                mSplitScreen.exitSplitScreen(topTaskId);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call exitSplitScreen");
             }
@@ -552,6 +606,22 @@
         }
     }
 
+    /**
+     * Start multiple tasks in split-screen simultaneously.
+     */
+    public void startTasksWithLegacyTransition(int mainTaskId, Bundle mainOptions, int sideTaskId,
+            Bundle sideOptions, @SplitConfigurationOptions.StagePosition int sidePosition,
+            RemoteAnimationAdapter adapter) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSplitScreen.startTasksWithLegacyTransition(mainTaskId, mainOptions, sideTaskId,
+                        sideOptions, sidePosition, adapter);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call startTasksWithLegacyTransition");
+            }
+        }
+    }
+
     public void startShortcut(String packageName, String shortcutId, int stage, int position,
             Bundle options, UserHandle user) {
         if (mSplitScreen != null) {
@@ -585,6 +655,25 @@
         }
     }
 
+    /**
+     * Call this when going to recents so that shell can set-up and provide appropriate leashes
+     * for animation (eg. DividerBar).
+     *
+     * @param cancel true if recents starting is being cancelled.
+     * @return RemoteAnimationTargets of windows that need to animate but only exist in shell.
+     */
+    public RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel,
+            RemoteAnimationTarget[] apps) {
+        if (mSplitScreen != null) {
+            try {
+                return mSplitScreen.onGoingToRecentsLegacy(cancel, apps);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call onGoingToRecentsLegacy");
+            }
+        }
+        return null;
+    }
+
     //
     // One handed
     //
@@ -621,6 +710,8 @@
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call registerRemoteTransition");
             }
+        } else {
+            mPendingRemoteTransitions.add(remoteTransition);
         }
     }
 
@@ -632,6 +723,7 @@
                 Log.w(TAG, "Failed call registerRemoteTransition");
             }
         }
+        mPendingRemoteTransitions.remove(remoteTransition);
     }
 
     //
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 67bd85f..4b89981 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -28,6 +28,7 @@
 import android.os.SystemProperties;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.Utilities;
@@ -106,9 +107,17 @@
         // But force-finish it anyways
         finishRunningRecentsAnimation(false /* toHome */);
 
+        if (mCallbacks != null) {
+            // If mCallbacks still != null, that means we are getting this startRecentsAnimation()
+            // before the previous one got onRecentsAnimationStart(). In that case, cleanup the
+            // previous animation so it doesn't mess up/listen to state changes in this animation.
+            cleanUpRecentsAnimation();
+        }
+
         final BaseActivityInterface activityInterface = gestureState.getActivityInterface();
         mLastGestureState = gestureState;
-        mCallbacks = new RecentsAnimationCallbacks(activityInterface.allowMinimizeSplitScreen());
+        mCallbacks = new RecentsAnimationCallbacks(SystemUiProxy.INSTANCE.get(mCtx),
+                activityInterface.allowMinimizeSplitScreen());
         mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
             @Override
             public void onRecentsAnimationStart(RecentsAnimationController controller,
@@ -262,6 +271,11 @@
         mLastAppearedTaskTarget = null;
     }
 
+    @Nullable
+    public RecentsAnimationCallbacks getCurrentCallbacks() {
+        return mCallbacks;
+    }
+
     public void dump() {
         // TODO
     }
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index e75d751..c45159e 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -51,6 +51,7 @@
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskThumbnailView;
 import com.android.quickstep.views.TaskView;
+import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
@@ -63,41 +64,45 @@
 public class TaskOverlayFactory implements ResourceBasedOverride {
 
     public static List<SystemShortcut> getEnabledShortcuts(TaskView taskView,
-            DeviceProfile deviceProfile) {
+            DeviceProfile deviceProfile, TaskIdAttributeContainer taskContainer) {
         final ArrayList<SystemShortcut> shortcuts = new ArrayList<>();
         final BaseDraggingActivity activity = BaseActivity.fromContext(taskView.getContext());
+        boolean hasMultipleTasks = taskView.getTaskIds()[1] != -1;
         for (TaskShortcutFactory menuOption : MENU_OPTIONS) {
-            SystemShortcut shortcut = menuOption.getShortcut(activity, taskView);
-            if (menuOption == TaskShortcutFactory.SPLIT_SCREEN &&
-                    FeatureFlags.ENABLE_SPLIT_SELECT.get()) {
-                addSplitOptions(shortcuts, activity, taskView, deviceProfile);
+            if (hasMultipleTasks && !menuOption.showForSplitscreen()) {
                 continue;
             }
 
-            if (shortcut != null) {
+            SystemShortcut shortcut = menuOption.getShortcut(activity, taskContainer);
+            if (shortcut == null) {
+                continue;
+            }
+
+            if (menuOption == TaskShortcutFactory.SPLIT_SCREEN &&
+                    FeatureFlags.ENABLE_SPLIT_SELECT.get()) {
+                addSplitOptions(shortcuts, activity, taskView, deviceProfile);
+            } else {
                 shortcuts.add(shortcut);
             }
         }
         RecentsOrientedState orientedState = taskView.getRecentsView().getPagedViewOrientedState();
-        boolean canLauncherRotate = orientedState.canRecentsActivityRotate();
+        boolean canLauncherRotate = orientedState.isRecentsActivityRotationAllowed();
         boolean isInLandscape = orientedState.getTouchRotation() != ROTATION_0;
 
         // Add overview actions to the menu when in in-place rotate landscape mode.
         if (!canLauncherRotate && isInLandscape) {
             // Add screenshot action to task menu.
             SystemShortcut screenshotShortcut = TaskShortcutFactory.SCREENSHOT
-                    .getShortcut(activity, taskView);
+                    .getShortcut(activity, taskContainer);
             if (screenshotShortcut != null) {
-                screenshotShortcut.setHasFinishRecentsInAction(true);
                 shortcuts.add(screenshotShortcut);
             }
 
             // Add modal action only if display orientation is the same as the device orientation.
             if (orientedState.getDisplayRotation() == ROTATION_0) {
                 SystemShortcut modalShortcut = TaskShortcutFactory.MODAL
-                        .getShortcut(activity, taskView);
+                        .getShortcut(activity, taskContainer);
                 if (modalShortcut != null) {
-                    modalShortcut.setHasFinishRecentsInAction(true);
                     shortcuts.add(modalShortcut);
                 }
             }
@@ -106,10 +111,30 @@
     }
 
 
-    public static void addSplitOptions(List<SystemShortcut> outShortcuts,
+    /**
+     * Does NOT add split options in the following scenarios:
+     * * The taskView to add split options is already showing split screen tasks
+     * * There aren't at least 2 tasks in overview to show split options for
+     * * The taskView to show split options for is the focused task AND we haven't started
+     *   scrolling in overview (if we haven't scrolled, there's a split overview action so
+     *   we don't need this menu option)
+     */
+    private static void addSplitOptions(List<SystemShortcut> outShortcuts,
             BaseDraggingActivity activity, TaskView taskView, DeviceProfile deviceProfile) {
-        PagedOrientationHandler orientationHandler =
-                taskView.getRecentsView().getPagedOrientationHandler();
+        RecentsView recentsView = taskView.getRecentsView();
+        PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
+        int[] taskViewTaskIds = taskView.getTaskIds();
+        boolean taskViewHasMultipleTasks = taskViewTaskIds[0] != -1 &&
+                taskViewTaskIds[1] != -1;
+        boolean notEnoughTasksToSplit = recentsView.getTaskViewCount() < 2;
+        boolean isFocusedTask = deviceProfile.overviewShowAsGrid && taskView.isFocusedTask();
+        boolean isTaskInExpectedScrollPosition =
+                recentsView.isTaskInExpectedScrollPosition(recentsView.indexOfChild(taskView));
+        if (taskViewHasMultipleTasks || notEnoughTasksToSplit ||
+                (isFocusedTask && isTaskInExpectedScrollPosition)) {
+            return;
+        }
+
         List<SplitPositionOption> positions =
                 orientationHandler.getSplitPositionOptions(deviceProfile);
         for (SplitPositionOption option : positions) {
@@ -184,6 +209,13 @@
         }
 
         /**
+         * Called when the current task's thumbnail has changed.
+         */
+        public void refreshActionVisibility(ThumbnailData thumbnail) {
+            getActionsView().updateDisabledFlags(DISABLED_NO_THUMBNAIL, thumbnail == null);
+        }
+
+        /**
          * End rendering live tile in Overview.
          *
          * @param callback callback to run, after switching to screenshot
@@ -212,6 +244,11 @@
             }
         }
 
+        private void enterSplitSelect() {
+            RecentsView overviewPanel = mThumbnailView.getTaskView().getRecentsView();
+            overviewPanel.initiateSplitSelect(mThumbnailView.getTaskView());
+        }
+
         /**
          * Called when the overlay is no longer used.
          */
@@ -305,18 +342,14 @@
                 mTask = task;
             }
 
-            public void onShare() {
-                if (mIsAllowedByPolicy) {
-                    endLiveTileMode(() -> mImageApi.startShareActivity(null));
-                } else {
-                    showBlockedByPolicyMessage();
-                }
-            }
-
             @SuppressLint("NewApi")
             public void onScreenshot() {
                 endLiveTileMode(() -> saveScreenshot(mTask));
             }
+
+            public void onSplit() {
+                endLiveTileMode(TaskOverlay.this::enterSplitSelect);
+            }
         }
     }
 
@@ -325,10 +358,10 @@
      * controller.
      */
     public interface OverlayUICallbacks {
-        /** User has indicated they want to share the current task. */
-        void onShare();
-
         /** User has indicated they want to screenshot the current task. */
         void onScreenshot();
+
+        /** User wants to start split screen with current app. */
+        void onSplit();
     }
 }
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index a078bf3..8c4ba97 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -35,7 +35,6 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
 import com.android.launcher3.model.WellbeingModel;
 import com.android.launcher3.popup.SystemShortcut;
@@ -45,6 +44,7 @@
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskThumbnailView;
 import com.android.quickstep.views.TaskView;
+import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
 import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
@@ -61,9 +61,25 @@
  * Represents a system shortcut that can be shown for a recent task.
  */
 public interface TaskShortcutFactory {
-    SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView view);
+    SystemShortcut getShortcut(BaseDraggingActivity activity,
+            TaskIdAttributeContainer taskContainer);
 
-    TaskShortcutFactory APP_INFO = (activity, view) -> new AppInfo(activity, view.getItemInfo());
+    default boolean showForSplitscreen() {
+        return false;
+    }
+
+    TaskShortcutFactory APP_INFO = new TaskShortcutFactory() {
+        @Override
+        public SystemShortcut getShortcut(BaseDraggingActivity activity,
+                TaskIdAttributeContainer taskContainer) {
+            return new AppInfo(activity, taskContainer.getItemInfo());
+        }
+
+        @Override
+        public boolean showForSplitscreen() {
+            return true;
+        }
+    };
 
     abstract class MultiWindowFactory implements TaskShortcutFactory {
 
@@ -82,28 +98,28 @@
         protected abstract boolean onActivityStarted(BaseDraggingActivity activity);
 
         @Override
-        public SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView taskView) {
-            final Task task  = taskView.getTask();
+        public SystemShortcut getShortcut(BaseDraggingActivity activity,
+                TaskIdAttributeContainer taskContainer) {
+            final Task task  = taskContainer.getTask();
             if (!task.isDockable) {
                 return null;
             }
             if (!isAvailable(activity, task.key.displayId)) {
                 return null;
             }
-            return new MultiWindowSystemShortcut(mIconRes, mTextRes, activity, taskView, this,
+            return new MultiWindowSystemShortcut(mIconRes, mTextRes, activity, taskContainer, this,
                     mLauncherEvent);
         }
     }
 
     class SplitSelectSystemShortcut extends SystemShortcut {
         private final TaskView mTaskView;
-        private SplitPositionOption mSplitPositionOption;
+        private final SplitPositionOption mSplitPositionOption;
         public SplitSelectSystemShortcut(BaseDraggingActivity target, TaskView taskView,
                 SplitPositionOption option) {
-            super(option.mIconResId, option.mTextResId, target, taskView.getItemInfo());
+            super(option.iconResId, option.textResId, target, taskView.getItemInfo());
             mTaskView = taskView;
             mSplitPositionOption = option;
-            setEnabled(taskView.getRecentsView().getTaskViewCount() > 1);
         }
 
         @Override
@@ -112,7 +128,7 @@
         }
     }
 
-    class MultiWindowSystemShortcut extends SystemShortcut {
+    class MultiWindowSystemShortcut extends SystemShortcut<BaseDraggingActivity> {
 
         private Handler mHandler;
 
@@ -123,13 +139,14 @@
         private final LauncherEvent mLauncherEvent;
 
         public MultiWindowSystemShortcut(int iconRes, int textRes, BaseDraggingActivity activity,
-                TaskView taskView, MultiWindowFactory factory, LauncherEvent launcherEvent) {
-            super(iconRes, textRes, activity, taskView.getItemInfo());
+                TaskIdAttributeContainer taskContainer, MultiWindowFactory factory,
+                LauncherEvent launcherEvent) {
+            super(iconRes, textRes, activity, taskContainer.getItemInfo());
             mLauncherEvent = launcherEvent;
             mHandler = new Handler(Looper.getMainLooper());
-            mTaskView = taskView;
+            mTaskView = taskContainer.getTaskView();
             mRecentsView = activity.getOverviewPanel();
-            mThumbnailView = taskView.getThumbnail();
+            mThumbnailView = taskContainer.getThumbnailView();
             mFactory = factory;
         }
 
@@ -233,16 +250,6 @@
         }
 
         @Override
-        public SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView taskView) {
-            SystemShortcut shortcut = super.getShortcut(activity, taskView);
-            if (shortcut != null && FeatureFlags.ENABLE_SPLIT_SELECT.get()) {
-                // Disable if there's only one recent app for split screen
-                shortcut.setEnabled(taskView.getRecentsView().getTaskViewCount() > 1);
-            }
-            return shortcut;
-        }
-
-        @Override
         protected ActivityOptions makeLaunchOptions(Activity activity) {
             final ActivityCompat act = new ActivityCompat(activity);
             final int navBarPosition = WindowManagerWrapper.getInstance().getNavBarPosition(
@@ -284,7 +291,7 @@
         }
     };
 
-    TaskShortcutFactory PIN = (activity, tv) -> {
+    TaskShortcutFactory PIN = (activity, taskContainer) -> {
         if (!SystemUiProxy.INSTANCE.get(activity).isActive()) {
             return null;
         }
@@ -295,18 +302,20 @@
             // We shouldn't be able to pin while an app is locked.
             return null;
         }
-        return new PinSystemShortcut(activity, tv);
+        return new PinSystemShortcut(activity, taskContainer);
     };
 
-    class PinSystemShortcut extends SystemShortcut {
+    class PinSystemShortcut extends SystemShortcut<BaseDraggingActivity> {
 
         private static final String TAG = "PinSystemShortcut";
 
         private final TaskView mTaskView;
 
-        public PinSystemShortcut(BaseDraggingActivity target, TaskView tv) {
-            super(R.drawable.ic_pin, R.string.recent_task_option_pin, target, tv.getItemInfo());
-            mTaskView = tv;
+        public PinSystemShortcut(BaseDraggingActivity target,
+                TaskIdAttributeContainer taskContainer) {
+            super(R.drawable.ic_pin, R.string.recent_task_option_pin, target,
+                    taskContainer.getItemInfo());
+            mTaskView = taskContainer.getTaskView();
         }
 
         @Override
@@ -320,20 +329,22 @@
         }
     }
 
-    TaskShortcutFactory INSTALL = (activity, view) ->
+    TaskShortcutFactory INSTALL = (activity, taskContainer) ->
             InstantAppResolver.newInstance(activity).isInstantApp(activity,
-                 view.getTask().getTopComponent().getPackageName())
-                    ? new SystemShortcut.Install(activity, view.getItemInfo()) : null;
+                 taskContainer.getTask().getTopComponent().getPackageName())
+                    ? new SystemShortcut.Install(activity, taskContainer.getItemInfo()) : null;
 
-    TaskShortcutFactory WELLBEING = (activity, view) ->
-            WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, view.getItemInfo());
+    TaskShortcutFactory WELLBEING = (activity, taskContainer) ->
+            WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, taskContainer.getItemInfo());
 
-    TaskShortcutFactory SCREENSHOT = (activity, tv) -> tv.getThumbnail().getTaskOverlay()
-            .getScreenshotShortcut(activity, tv.getItemInfo());
+    TaskShortcutFactory SCREENSHOT = (activity, taskContainer) ->
+            taskContainer.getThumbnailView().getTaskOverlay()
+                    .getScreenshotShortcut(activity, taskContainer.getItemInfo());
 
-    TaskShortcutFactory MODAL = (activity, tv) -> {
+    TaskShortcutFactory MODAL = (activity, taskContainer) -> {
         if (ENABLE_OVERVIEW_SELECTIONS.get()) {
-            return tv.getThumbnail().getTaskOverlay().getModalStateSystemShortcut(tv.getItemInfo());
+            return taskContainer.getThumbnailView()
+                    .getTaskOverlay().getModalStateSystemShortcut(taskContainer.getItemInfo());
         }
         return null;
     };
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 37fda73..12b071d 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -15,6 +15,7 @@
  */
 package com.android.quickstep;
 
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
 
@@ -41,6 +42,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
 import android.content.ComponentName;
 import android.content.Context;
@@ -54,7 +56,6 @@
 import android.window.TransitionInfo;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
@@ -62,11 +63,11 @@
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.util.DisplayController;
+import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
 import com.android.quickstep.util.MultiValueUpdateListener;
 import com.android.quickstep.util.SurfaceTransactionApplier;
 import com.android.quickstep.util.TaskViewSimulator;
@@ -79,6 +80,8 @@
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 
+import java.util.ArrayList;
+
 /**
  * Utility class for helpful methods related to {@link TaskView} objects and their tasks.
  */
@@ -139,7 +142,7 @@
 
         // If the opening task id is not currently visible in overview, then fall back to normal app
         // icon launch animation
-        TaskView taskView = recentsView.getTaskView(openingTaskId);
+        TaskView taskView = recentsView.getTaskViewByTaskId(openingTaskId);
         if (taskView == null || !recentsView.isTaskViewVisible(taskView)) {
             return null;
         }
@@ -151,27 +154,7 @@
             RemoteAnimationTargetCompat[] wallpaperTargets,
             RemoteAnimationTargetCompat[] nonAppTargets, DepthController depthController,
             PendingAnimation out) {
-        boolean isRunningTask = v.isRunningTask();
-        TransformParams params = null;
-        TaskViewSimulator tsv = null;
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask) {
-            params = v.getRecentsView().getLiveTileParams();
-            tsv = v.getRecentsView().getLiveTileTaskViewSimulator();
-        }
-        createRecentsWindowAnimator(v, skipViewChanges, appTargets, wallpaperTargets, nonAppTargets,
-                depthController, out, params, tsv);
-    }
-
-    /**
-     * Creates an animation that controls the window of the opening targets for the recents launch
-     * animation.
-     */
-    public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
-            RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets,
-            RemoteAnimationTargetCompat[] nonAppTargets, DepthController depthController,
-            PendingAnimation out, @Nullable TransformParams params,
-            @Nullable TaskViewSimulator tsv) {
+        RecentsView recentsView = v.getRecentsView();
         boolean isQuickSwitch = v.isEndQuickswitchCuj();
         v.setEndQuickswitchCuj(false);
 
@@ -182,64 +165,81 @@
                         inLiveTileMode ? MODE_CLOSING : MODE_OPENING);
         final RemoteAnimationTargetCompat navBarTarget = targets.getNavBarRemoteAnimationTarget();
 
-        if (params == null) {
-            SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
-            targets.addReleaseCheck(applier);
+        SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
+        targets.addReleaseCheck(applier);
 
-            params = new TransformParams()
-                    .setSyncTransactionApplier(applier)
-                    .setTargetSet(targets);
+        RemoteTargetHandle[] remoteTargetHandles;
+        RemoteTargetHandle[] recentsViewHandles = recentsView.getRemoteTargetHandles();
+        if (v.isRunningTask() && recentsViewHandles != null) {
+            // Re-use existing handles
+            remoteTargetHandles = recentsViewHandles;
+        } else {
+            RemoteTargetGluer gluer = new RemoteTargetGluer(v.getContext(),
+                    recentsView.getSizeStrategy(), targets);
+            if (recentsViewHandles != null && recentsViewHandles.length > 1) {
+                remoteTargetHandles = gluer.assignTargetsForSplitScreen(targets);
+            } else {
+                remoteTargetHandles = gluer.assignTargets(targets);
+            }
+        }
+        for (RemoteTargetHandle remoteTargetGluer : remoteTargetHandles) {
+            remoteTargetGluer.getTransformParams().setSyncTransactionApplier(applier);
         }
 
-        final RecentsView recentsView = v.getRecentsView();
         int taskIndex = recentsView.indexOfChild(v);
         Context context = v.getContext();
         DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile();
-        boolean showAsGrid = dp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get();
+        boolean showAsGrid = dp.overviewShowAsGrid;
         boolean parallaxCenterAndAdjacentTask =
                 taskIndex != recentsView.getCurrentPage() && !showAsGrid;
         float gridTranslationSecondary = recentsView.getGridTranslationSecondary(taskIndex);
         int startScroll = recentsView.getScrollOffset(taskIndex);
 
-        TaskViewSimulator topMostSimulator = null;
+        RemoteTargetHandle[] topMostSimulators = null;
 
-        if (tsv == null && targets.apps.length > 0) {
-            tsv = new TaskViewSimulator(context, recentsView.getSizeStrategy());
-            tsv.setDp(dp);
+        if (!v.isRunningTask()) {
+            // TVSs already initialized from the running task, no need to re-init
+            for (RemoteTargetHandle targetHandle : remoteTargetHandles) {
+                TaskViewSimulator tvsLocal = targetHandle.getTaskViewSimulator();
+                tvsLocal.setDp(dp);
 
-            // RecentsView never updates the display rotation until swipe-up so the value may
-            // be stale. Use the display value instead.
-            int displayRotation = DisplayController.INSTANCE.get(context).getInfo().rotation;
-            tsv.getOrientationState().update(displayRotation, displayRotation);
+                // RecentsView never updates the display rotation until swipe-up so the value may
+                // be stale. Use the display value instead.
+                int displayRotation = DisplayController.INSTANCE.get(context).getInfo().rotation;
+                tvsLocal.getOrientationState().update(displayRotation, displayRotation);
 
-            tsv.setPreview(targets.apps[targets.apps.length - 1]);
-            tsv.fullScreenProgress.value = 0;
-            tsv.recentsViewScale.value = 1;
-            if (showAsGrid) {
-                tsv.taskSecondaryTranslation.value = gridTranslationSecondary;
+                tvsLocal.fullScreenProgress.value = 0;
+                tvsLocal.recentsViewScale.value = 1;
+                if (showAsGrid) {
+                    tvsLocal.taskSecondaryTranslation.value = gridTranslationSecondary;
+                }
+                tvsLocal.setScroll(startScroll);
+
+                // Fade in the task during the initial 20% of the animation
+                out.addFloat(targetHandle.getTransformParams(), TransformParams.TARGET_ALPHA, 0, 1,
+                        clampToProgress(LINEAR, 0, 0.2f));
             }
-            tsv.setScroll(startScroll);
-
-            // Fade in the task during the initial 20% of the animation
-            out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1,
-                    clampToProgress(LINEAR, 0, 0.2f));
         }
 
-        if (tsv != null) {
-            out.setFloat(tsv.fullScreenProgress,
+        for (RemoteTargetHandle targetHandle : remoteTargetHandles) {
+            TaskViewSimulator tvsLocal = targetHandle.getTaskViewSimulator();
+            out.setFloat(tvsLocal.fullScreenProgress,
                     AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR);
-            out.setFloat(tsv.recentsViewScale,
-                    AnimatedFloat.VALUE, tsv.getFullScreenScale(), TOUCH_RESPONSE_INTERPOLATOR);
+            out.setFloat(tvsLocal.recentsViewScale,
+                    AnimatedFloat.VALUE, tvsLocal.getFullScreenScale(),
+                    TOUCH_RESPONSE_INTERPOLATOR);
             if (showAsGrid) {
-                out.setFloat(tsv.taskSecondaryTranslation, AnimatedFloat.VALUE, 0,
+                out.setFloat(tvsLocal.taskSecondaryTranslation, AnimatedFloat.VALUE, 0,
                         TOUCH_RESPONSE_INTERPOLATOR_ACCEL_DEACCEL);
             }
-            out.setFloat(tsv.recentsViewScroll, AnimatedFloat.VALUE, 0,
+            out.setFloat(tvsLocal.recentsViewScroll, AnimatedFloat.VALUE, 0,
                     TOUCH_RESPONSE_INTERPOLATOR);
 
-            TaskViewSimulator finalTsv = tsv;
-            TransformParams finalParams = params;
-            out.addOnFrameCallback(() -> finalTsv.apply(finalParams));
+            out.addOnFrameCallback(() -> {
+                for (RemoteTargetHandle handle : remoteTargetHandles) {
+                    handle.getTaskViewSimulator().apply(handle.getTransformParams());
+                }
+            });
             if (navBarTarget != null) {
                 final Rect cropRect = new Rect();
                 out.addOnFrameListener(new MultiValueUpdateListener() {
@@ -252,15 +252,20 @@
                     public void onUpdate(float percent, boolean initOnly) {
                         final SurfaceParams.Builder navBuilder =
                                 new SurfaceParams.Builder(navBarTarget.leash);
-                        if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
-                            finalTsv.getCurrentCropRect().round(cropRect);
-                            navBuilder.withMatrix(finalTsv.getCurrentMatrix())
-                                    .withWindowCrop(cropRect)
-                                    .withAlpha(mNavFadeIn.value);
-                        } else {
-                            navBuilder.withAlpha(mNavFadeOut.value);
+
+                        // TODO Do we need to operate over multiple TVSs for the navbar leash?
+                        for (RemoteTargetHandle handle : remoteTargetHandles) {
+                            if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
+                                TaskViewSimulator taskViewSimulator = handle.getTaskViewSimulator();
+                                taskViewSimulator.getCurrentCropRect().round(cropRect);
+                                navBuilder.withMatrix(taskViewSimulator.getCurrentMatrix())
+                                        .withWindowCrop(cropRect)
+                                        .withAlpha(mNavFadeIn.value);
+                            } else {
+                                navBuilder.withAlpha(mNavFadeOut.value);
+                            }
+                            handle.getTransformParams().applySurfaceParams(navBuilder.build());
                         }
-                        finalParams.applySurfaceParams(navBuilder.build());
                     }
                 });
             } else if (inLiveTileMode) {
@@ -272,14 +277,16 @@
                     controller.animateNavigationBarToApp(RECENTS_LAUNCH_DURATION);
                 }
             }
-            topMostSimulator = tsv;
+            topMostSimulators = remoteTargetHandles;
         }
 
-        if (!skipViewChanges && parallaxCenterAndAdjacentTask && topMostSimulator != null) {
+        if (!skipViewChanges && parallaxCenterAndAdjacentTask && topMostSimulators.length > 0) {
             out.addFloat(v, VIEW_ALPHA, 1, 0, clampToProgress(LINEAR, 0.2f, 0.4f));
 
-            TaskViewSimulator simulatorToCopy = topMostSimulator;
-            simulatorToCopy.apply(params);
+            RemoteTargetHandle[] simulatorCopies = topMostSimulators;
+            for (RemoteTargetHandle handle : simulatorCopies) {
+                handle.getTaskViewSimulator().apply(handle.getTransformParams());
+            }
 
             // Mt represents the overall transformation on the thumbnailView relative to the
             // Launcher's rootView
@@ -293,36 +300,49 @@
             // During animation we apply transformation on the thumbnailView (and not the rootView)
             // to follow the TaskViewSimulator. So the final matrix applied on the thumbnailView is:
             //    Mt K(0)` K(t) Mt`
-            TaskThumbnailView ttv = v.getThumbnail();
-            RectF tvBounds = new RectF(0, 0,  ttv.getWidth(), ttv.getHeight());
-            float[] tvBoundsMapped = new float[]{0, 0,  ttv.getWidth(), ttv.getHeight()};
-            getDescendantCoordRelativeToAncestor(ttv, ttv.getRootView(), tvBoundsMapped, false);
-            RectF tvBoundsInRoot = new RectF(
-                    tvBoundsMapped[0], tvBoundsMapped[1],
-                    tvBoundsMapped[2], tvBoundsMapped[3]);
+            TaskThumbnailView[] thumbnails = v.getThumbnails();
+            Matrix[] mt = new Matrix[simulatorCopies.length];
+            Matrix[] mti = new Matrix[simulatorCopies.length];
+            for (int i = 0; i < thumbnails.length; i++) {
+                TaskThumbnailView ttv = thumbnails[i];
+                RectF localBounds = new RectF(0, 0,  ttv.getWidth(), ttv.getHeight());
+                float[] tvBoundsMapped = new float[]{0, 0,  ttv.getWidth(), ttv.getHeight()};
+                getDescendantCoordRelativeToAncestor(ttv, ttv.getRootView(), tvBoundsMapped, false);
+                RectF localBoundsInRoot = new RectF(
+                        tvBoundsMapped[0], tvBoundsMapped[1],
+                        tvBoundsMapped[2], tvBoundsMapped[3]);
+                Matrix localMt = new Matrix();
+                localMt.setRectToRect(localBounds, localBoundsInRoot, ScaleToFit.FILL);
+                mt[i] = localMt;
 
-            Matrix mt = new Matrix();
-            mt.setRectToRect(tvBounds, tvBoundsInRoot, ScaleToFit.FILL);
+                Matrix localMti = new Matrix();
+                localMti.invert(localMt);
+                mti[i] = localMti;
+            }
 
-            Matrix mti = new Matrix();
-            mt.invert(mti);
-
-            Matrix k0i = new Matrix();
-            simulatorToCopy.getCurrentMatrix().invert(k0i);
-
+            Matrix[] k0i = new Matrix[simulatorCopies.length];
+            for (int i = 0; i < simulatorCopies.length; i++) {
+                k0i[i] = new Matrix();
+                simulatorCopies[i].getTaskViewSimulator().getCurrentMatrix().invert(k0i[i]);
+            }
             Matrix animationMatrix = new Matrix();
             out.addOnFrameCallback(() -> {
-                animationMatrix.set(mt);
-                animationMatrix.postConcat(k0i);
-                animationMatrix.postConcat(simulatorToCopy.getCurrentMatrix());
-                animationMatrix.postConcat(mti);
-                ttv.setAnimationMatrix(animationMatrix);
+                for (int i = 0; i < simulatorCopies.length; i++) {
+                    animationMatrix.set(mt[i]);
+                    animationMatrix.postConcat(k0i[i]);
+                    animationMatrix.postConcat(simulatorCopies[i]
+                            .getTaskViewSimulator().getCurrentMatrix());
+                    animationMatrix.postConcat(mti[i]);
+                    thumbnails[i].setAnimationMatrix(animationMatrix);
+                }
             });
 
             out.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
-                    ttv.setAnimationMatrix(null);
+                    for (TaskThumbnailView ttv : thumbnails) {
+                        ttv.setAnimationMatrix(null);
+                    }
                 }
             });
         }
@@ -365,8 +385,8 @@
      * device is considered in multiWindowMode and things like insets and stuff change
      * and calculations have to be adjusted in the animations for that
      */
-    public static void composeRecentsSplitLaunchAnimator(@NonNull TaskView initialView,
-            @NonNull TaskView v, @NonNull TransitionInfo transitionInfo,
+    public static void composeRecentsSplitLaunchAnimator(@NonNull Task initalTask,
+            @NonNull Task secondTask, @NonNull TransitionInfo transitionInfo,
             SurfaceControl.Transaction t, @NonNull Runnable finishCallback) {
 
         final TransitionInfo.Change[] splitRoots = new TransitionInfo.Change[2];
@@ -376,7 +396,7 @@
             final int mode = change.getMode();
             // Find the target tasks' root tasks since those are the split stages that need to
             // be animated (the tasks themselves are children and thus inherit animation).
-            if (taskId == initialView.getTask().key.id || taskId == v.getTask().key.id) {
+            if (taskId == initalTask.key.id || taskId == secondTask.key.id) {
                 if (!(mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) {
                     throw new IllegalStateException(
                             "Expected task to be showing, but it is " + mode);
@@ -385,7 +405,7 @@
                     throw new IllegalStateException("Initiating multi-split launch but the split"
                             + "root of " + taskId + " is already visible or has broken hierarchy.");
                 }
-                splitRoots[taskId == initialView.getTask().key.id ? 0 : 1] =
+                splitRoots[taskId == initalTask.key.id ? 0 : 1] =
                         transitionInfo.getChange(change.getParent());
             }
         }
@@ -405,77 +425,69 @@
     }
 
     /** Legacy version (until shell transitions are enabled) */
-    public static void composeRecentsSplitLaunchAnimatorLegacy(@NonNull AnimatorSet anim,
-            @NonNull TaskView v, @NonNull RemoteAnimationTargetCompat[] appTargets,
+    public static void composeRecentsSplitLaunchAnimatorLegacy(@NonNull Task initialTask,
+            @NonNull Task secondTask, @NonNull RemoteAnimationTargetCompat[] appTargets,
             @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
-            @NonNull RemoteAnimationTargetCompat[] nonAppTargets, boolean launcherClosing,
-            @NonNull StateManager stateManager, @NonNull DepthController depthController,
-            int targetStage) {
-        PendingAnimation out = new PendingAnimation(RECENTS_LAUNCH_DURATION);
-        boolean isRunningTask = v.isRunningTask();
-        TransformParams params = null;
-        TaskViewSimulator tvs = null;
-        RecentsView recentsView = v.getRecentsView();
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask) {
-            params = recentsView.getLiveTileParams();
-            tvs = recentsView.getLiveTileTaskViewSimulator();
+            @NonNull RemoteAnimationTargetCompat[] nonAppTargets,
+            @NonNull Runnable finishCallback) {
+        final ArrayList<SurfaceControl> openingTargets = new ArrayList<>();
+        final ArrayList<SurfaceControl> closingTargets = new ArrayList<>();
+
+        for (RemoteAnimationTargetCompat appTarget : appTargets) {
+            final int taskId = appTarget.taskInfo != null ? appTarget.taskInfo.taskId : -1;
+            final int mode = appTarget.mode;
+            final SurfaceControl leash = appTarget.leash.getSurfaceControl();
+            if (leash == null) {
+                continue;
+            }
+
+            if (mode == MODE_OPENING) {
+                openingTargets.add(leash);
+            } else if (taskId == initialTask.key.id || taskId == secondTask.key.id) {
+                throw new IllegalStateException("Expected task to be opening, but it is " + mode);
+            } else if (mode == MODE_CLOSING) {
+                closingTargets.add(leash);
+            }
         }
 
-        boolean inLiveTileMode =
-                ENABLE_QUICKSTEP_LIVE_TILE.get() && recentsView.getRunningTaskIndex() != -1;
-        final RemoteAnimationTargets targets =
-                new RemoteAnimationTargets(appTargets, wallpaperTargets, nonAppTargets,
-                        inLiveTileMode ? MODE_CLOSING : MODE_OPENING);
-
-        if (params == null) {
-            SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
-            targets.addReleaseCheck(applier);
-
-            params = new TransformParams()
-                    .setSyncTransactionApplier(applier)
-                    .setTargetSet(targets);
+        for (int i = 0; i < nonAppTargets.length; ++i) {
+            final SurfaceControl leash = appTargets[i].leash.getSurfaceControl();
+            if (nonAppTargets[i].windowType == TYPE_DOCK_DIVIDER && leash != null) {
+                openingTargets.add(leash);
+            }
         }
 
-        Rect crop = new Rect();
-        Context context = v.getContext();
-        DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile();
-        if (tvs == null && targets.apps.length > 0) {
-            tvs = new TaskViewSimulator(recentsView.getContext(), recentsView.getSizeStrategy());
-            tvs.setDp(dp);
+        final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
+        animator.addUpdateListener(valueAnimator -> {
+            float progress = valueAnimator.getAnimatedFraction();
+            for (SurfaceControl leash: openingTargets) {
+                t.setAlpha(leash, progress);
+            }
+            for (SurfaceControl leash: closingTargets) {
+                t.setAlpha(leash, 1 - progress);
+            }
+            t.apply();
+        });
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                for (SurfaceControl leash: openingTargets) {
+                    t.show(leash).setAlpha(leash, 0.0f);
+                }
+                t.apply();
+            }
 
-            // RecentsView never updates the display rotation until swipe-up so the value may
-            // be stale. Use the display value instead.
-            int displayRotation = DisplayController.INSTANCE.get(recentsView.getContext())
-                    .getInfo().rotation;
-            tvs.getOrientationState().update(displayRotation, displayRotation);
-
-            tvs.setPreview(targets.apps[targets.apps.length - 1]);
-            tvs.fullScreenProgress.value = 0;
-            tvs.recentsViewScale.value = 1;
-//            tvs.setScroll(startScroll);
-
-            // Fade in the task during the initial 20% of the animation
-            out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1,
-                    clampToProgress(LINEAR, 0, 0.2f));
-        }
-
-        TaskViewSimulator topMostSimulator = null;
-
-        if (tvs != null) {
-            out.setFloat(tvs.fullScreenProgress,
-                    AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR);
-            out.setFloat(tvs.recentsViewScale,
-                    AnimatedFloat.VALUE, tvs.getFullScreenScale(), TOUCH_RESPONSE_INTERPOLATOR);
-            out.setFloat(tvs.recentsViewScroll,
-                    AnimatedFloat.VALUE, 0, TOUCH_RESPONSE_INTERPOLATOR);
-
-            TaskViewSimulator finalTsv = tvs;
-            TransformParams finalParams = params;
-            out.addOnFrameCallback(() -> finalTsv.apply(finalParams));
-            topMostSimulator = tvs;
-        }
-
-        anim.play(out.buildAnim());
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                for (SurfaceControl leash: closingTargets) {
+                    t.hide(leash);
+                }
+                super.onAnimationEnd(animation);
+                finishCallback.run();
+            }
+        });
+        animator.start();
     }
 
     public static void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
@@ -490,6 +502,10 @@
         PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
         createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets,
                 nonAppTargets, depthController, pa);
+        if (launcherClosing) {
+            // TODO(b/182592057): differentiate between "restore split" vs "launch fullscreen app"
+            TaskViewUtils.setSplitAuxiliarySurfacesShown(nonAppTargets, true);
+        }
 
         Animator childStateAnimation = null;
         // Found a visible recents task that matches the opening app, lets launch the app from there
@@ -498,7 +514,7 @@
         if (launcherClosing) {
             Context context = v.getContext();
             DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile();
-            launcherAnim = dp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()
+            launcherAnim = dp.overviewShowAsGrid
                     ? ObjectAnimator.ofFloat(recentsView, RecentsView.CONTENT_ALPHA, 0)
                     : recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
             launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
@@ -542,4 +558,21 @@
         stateManager.setCurrentAnimation(anim, childStateAnimation);
         anim.addListener(windowAnimEndListener);
     }
+
+    static void setSplitAuxiliarySurfacesShown(RemoteAnimationTargetCompat[] nonApps,
+            boolean shown) {
+        // TODO(b/182592057): make this part of the animations instead.
+        if (nonApps != null && nonApps.length > 0) {
+            SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+            for (int i = 0; i < nonApps.length; ++i) {
+                final RemoteAnimationTargetCompat targ = nonApps[i];
+                final SurfaceControl leash = targ.leash.getSurfaceControl();
+                if (targ.windowType == TYPE_DOCK_DIVIDER && leash != null) {
+                    t.setVisibility(leash, shown);
+                }
+            }
+            t.apply();
+            t.close();
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 0f5671c..1516b7a 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -40,7 +40,6 @@
 import android.app.RemoteAction;
 import android.app.Service;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
@@ -67,7 +66,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
-import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.R;
@@ -90,18 +88,18 @@
 import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
 import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer;
 import com.android.quickstep.inputconsumers.OtherActivityInputConsumer;
-import com.android.quickstep.inputconsumers.OverscrollInputConsumer;
 import com.android.quickstep.inputconsumers.OverviewInputConsumer;
 import com.android.quickstep.inputconsumers.OverviewWithoutFocusInputConsumer;
 import com.android.quickstep.inputconsumers.ResetGestureInputConsumer;
 import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer;
 import com.android.quickstep.inputconsumers.SysUiOverlayInputConsumer;
+import com.android.quickstep.inputconsumers.TaskbarStashInputConsumer;
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.AssistantUtilities;
+import com.android.quickstep.util.LauncherSplitScreenListener;
 import com.android.quickstep.util.ProtoTracer;
+import com.android.quickstep.util.ProxyScreenStatusProvider;
 import com.android.quickstep.util.SplitScreenBounds;
-import com.android.systemui.plugins.OverscrollPlugin;
-import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -126,8 +124,8 @@
  * Service connected by system-UI for handling touch interaction.
  */
 @TargetApi(Build.VERSION_CODES.R)
-public class TouchInteractionService extends Service implements PluginListener<OverscrollPlugin>,
-        ProtoTraceable<LauncherTraceProto.Builder> {
+public class TouchInteractionService extends Service
+        implements ProtoTraceable<LauncherTraceProto.Builder> {
 
     private static final String TAG = "TouchInteractionService";
 
@@ -146,8 +144,8 @@
             SystemProperties.getBoolean("persist.debug.per_window_input_rotation", false);
 
     private int mBackGestureNotificationCounter = -1;
-    @Nullable
-    private OverscrollPlugin mOverscrollPlugin;
+
+    private final TISBinder mTISBinder = new TISBinder();
 
     /**
      * Local IOverviewProxy implementation with some methods for local components
@@ -176,12 +174,6 @@
                         smartspaceTransitionController);
                 TouchInteractionService.this.initInputMonitor();
                 preloadOverview(true /* fromInit */);
-                mDeviceState.runOnUserUnlocked(() -> {
-                    final BaseActivityInterface ai =
-                            mOverviewComponentObserver.getActivityInterface();
-                    if (ai == null) return;
-                    ai.onOverviewServiceBound();
-                });
             });
             sIsInitialized = true;
         }
@@ -262,16 +254,41 @@
         }
 
         @Override
-        public void onSplitScreenSecondaryBoundsChanged(Rect bounds, Rect insets)  {
+        public void onSplitScreenSecondaryBoundsChanged(Rect bounds, Rect insets) {
             WindowBounds wb = new WindowBounds(bounds, insets);
             MAIN_EXECUTOR.execute(() -> SplitScreenBounds.INSTANCE.setSecondaryWindowBounds(wb));
         }
 
+        @BinderThread
         @Override
-        public void onImeWindowStatusChanged(int displayId, IBinder token, int vis,
-                int backDisposition, boolean showImeSwitcher) {
-            MAIN_EXECUTOR.execute(() -> mTaskbarManager.updateImeStatus(
-                    displayId, vis, backDisposition, showImeSwitcher));
+        public void onScreenTurnedOn() {
+            MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurnedOn);
+        }
+
+        @Override
+        public void onRotationProposal(int rotation, boolean isValid) {
+            executeForTaskbarManager(() -> mTaskbarManager.onRotationProposal(rotation, isValid));
+        }
+
+        @Override
+        public void disable(int displayId, int state1, int state2, boolean animate) {
+            executeForTaskbarManager(() -> mTaskbarManager
+                    .disableNavBarElements(displayId, state1, state2, animate));
+        }
+
+        @Override
+        public void onSystemBarAttributesChanged(int displayId, int behavior) {
+            executeForTaskbarManager(() -> mTaskbarManager
+                    .onSystemBarAttributesChanged(displayId, behavior));
+        }
+
+        private void executeForTaskbarManager(final Runnable r) {
+            MAIN_EXECUTOR.execute(() -> {
+                if (mTaskbarManager == null) {
+                    return;
+                }
+                r.run();
+            });
         }
 
         public TaskbarManager getTaskbarManager() {
@@ -291,7 +308,6 @@
         return sConnected;
     }
 
-
     public static boolean isInitialized() {
         return sIsInitialized;
     }
@@ -340,6 +356,7 @@
         mDeviceState.addOneHandedModeChangedCallback(this::onOneHandedModeOverlayChanged);
 
         ProtoTracer.INSTANCE.get(this).add(this);
+        LauncherSplitScreenListener.INSTANCE.get(this).init();
         sConnected = true;
     }
 
@@ -401,9 +418,6 @@
                 .getInt(KEY_BACK_NOTIFICATION_COUNT, MAX_BACK_NOTIFICATION_COUNT));
         resetHomeBounceSeenOnQuickstepEnabledFirstTime();
 
-        PluginManagerWrapper.INSTANCE.get(getBaseContext()).addPluginListener(this,
-                OverscrollPlugin.class, false /* allowMultiple */);
-
         mOverviewComponentObserver.setOverviewChangeListener(this::onOverviewTargetChange);
         onOverviewTargetChange(mOverviewComponentObserver.isHomeAndOverviewSame());
     }
@@ -445,6 +459,12 @@
         } else {
             am.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
         }
+
+        StatefulActivity newOverviewActivity = mOverviewComponentObserver.getActivityInterface()
+                .getCreatedActivity();
+        if (newOverviewActivity != null) {
+            mTaskbarManager.setActivity(newOverviewActivity);
+        }
     }
 
     @UiThread
@@ -485,7 +505,6 @@
         if (mDeviceState.isUserUnlocked()) {
             mInputConsumer.unregisterInputConsumer();
             mOverviewComponentObserver.onDestroy();
-            PluginManagerWrapper.INSTANCE.get(getBaseContext()).removePluginListener(this);
         }
         disposeEventHandlers();
         mDeviceState.destroy();
@@ -496,6 +515,7 @@
         getSystemService(AccessibilityManager.class)
                 .unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
 
+        LauncherSplitScreenListener.INSTANCE.get(this).destroy();
         mTaskbarManager.destroy();
         sConnected = false;
         super.onDestroy();
@@ -504,7 +524,7 @@
     @Override
     public IBinder onBind(Intent intent) {
         Log.d(TAG, "Touch service connected: user=" + getUserId());
-        return new TISBinder();
+        return mTISBinder;
     }
 
     private void onInputEvent(InputEvent ev) {
@@ -660,24 +680,12 @@
                         mDeviceState, event);
             }
 
-            if (FeatureFlags.ENABLE_QUICK_CAPTURE_GESTURE.get()) {
-                OverscrollPlugin plugin = null;
-                if (FeatureFlags.FORCE_LOCAL_OVERSCROLL_PLUGIN.get()) {
-                    plugin = OverscrollPluginFactory.INSTANCE.get(
-                            getApplicationContext()).getLocalOverscrollPlugin();
-                }
-
-                // If not local plugin was forced, use the actual overscroll plugin if available.
-                if (plugin == null && mOverscrollPlugin != null && mOverscrollPlugin.isActive()) {
-                    plugin = mOverscrollPlugin;
-                }
-
-                if (plugin != null) {
-                    // Put the overscroll gesture as higher priority than the Assistant or base
-                    // gestures
-                    base = new OverscrollInputConsumer(this, newGestureState, base,
-                        mInputMonitorCompat, plugin);
-                }
+            // If Taskbar is present, we listen for long press to unstash it.
+            BaseActivityInterface activityInterface = newGestureState.getActivityInterface();
+            StatefulActivity activity = activityInterface.getCreatedActivity();
+            if (activity != null && activity.getDeviceProfile().isTaskbarPresent) {
+                base = new TaskbarStashInputConsumer(this, base, mInputMonitorCompat,
+                        mTaskbarManager.getCurrentActivityContext());
             }
 
             // If Bubbles is expanded, use the overlay input consumer, which will close Bubbles
@@ -971,32 +979,6 @@
                 mInputConsumer);
     }
 
-    protected boolean shouldNotifyBackGesture() {
-        return mBackGestureNotificationCounter > 0 &&
-                !mDeviceState.getGestureBlockedActivityPackages().isEmpty();
-    }
-
-    @WorkerThread
-    protected void tryNotifyBackGesture() {
-        if (shouldNotifyBackGesture()) {
-            mBackGestureNotificationCounter--;
-            Utilities.getDevicePrefs(this).edit()
-                    .putInt(KEY_BACK_NOTIFICATION_COUNT, mBackGestureNotificationCounter).apply();
-            mDeviceState.getGestureBlockedActivityPackages().forEach(blockedPackage ->
-                    sendBroadcast(new Intent(NOTIFY_ACTION_BACK).setPackage(blockedPackage)));
-        }
-    }
-
-    @Override
-    public void onPluginConnected(OverscrollPlugin overscrollPlugin, Context context) {
-        mOverscrollPlugin = overscrollPlugin;
-    }
-
-    @Override
-    public void onPluginDisconnected(OverscrollPlugin overscrollPlugin) {
-        mOverscrollPlugin = null;
-    }
-
     @Override
     public void writeToProto(LauncherTraceProto.Builder proto) {
         TouchInteractionServiceProto.Builder serviceProto =
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index f0364eb..50b69dc 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -15,6 +15,8 @@
  */
 package com.android.quickstep.fallback;
 
+import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
+import static com.android.launcher3.anim.Interpolators.INSTANT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
@@ -77,9 +79,7 @@
         float clearAllButtonAlpha = state.hasClearAllButton() ? 1 : 0;
         setter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
                 clearAllButtonAlpha, LINEAR);
-        float overviewButtonAlpha =
-                state.hasOverviewActions() && mRecentsView.shouldShowOverviewActionsForState(state)
-                        ? 1 : 0;
+        float overviewButtonAlpha = state.hasOverviewActions() ? 1 : 0;
         setter.setFloat(mActivity.getActionsView().getVisibilityAlpha(),
                 MultiValueAlpha.VALUE, overviewButtonAlpha, LINEAR);
 
@@ -94,8 +94,9 @@
         setter.setFloat(mRecentsView, TASK_MODALNESS, state.getOverviewModalness(),
                 config.getInterpolator(ANIM_OVERVIEW_MODAL, LINEAR));
         setter.setFloat(mRecentsView, FULLSCREEN_PROGRESS, state.isFullScreen() ? 1 : 0, LINEAR);
-        setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS,
-                state.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()) ? 1f : 0f, LINEAR);
+        boolean showAsGrid = state.displayOverviewTasksAsGrid(mActivity.getDeviceProfile());
+        setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS, showAsGrid ? 1f : 0f,
+                showAsGrid ? INSTANT : FINAL_FRAME);
 
         setter.setViewBackgroundColor(mActivity.getScrimView(), state.getScrimColor(mActivity),
                 config.getInterpolator(ANIM_SCRIM_FADE, LINEAR));
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index ac3fb27..765480c 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -24,22 +24,23 @@
 import android.annotation.TargetApi;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.os.Build;
 import android.util.AttributeSet;
-import android.util.Log;
+import android.view.MotionEvent;
 
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statemanager.StateManager.StateListener;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.quickstep.FallbackActivityInterface;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.RecentsActivity;
+import com.android.quickstep.util.SplitSelectStateController;
+import com.android.quickstep.util.TaskViewSimulator;
 import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.SplitPlaceholderView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.Task.TaskKey;
@@ -62,8 +63,8 @@
     }
 
     @Override
-    public void init(OverviewActionsView actionsView, SplitPlaceholderView splitPlaceholderView) {
-        super.init(actionsView, splitPlaceholderView);
+    public void init(OverviewActionsView actionsView, SplitSelectStateController splitController) {
+        super.init(actionsView, splitController);
         setOverviewStateEnabled(true);
         setOverlayEnabled(true);
     }
@@ -78,8 +79,10 @@
      * to the home task. This allows us to handle quick-switch similarly to a quick-switching
      * from a foreground task.
      */
-    public void onGestureAnimationStartOnHome(RunningTaskInfo homeTaskInfo) {
-        mHomeTaskInfo = homeTaskInfo;
+    public void onGestureAnimationStartOnHome(RunningTaskInfo[] homeTaskInfo) {
+        // TODO(b/195607777) General fallback love, but this might be correct
+        //  Home task should be defined as the front-most task info I think?
+        mHomeTaskInfo = homeTaskInfo[0];
         onGestureAnimationStart(homeTaskInfo);
     }
 
@@ -90,12 +93,14 @@
      */
     @Override
     public void onPrepareGestureEndAnimation(
-            @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget) {
-        super.onPrepareGestureEndAnimation(animatorSet, endTarget);
+            @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget,
+            TaskViewSimulator[] taskViewSimulators) {
+        super.onPrepareGestureEndAnimation(animatorSet, endTarget, taskViewSimulators);
         if (mHomeTaskInfo != null && endTarget == RECENTS && animatorSet != null) {
-            TaskView tv = getTaskView(mHomeTaskInfo.taskId);
+            TaskView tv = getTaskViewByTaskId(mHomeTaskInfo.taskId);
             if (tv != null) {
-                PendingAnimation pa = createTaskDismissAnimation(tv, true, false, 150);
+                PendingAnimation pa = createTaskDismissAnimation(tv, true, false, 150,
+                        false /* dismissingForSplitSelection*/);
                 pa.addEndListener(e -> setCurrentTask(-1));
                 AnimatorPlaybackController controller = pa.createPlaybackController();
                 controller.dispatchOnStart();
@@ -114,16 +119,29 @@
     }
 
     @Override
-    public void setCurrentTask(int runningTaskId) {
-        super.setCurrentTask(runningTaskId);
+    public void setCurrentTask(int runningTaskViewId) {
+        super.setCurrentTask(runningTaskViewId);
+        int runningTaskId = getTaskIdsForRunningTaskView()[0];
         if (mHomeTaskInfo != null && mHomeTaskInfo.taskId != runningTaskId) {
             mHomeTaskInfo = null;
             setRunningTaskHidden(false);
         }
     }
 
+    @Nullable
     @Override
-    protected boolean shouldAddStubTaskView(RunningTaskInfo runningTaskInfo) {
+    protected TaskView getHomeTaskView() {
+        return mHomeTaskInfo != null ? getTaskViewByTaskId(mHomeTaskInfo.taskId) : null;
+    }
+
+    @Override
+    protected boolean shouldAddStubTaskView(RunningTaskInfo[] runningTaskInfos) {
+        if (runningTaskInfos.length > 1) {
+            // can't be in split screen w/ home task
+            return super.shouldAddStubTaskView(runningTaskInfos);
+        }
+
+        RunningTaskInfo runningTaskInfo = runningTaskInfos[0];
         if (mHomeTaskInfo != null && runningTaskInfo != null &&
                 mHomeTaskInfo.taskId == runningTaskInfo.taskId
                 && getTaskViewCount() == 0) {
@@ -131,7 +149,7 @@
             // show the empty recents message instead of showing a stub task and later removing it.
             return false;
         }
-        return super.shouldAddStubTaskView(runningTaskInfo);
+        return super.shouldAddStubTaskView(runningTaskInfos);
     }
 
     @Override
@@ -139,11 +157,13 @@
         // When quick-switching on 3p-launcher, we add a "stub" tile corresponding to Launcher
         // as well. This tile is never shown as we have setCurrentTaskHidden, but allows use to
         // track the index of the next task appropriately, as if we are switching on any other app.
-        if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == mRunningTaskId && !tasks.isEmpty()) {
+        // TODO(b/195607777) Confirm home task info is front-most task and not mixed in with others
+        int runningTaskId = getTaskIdsForRunningTaskView()[0];
+        if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == runningTaskId && !tasks.isEmpty()) {
             // Check if the task list has running task
             boolean found = false;
             for (Task t : tasks) {
-                if (t.key.id == mRunningTaskId) {
+                if (t.key.id == runningTaskId) {
                     found = true;
                     break;
                 }
@@ -175,6 +195,7 @@
         } else {
             if (mActivity.isInState(RecentsState.MODAL_TASK)) {
                 mActivity.getStateManager().goToState(DEFAULT);
+                resetModalVisuals();
             }
         }
     }
@@ -205,4 +226,19 @@
             setDisallowScrollToClearAll(!state.hasClearAllButton());
         }
     }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        boolean result = super.onTouchEvent(ev);
+        // Do not let touch escape to siblings below this view.
+        return result || mActivity.getStateManager().getState().overviewUi();
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+
+        // Reset modal state if full configuration changes
+        setModalStateEnabled(false);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index b6cfdce..917b58a 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -23,7 +23,6 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.util.Themes;
 import com.android.quickstep.RecentsActivity;
@@ -40,15 +39,16 @@
     private static final int FLAG_SHOW_AS_GRID = BaseState.getFlag(4);
     private static final int FLAG_SCRIM = BaseState.getFlag(5);
     private static final int FLAG_LIVE_TILE = BaseState.getFlag(6);
+    private static final int FLAG_OVERVIEW_UI = BaseState.getFlag(7);
 
     public static final RecentsState DEFAULT = new RecentsState(0,
-            FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_SHOW_AS_GRID | FLAG_SCRIM
-                    | FLAG_LIVE_TILE);
+            FLAG_DISABLE_RESTORE | FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_SHOW_AS_GRID
+                    | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_OVERVIEW_UI);
     public static final RecentsState MODAL_TASK = new ModalState(1,
             FLAG_DISABLE_RESTORE | FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_MODAL
-                    | FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_LIVE_TILE);
+                    | FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_OVERVIEW_UI);
     public static final RecentsState BACKGROUND_APP = new BackgroundAppState(2,
-            FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN);
+            FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN | FLAG_OVERVIEW_UI);
     public static final RecentsState HOME = new RecentsState(3, 0);
     public static final RecentsState BG_LAUNCHER = new LauncherState(4, 0);
 
@@ -133,11 +133,14 @@
      * For this state, whether tasks should layout as a grid rather than a list.
      */
     public boolean displayOverviewTasksAsGrid(DeviceProfile deviceProfile) {
-        return hasFlag(FLAG_SHOW_AS_GRID) && showAsGrid(deviceProfile);
+        return hasFlag(FLAG_SHOW_AS_GRID) && deviceProfile.overviewShowAsGrid;
     }
 
-    private boolean showAsGrid(DeviceProfile deviceProfile) {
-        return deviceProfile.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get();
+    /**
+     * True if the state has overview panel visible.
+     */
+    public boolean overviewUi() {
+        return hasFlag(FLAG_OVERVIEW_UI);
     }
 
     private static class ModalState extends RecentsState {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 725c7c4..0bd8832 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -29,7 +29,6 @@
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
 import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
-import static com.android.quickstep.GestureState.STATE_OVERSCROLL_WINDOW_CREATED;
 import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
 
 import android.annotation.TargetApi;
@@ -490,7 +489,7 @@
 
     @Override
     public boolean allowInterceptByParent() {
-        return !mPassedPilferInputSlop || mGestureState.hasState(STATE_OVERSCROLL_WINDOW_CREATED);
+        return !mPassedPilferInputSlop;
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java
deleted file mode 100644
index fb420a2..0000000
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.inputconsumers;
-
-import static android.view.MotionEvent.ACTION_CANCEL;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_MOVE;
-import static android.view.MotionEvent.ACTION_POINTER_DOWN;
-import static android.view.MotionEvent.ACTION_POINTER_UP;
-import static android.view.MotionEvent.ACTION_UP;
-
-import static com.android.launcher3.Utilities.squaredHypot;
-
-import static java.lang.Math.abs;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.ViewConfiguration;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.R;
-import com.android.quickstep.GestureState;
-import com.android.quickstep.InputConsumer;
-import com.android.quickstep.views.RecentsView;
-import com.android.systemui.plugins.OverscrollPlugin;
-import com.android.systemui.shared.system.InputMonitorCompat;
-
-/**
- * Input consumer for handling events to pass to an {@code OverscrollPlugin}.
- */
-public class OverscrollInputConsumer extends DelegateInputConsumer {
-    private static final String TAG = "OverscrollInputConsumer";
-    private static final boolean DEBUG_LOGS_ENABLED = false;
-    private static void debugPrint(String log) {
-        if (DEBUG_LOGS_ENABLED) {
-            Log.v(TAG, log);
-        }
-    }
-
-    private final PointF mDownPos = new PointF();
-    private final PointF mLastPos = new PointF();
-    private final PointF mStartDragPos = new PointF();
-    private final int mAngleThreshold;
-
-    private final int mFlingDistanceThresholdPx;
-    private final int mFlingVelocityThresholdPx;
-    private int mActivePointerId = -1;
-    private boolean mPassedSlop = false;
-    // True if we set ourselves as active, meaning we no longer pass events to the delegate.
-    private boolean mPassedActiveThreshold = false;
-    // When a gesture crosses this length, this recognizer will attempt to interpret touch events.
-    private final float mSquaredSlop;
-    // When a gesture crosses this length, this recognizer will become the sole active recognizer.
-    private final float mSquaredActiveThreshold;
-    // When a gesture crosses this length, the overscroll view should be shown.
-    private final float mSquaredFinishThreshold;
-    private boolean mThisDownIsIgnored = false;
-
-    private final GestureState mGestureState;
-    @Nullable
-    private final OverscrollPlugin mPlugin;
-
-    @Nullable
-    private RecentsView mRecentsView;
-
-    public OverscrollInputConsumer(Context context, GestureState gestureState,
-            InputConsumer delegate, InputMonitorCompat inputMonitor, OverscrollPlugin plugin) {
-        super(delegate, inputMonitor);
-
-        mAngleThreshold = context.getResources()
-                .getInteger(R.integer.assistant_gesture_corner_deg_threshold);
-        mFlingDistanceThresholdPx = (int) context.getResources()
-                .getDimension(R.dimen.gestures_overscroll_fling_threshold);
-        mFlingVelocityThresholdPx = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
-        mGestureState = gestureState;
-        mPlugin = plugin;
-
-        float slop = ViewConfiguration.get(context).getScaledTouchSlop();
-
-        mSquaredSlop = slop * slop;
-
-
-        float finishGestureThreshold = (int) context.getResources()
-                .getDimension(R.dimen.gestures_overscroll_finish_threshold);
-        mSquaredFinishThreshold = finishGestureThreshold * finishGestureThreshold;
-
-        float activeThreshold = (int) context.getResources()
-                .getDimension(R.dimen.gestures_overscroll_active_threshold);
-        mSquaredActiveThreshold = activeThreshold * activeThreshold;
-    }
-
-    @Override
-    public int getType() {
-        return TYPE_OVERSCROLL | mDelegate.getType();
-    }
-
-    @Override
-    public void onMotionEvent(MotionEvent ev) {
-        if (mPlugin == null) {
-            return;
-        }
-
-        debugPrint("got event, underlying activity is " + getUnderlyingActivity());
-        switch (ev.getActionMasked()) {
-            case ACTION_DOWN: {
-                debugPrint("ACTION_DOWN");
-                mActivePointerId = ev.getPointerId(0);
-                mDownPos.set(ev.getX(), ev.getY());
-                mLastPos.set(mDownPos);
-                if (mPlugin.blockOtherGestures()) {
-                    debugPrint("mPlugin.blockOtherGestures(), becoming active on ACTION_DOWN");
-                    // Otherwise, if an appear gesture is performed when the Activity is visible,
-                    // the Activity will dismiss its keyboard.
-                    mPassedActiveThreshold = true;
-                    mPassedSlop = true;
-                    mStartDragPos.set(mLastPos.x, mLastPos.y);
-                    setActive(ev);
-                }
-                break;
-            }
-            case ACTION_POINTER_DOWN: {
-                if (mState != STATE_ACTIVE) {
-                    mState = STATE_DELEGATE_ACTIVE;
-                }
-                break;
-            }
-            case ACTION_POINTER_UP: {
-                int ptrIdx = ev.getActionIndex();
-                int ptrId = ev.getPointerId(ptrIdx);
-                if (ptrId == mActivePointerId) {
-                    final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
-                    mDownPos.set(
-                            ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
-                            ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
-                    mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
-                    mActivePointerId = ev.getPointerId(newPointerIdx);
-                }
-                break;
-            }
-            case ACTION_MOVE: {
-                if (mState == STATE_DELEGATE_ACTIVE) {
-                    break;
-                }
-
-                if (!mDelegate.allowInterceptByParent()) {
-                    mState = STATE_DELEGATE_ACTIVE;
-                    break;
-                }
-
-                // Update last touch position.
-                int pointerIndex = ev.findPointerIndex(mActivePointerId);
-                if (pointerIndex == -1) {
-                    break;
-                }
-                mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
-
-                float squaredDist = squaredHypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y);
-                if ((!mPassedSlop) && (squaredDist > mSquaredSlop)) {
-                    mPassedSlop = true;
-                    mStartDragPos.set(mLastPos.x, mLastPos.y);
-                    mGestureState.setState(GestureState.STATE_OVERSCROLL_WINDOW_CREATED);
-                }
-
-                boolean becomeActive = mPassedSlop && !mPassedActiveThreshold && isOverscrolled()
-                        && (squaredDist > mSquaredActiveThreshold);
-                if (becomeActive) {
-                    debugPrint("Past slop and past threshold, set active");
-                    mPassedActiveThreshold = true;
-                    setActive(ev);
-                }
-
-                if (mPassedActiveThreshold) {
-                    debugPrint("ACTION_MOVE Relaying touch event");
-                    mPlugin.onTouchEvent(ev, getHorizontalDistancePx(), getVerticalDistancePx(),
-                            (int) Math.sqrt(mSquaredFinishThreshold), mFlingDistanceThresholdPx,
-                            mFlingVelocityThresholdPx, getDeviceState(), getUnderlyingActivity());
-                }
-
-                break;
-            }
-            case ACTION_CANCEL:
-            case ACTION_UP:
-                debugPrint("ACTION_UP");
-                if (mPassedActiveThreshold) {
-                    debugPrint("ACTION_UP Relaying touch event");
-
-                    mPlugin.onTouchEvent(ev, getHorizontalDistancePx(), getVerticalDistancePx(),
-                            (int) Math.sqrt(mSquaredFinishThreshold), mFlingDistanceThresholdPx,
-                            mFlingVelocityThresholdPx, getDeviceState(), getUnderlyingActivity());
-                }
-
-
-                mPassedSlop = false;
-                mPassedActiveThreshold = false;
-                mState = STATE_INACTIVE;
-                break;
-        }
-
-        if (mState != STATE_ACTIVE) {
-            mDelegate.onMotionEvent(ev);
-        }
-    }
-
-    private boolean isOverscrolled() {
-        if (mRecentsView == null) {
-            BaseDraggingActivity activity = mGestureState.getActivityInterface()
-                    .getCreatedActivity();
-            if (activity != null) {
-                mRecentsView = activity.getOverviewPanel();
-            }
-        }
-
-        // Make sure there isn't an app to quick switch to on our right
-        int maxIndex = 0;
-        if (mRecentsView != null && mRecentsView.hasRecentsExtraCard()) {
-            maxIndex = 1;
-        }
-
-        boolean atRightMostApp = mRecentsView == null
-                || (mRecentsView.getRunningTaskIndex() <= maxIndex);
-
-        // Check if the gesture is within our angle threshold of horizontal
-        float deltaY = abs(mLastPos.y - mDownPos.y);
-        float deltaX = mLastPos.x - mDownPos.x;
-
-        boolean angleInBounds = (Math.toDegrees(Math.atan2(deltaY, abs(deltaX))) < mAngleThreshold);
-
-        boolean overscrollVisible = mPlugin.blockOtherGestures();
-        boolean overscrollInvisibleAndLeftSwipe = !overscrollVisible && deltaX < 0;
-        boolean gestureDirectionMatchesVisibility = overscrollVisible
-                || overscrollInvisibleAndLeftSwipe;
-        return atRightMostApp && angleInBounds && gestureDirectionMatchesVisibility;
-    }
-
-    private String getDeviceState() {
-        String deviceState = OverscrollPlugin.DEVICE_STATE_UNKNOWN;
-        int consumerType = mDelegate.getType();
-        if (((consumerType & InputConsumer.TYPE_OVERVIEW) > 0)
-                || ((consumerType & InputConsumer.TYPE_OVERVIEW_WITHOUT_FOCUS)) > 0) {
-            deviceState = OverscrollPlugin.DEVICE_STATE_LAUNCHER;
-        } else if ((consumerType & InputConsumer.TYPE_OTHER_ACTIVITY) > 0) {
-            deviceState = OverscrollPlugin.DEVICE_STATE_APP;
-        } else if (((consumerType & InputConsumer.TYPE_RESET_GESTURE) > 0)
-                || ((consumerType & InputConsumer.TYPE_DEVICE_LOCKED) > 0)) {
-            deviceState = OverscrollPlugin.DEVICE_STATE_LOCKED;
-        }
-
-        return deviceState;
-    }
-
-    private int getHorizontalDistancePx() {
-        return (int) (mLastPos.x - mDownPos.x);
-    }
-
-    private int getVerticalDistancePx() {
-        return (int) (mLastPos.y - mDownPos.y);
-    }
-
-    private @NonNull String getUnderlyingActivity() {
-        // Overly defensive, got guidance on code review that something in the chain of
-        // `mGestureState.getRunningTask().topActivity` can be null and thus cause a null pointer
-        // exception to be thrown, but we aren't sure which part can be null.
-        if ((mGestureState == null) || (mGestureState.getRunningTask() == null)
-                || (mGestureState.getRunningTask().topActivity == null)) {
-            return "";
-        }
-        return mGestureState.getRunningTask().topActivity.flattenToString();
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
new file mode 100644
index 0000000..dbe260a
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
@@ -0,0 +1,104 @@
+/*
+ * 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.inputconsumers;
+
+import static com.android.launcher3.Utilities.squaredHypot;
+
+import android.content.Context;
+import android.view.GestureDetector;
+import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.MotionEvent;
+
+import com.android.launcher3.Utilities;
+import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.quickstep.InputConsumer;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+/**
+ * Listens for a long press, and cancels the current gesture if that causes Taskbar to be unstashed.
+ */
+public class TaskbarStashInputConsumer extends DelegateInputConsumer {
+
+    private final TaskbarActivityContext mTaskbarActivityContext;
+    private final GestureDetector mLongPressDetector;
+    private final float mSquaredTouchSlop;
+
+    private float mDownX, mDownY;
+    private boolean mCanceledUnstashHint;
+
+    public TaskbarStashInputConsumer(Context context, InputConsumer delegate,
+            InputMonitorCompat inputMonitor, TaskbarActivityContext taskbarActivityContext) {
+        super(delegate, inputMonitor);
+        mTaskbarActivityContext = taskbarActivityContext;
+        mSquaredTouchSlop = Utilities.squaredTouchSlop(context);
+
+        mLongPressDetector = new GestureDetector(context, new SimpleOnGestureListener() {
+            @Override
+            public void onLongPress(MotionEvent motionEvent) {
+                onLongPressDetected(motionEvent);
+            }
+        });
+    }
+
+    @Override
+    public int getType() {
+        return TYPE_TASKBAR_STASH | mDelegate.getType();
+    }
+
+    @Override
+    public void onMotionEvent(MotionEvent ev) {
+        mLongPressDetector.onTouchEvent(ev);
+        if (mState != STATE_ACTIVE) {
+            mDelegate.onMotionEvent(ev);
+
+            if (mTaskbarActivityContext != null) {
+                final float x = ev.getRawX();
+                final float y = ev.getRawY();
+                switch (ev.getAction()) {
+                    case MotionEvent.ACTION_DOWN:
+                        mDownX = x;
+                        mDownY = y;
+                        mTaskbarActivityContext.startTaskbarUnstashHint(
+                                /* animateForward = */ true);
+                        mCanceledUnstashHint = false;
+                        break;
+                    case MotionEvent.ACTION_MOVE:
+                        if (!mCanceledUnstashHint
+                                && squaredHypot(mDownX - x, mDownY - y) > mSquaredTouchSlop) {
+                            mTaskbarActivityContext.startTaskbarUnstashHint(
+                                    /* animateForward = */ false);
+                            mCanceledUnstashHint = true;
+                        }
+                        break;
+                    case MotionEvent.ACTION_UP:
+                    case MotionEvent.ACTION_CANCEL:
+                        if (!mCanceledUnstashHint) {
+                            mTaskbarActivityContext.startTaskbarUnstashHint(
+                                    /* animateForward = */ false);
+                        }
+                        break;
+                }
+            }
+        }
+    }
+
+    private void onLongPressDetected(MotionEvent motionEvent) {
+        if (mTaskbarActivityContext != null
+                && mTaskbarActivityContext.onLongPressToUnstashTaskbar()) {
+            setActive(motionEvent);
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index 4472bdc..f731cb3 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -32,6 +32,8 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.quickstep.TouchInteractionService.TISBinder;
+import com.android.quickstep.util.TISBindHelper;
 
 import java.net.URISyntaxException;
 
@@ -47,6 +49,9 @@
     private static final String EXTRA_ACCENT_COLOR_DARK_MODE = "suwColorAccentDark";
     private static final String EXTRA_ACCENT_COLOR_LIGHT_MODE = "suwColorAccentLight";
 
+    private TISBindHelper mTISBindHelper;
+    private TISBinder mBinder;
+
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -73,6 +78,34 @@
         });
 
         findViewById(R.id.hint).setAccessibilityDelegate(new SkipButtonAccessibilityDelegate());
+        mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (mBinder != null) {
+            mBinder.getTaskbarManager().setSetupUIVisible(true);
+        }
+    }
+
+    private void onTISConnected(TISBinder binder) {
+        mBinder = binder;
+        mBinder.getTaskbarManager().setSetupUIVisible(isResumed());
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (mBinder != null) {
+            mBinder.getTaskbarManager().setSetupUIVisible(false);
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mTISBindHelper.onDestroy();
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/interaction/AnimatedTaskView.java b/quickstep/src/com/android/quickstep/interaction/AnimatedTaskView.java
new file mode 100644
index 0000000..53ad138
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/interaction/AnimatedTaskView.java
@@ -0,0 +1,202 @@
+/*
+ * 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.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.annotation.ColorInt;
+import android.content.Context;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.cardview.widget.CardView;
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.android.launcher3.R;
+
+import java.util.ArrayList;
+
+/**
+ * Helper View for the gesture tutorial mock previous app task view.
+ *
+ * This helper class allows animating from a single-row layout to a two-row layout as seen in
+ * large screen devices.
+ */
+public class AnimatedTaskView extends ConstraintLayout {
+
+    private View mFullTaskView;
+    private CardView mTopTaskView;
+    private CardView mBottomTaskView;
+
+    private ViewOutlineProvider mTaskViewOutlineProvider = null;
+    private final Rect mTaskViewAnimatedRect = new Rect();
+    private float mTaskViewAnimatedRadius;
+
+    public AnimatedTaskView(@NonNull Context context) {
+        super(context);
+    }
+
+    public AnimatedTaskView(@NonNull Context context,
+            @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public AnimatedTaskView(
+            @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public AnimatedTaskView(
+            @NonNull Context context,
+            @Nullable AttributeSet attrs,
+            int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mFullTaskView = findViewById(R.id.full_task_view);
+        mTopTaskView = findViewById(R.id.top_task_view);
+        mBottomTaskView = findViewById(R.id.bottom_task_view);
+
+        setToSingleRowLayout(false);
+    }
+
+    AnimatorSet createAnimationToMultiRowLayout() {
+        if (mTaskViewOutlineProvider == null) {
+            // This is an illegal state.
+            return null;
+        }
+        Outline startOutline = new Outline();
+        mTaskViewOutlineProvider.getOutline(this, startOutline);
+        Rect outlineStartRect = new Rect();
+        startOutline.getRect(outlineStartRect);
+        int endRectBottom = mTopTaskView.getHeight();
+        float outlineStartRadius = startOutline.getRadius();
+        float outlineEndRadius = getContext().getResources().getDimensionPixelSize(
+                R.dimen.gesture_tutorial_small_task_view_corner_radius);
+
+        ValueAnimator outlineAnimator = ValueAnimator.ofFloat(0f, 1f);
+        outlineAnimator.addUpdateListener(valueAnimator -> {
+            float progress = (float) valueAnimator.getAnimatedValue();
+            mTaskViewAnimatedRect.bottom = (int) (outlineStartRect.bottom
+                    + progress * (endRectBottom - outlineStartRect.bottom));
+            mTaskViewAnimatedRadius = outlineStartRadius
+                    + progress * (outlineEndRadius - outlineStartRadius);
+            mFullTaskView.invalidateOutline();
+        });
+        outlineAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
+
+                mTaskViewAnimatedRect.set(outlineStartRect);
+                mTaskViewAnimatedRadius = outlineStartRadius;
+
+                mFullTaskView.setClipToOutline(true);
+                mFullTaskView.setOutlineProvider(new ViewOutlineProvider() {
+                    @Override
+                    public void getOutline(View view, Outline outline) {
+                        outline.setRoundRect(mTaskViewAnimatedRect, mTaskViewAnimatedRadius);
+                    }
+                });
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                mFullTaskView.setOutlineProvider(mTaskViewOutlineProvider);
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                super.onAnimationCancel(animation);
+                mFullTaskView.setOutlineProvider(mTaskViewOutlineProvider);
+            }
+        });
+
+        ArrayList<Animator> animations = new ArrayList<>();
+        animations.add(ObjectAnimator.ofFloat(
+                mBottomTaskView, View.TRANSLATION_X, -mBottomTaskView.getWidth(), 0));
+        animations.add(outlineAnimator);
+
+        AnimatorSet animatorSet = new AnimatorSet();
+        animatorSet.playTogether(animations);
+        animatorSet.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
+                setToSingleRowLayout(true);
+
+                setPadding(0, outlineStartRect.top, 0, getHeight() - outlineStartRect.bottom);
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                setToMultiRowLayout();
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                super.onAnimationCancel(animation);
+                setToMultiRowLayout();
+            }
+        });
+
+        return animatorSet;
+    }
+
+    void setToSingleRowLayout(boolean forAnimation) {
+        mFullTaskView.setVisibility(VISIBLE);
+        mTopTaskView.setVisibility(INVISIBLE);
+        mBottomTaskView.setVisibility(forAnimation ? VISIBLE : INVISIBLE);
+    }
+
+    void setToMultiRowLayout() {
+        mFullTaskView.setVisibility(INVISIBLE);
+        mTopTaskView.setVisibility(VISIBLE);
+        mBottomTaskView.setVisibility(VISIBLE);
+    }
+
+    void setFakeTaskViewFillColor(@ColorInt int colorResId) {
+        mFullTaskView.setBackgroundColor(colorResId);
+        mTopTaskView.setCardBackgroundColor(colorResId);
+        mBottomTaskView.setCardBackgroundColor(colorResId);
+    }
+
+    @Override
+    public void setClipToOutline(boolean clipToOutline) {
+        mFullTaskView.setClipToOutline(clipToOutline);
+    }
+
+    @Override
+    public void setOutlineProvider(ViewOutlineProvider provider) {
+        mTaskViewOutlineProvider = provider;
+        mFullTaskView.setOutlineProvider(mTaskViewOutlineProvider);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java
index 957f776..2f3a912 100644
--- a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java
@@ -68,7 +68,6 @@
                         showFeedback(R.string.assistant_gesture_feedback_swipe_too_far_from_corner);
                         break;
                     case ASSISTANT_COMPLETED:
-                        hideFeedback(true);
                         showRippleEffect(null);
                         showFeedback(R.string.assistant_gesture_tutorial_playground_subtitle);
                         break;
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index 3cb22f4..fb6cd8a 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -18,10 +18,9 @@
 import static com.android.quickstep.interaction.TutorialController.TutorialType.BACK_NAVIGATION;
 import static com.android.quickstep.interaction.TutorialController.TutorialType.BACK_NAVIGATION_COMPLETE;
 
+import android.annotation.LayoutRes;
 import android.graphics.PointF;
 
-import androidx.appcompat.content.res.AppCompatResources;
-
 import com.android.launcher3.R;
 import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
 import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult;
@@ -44,13 +43,27 @@
     }
 
     @Override
-    protected int getMockAppTaskThumbnailResId(boolean forDarkMode) {
-        return R.drawable.mock_conversation;
+    protected int getMockAppTaskLayoutResId() {
+        return getMockAppTaskCurrentPageLayoutResId();
+    }
+
+    @LayoutRes
+    int getMockAppTaskCurrentPageLayoutResId() {
+        return mTutorialFragment.isLargeScreen()
+                ? R.layout.gesture_tutorial_foldable_mock_conversation
+                : R.layout.gesture_tutorial_mock_conversation;
+    }
+
+    @LayoutRes
+    int getMockAppTaskPreviousPageLayoutResId() {
+        return mTutorialFragment.isLargeScreen()
+                ? R.layout.gesture_tutorial_foldable_mock_conversation_list
+                : R.layout.gesture_tutorial_mock_conversation_list;
     }
 
     @Override
     public void onBackGestureAttempted(BackGestureResult result) {
-        if (mGestureCompleted) {
+        if (isGestureCompleted()) {
             return;
         }
         switch (mTutorialType) {
@@ -70,10 +83,8 @@
         switch (result) {
             case BACK_COMPLETED_FROM_LEFT:
             case BACK_COMPLETED_FROM_RIGHT:
-                mTutorialFragment.releaseGestureVideoView();
-                hideFeedback(true);
-                mFakeTaskView.setBackground(AppCompatResources.getDrawable(mContext,
-                        R.drawable.mock_conversations_list));
+                mTutorialFragment.releaseFeedbackAnimation();
+                updateFakeAppTaskViewLayout(getMockAppTaskPreviousPageLayoutResId());
                 int subtitleResId = mTutorialFragment.isAtFinalStep()
                         ? R.string.back_gesture_feedback_complete_without_follow_up
                         : R.string.back_gesture_feedback_complete_with_overview_follow_up;
@@ -94,7 +105,7 @@
 
     @Override
     public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
-        if (mGestureCompleted) {
+        if (isGestureCompleted()) {
             return;
         }
         if (mTutorialType == BACK_NAVIGATION_COMPLETE) {
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java
index 1740f68..f54734d 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java
@@ -15,6 +15,10 @@
  */
 package com.android.quickstep.interaction;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.view.MotionEvent;
 import android.view.View;
 
@@ -23,18 +27,76 @@
 import com.android.launcher3.R;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
 
+import java.util.ArrayList;
+
 /** Shows the Back gesture interactive tutorial. */
 public class BackGestureTutorialFragment extends TutorialFragment {
+
     @Nullable
     @Override
-    Integer getFeedbackVideoResId(boolean forDarkMode) {
-        return R.drawable.gesture_tutorial_motion_back;
+    Integer getEdgeAnimationResId() {
+        return R.drawable.gesture_tutorial_loop_back;
     }
 
     @Nullable
     @Override
-    Integer getGestureVideoResId() {
-        return R.drawable.gesture_tutorial_loop_back;
+    protected Animator createGestureAnimation() {
+        if (!(mTutorialController instanceof BackGestureTutorialController)) {
+            return null;
+        }
+        BackGestureTutorialController controller =
+                (BackGestureTutorialController) mTutorialController;
+        float fingerDotStartTranslationX = (float) -(mRootView.getWidth() / 2);
+
+        AnimatorSet fingerDotAppearanceAnimator = controller.createFingerDotAppearanceAnimatorSet();
+        fingerDotAppearanceAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
+                mFingerDotView.setTranslationX(fingerDotStartTranslationX);
+            }
+        });
+
+        ObjectAnimator translationAnimator = ObjectAnimator.ofFloat(
+                mFingerDotView, View.TRANSLATION_X, fingerDotStartTranslationX, 0);
+        translationAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                controller.updateFakeAppTaskViewLayout(
+                        controller.getMockAppTaskPreviousPageLayoutResId());
+            }
+        });
+        translationAnimator.setDuration(1000);
+
+        Animator animationPause = controller.createAnimationPause();
+        animationPause.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                controller.updateFakeAppTaskViewLayout(
+                        controller.getMockAppTaskCurrentPageLayoutResId());
+            }
+        });
+        ArrayList<Animator> animators = new ArrayList<>();
+
+        animators.add(fingerDotAppearanceAnimator);
+        animators.add(translationAnimator);
+        animators.add(controller.createFingerDotDisappearanceAnimatorSet());
+        animators.add(animationPause);
+
+        AnimatorSet finalAnimation = new AnimatorSet();
+        finalAnimation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                super.onAnimationCancel(animation);
+                controller.updateFakeAppTaskViewLayout(
+                        controller.getMockAppTaskCurrentPageLayoutResId());
+            }
+        });
+        finalAnimation.playSequentially(animators);
+
+        return finalAnimation;
     }
 
     @Override
@@ -49,6 +111,7 @@
 
     @Override
     public boolean onTouch(View view, MotionEvent motionEvent) {
+        releaseFeedbackAnimation();
         if (motionEvent.getAction() == MotionEvent.ACTION_DOWN && mTutorialController != null) {
             mTutorialController.setRippleHotspot(motionEvent.getX(), motionEvent.getY());
         }
diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java
index 0521db4..b2b2f59 100644
--- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java
+++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java
@@ -43,7 +43,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.util.VibratorWrapper;
+import com.android.quickstep.util.VibratorWrapper;
 
 /** Forked from platform/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java. */
 public class EdgeBackGesturePanel extends View {
@@ -282,9 +282,7 @@
                         .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
         int currentNightMode =
                 context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
-        mPaint.setColor(context.getColor(currentNightMode == Configuration.UI_MODE_NIGHT_YES
-                ? R.color.back_arrow_color_light
-                : R.color.back_arrow_color_dark));
+        mPaint.setColor(context.getColor(R.color.gesture_tutorial_back_arrow_color));
         loadDimens();
         updateArrowDirection();
 
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
index 819c91c..a45f273 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
@@ -42,13 +42,15 @@
     }
 
     @Override
-    protected int getMockAppTaskThumbnailResId(boolean forDarkMode) {
-        return forDarkMode ? R.drawable.mock_webpage_dark_mode : R.drawable.mock_webpage_light_mode;
+    protected int getMockAppTaskLayoutResId() {
+        return mTutorialFragment.isLargeScreen()
+                ? R.layout.gesture_tutorial_foldable_mock_webpage
+                : R.layout.gesture_tutorial_mock_webpage;
     }
 
     @Override
     public void onBackGestureAttempted(BackGestureResult result) {
-        if (mGestureCompleted) {
+        if (isGestureCompleted()) {
             return;
         }
         switch (mTutorialType) {
@@ -73,14 +75,14 @@
 
     @Override
     public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
-        if (mGestureCompleted) {
+        if (isGestureCompleted()) {
             return;
         }
         switch (mTutorialType) {
             case HOME_NAVIGATION:
                 switch (result) {
                     case HOME_GESTURE_COMPLETED: {
-                        mTutorialFragment.releaseGestureVideoView();
+                        mTutorialFragment.releaseFeedbackAnimation();
                         animateFakeTaskViewHome(finalVelocity, null);
                         int subtitleResId = mTutorialFragment.isAtFinalStep()
                                 ? R.string.home_gesture_feedback_complete_without_follow_up
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java
index 9572637..dcae07d 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java
@@ -15,25 +15,73 @@
  */
 package com.android.quickstep.interaction;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.view.MotionEvent;
+import android.view.View;
+
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.R;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
 
+import java.util.ArrayList;
+
 /** Shows the Home gesture interactive tutorial. */
 public class HomeGestureTutorialFragment extends TutorialFragment {
+
     @Nullable
     @Override
-    Integer getFeedbackVideoResId(boolean forDarkMode) {
-        return forDarkMode
-                ? R.drawable.gesture_tutorial_motion_home_dark_mode
-                : R.drawable.gesture_tutorial_motion_home_light_mode;
+    Integer getEdgeAnimationResId() {
+        return R.drawable.gesture_tutorial_loop_home;
     }
 
     @Nullable
     @Override
-    Integer getGestureVideoResId() {
-        return R.drawable.gesture_tutorial_loop_home;
+    protected Animator createGestureAnimation() {
+        if (!(mTutorialController instanceof HomeGestureTutorialController)) {
+            return null;
+        }
+        float fingerDotStartTranslationY = (float) mRootView.getFullscreenHeight() / 2;
+        HomeGestureTutorialController controller =
+                (HomeGestureTutorialController) mTutorialController;
+
+        AnimatorSet fingerDotAppearanceAnimator = controller.createFingerDotAppearanceAnimatorSet();
+        fingerDotAppearanceAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
+                mFingerDotView.setTranslationY(fingerDotStartTranslationY);
+            }
+        });
+
+        Animator animationPause = controller.createAnimationPause();
+        animationPause.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                controller.resetFakeTaskView();
+            }
+        });
+        ArrayList<Animator> animators = new ArrayList<>();
+
+        animators.add(fingerDotAppearanceAnimator);
+        animators.add(controller.createFingerDotHomeSwipeAnimator(fingerDotStartTranslationY));
+        animators.add(controller.createFingerDotDisappearanceAnimatorSet());
+        animators.add(animationPause);
+
+        AnimatorSet finalAnimation = new AnimatorSet();
+        finalAnimation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                super.onAnimationCancel(animation);
+                controller.resetFakeTaskView();
+            }
+        });
+        finalAnimation.playSequentially(animators);
+
+        return finalAnimation;
     }
 
     @Override
@@ -45,4 +93,10 @@
     Class<? extends TutorialController> getControllerClass() {
         return HomeGestureTutorialController.class;
     }
+
+    @Override
+    public boolean onTouch(View view, MotionEvent motionEvent) {
+        releaseFeedbackAnimation();
+        return super.onTouch(view, motionEvent);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
index a9a9e2a..851cccf 100644
--- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
@@ -16,7 +16,6 @@
 package com.android.quickstep.interaction;
 
 import static com.android.launcher3.Utilities.squaredHypot;
-import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
 import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_COMPLETED;
 import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_NOT_STARTED_BAD_ANGLE;
 import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_NOT_STARTED_SWIPE_TOO_SHORT;
@@ -26,6 +25,7 @@
 import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION;
 import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.OVERVIEW_GESTURE_COMPLETED;
 import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE;
+import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
 
 import android.animation.ValueAnimator;
 import android.content.Context;
@@ -47,11 +47,11 @@
 import com.android.launcher3.R;
 import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.util.VibratorWrapper;
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.util.MotionPauseDetector;
 import com.android.quickstep.util.NavBarPosition;
 import com.android.quickstep.util.TriggerSwipeUpTouchTracker;
+import com.android.quickstep.util.VibratorWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 
 /** Utility class to handle Home and Assistant gestures. */
@@ -120,7 +120,7 @@
         mAssistantGestureDetector = new GestureDetector(context, new AssistantGestureListener());
         int assistantWidth = resources.getDimensionPixelSize(R.dimen.gestures_assistant_width);
         final float assistantHeight = Math.max(mBottomGestureHeight,
-                QuickStepContract.getWindowCornerRadius(resources));
+                QuickStepContract.getWindowCornerRadius(context));
         mAssistantLeftRegion.bottom = mAssistantRightRegion.bottom = mDisplaySize.y;
         mAssistantLeftRegion.top = mAssistantRightRegion.top = mDisplaySize.y - assistantHeight;
         mAssistantLeftRegion.left = 0;
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index 77ddb2b..24ef1fa 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 
+import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.annotation.TargetApi;
 import android.graphics.PointF;
@@ -29,6 +30,8 @@
 import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
 import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult;
 
+import java.util.ArrayList;
+
 /** A {@link TutorialController} for the Overview tutorial. */
 @TargetApi(Build.VERSION_CODES.R)
 final class OverviewGestureTutorialController extends SwipeUpGestureTutorialController {
@@ -49,13 +52,15 @@
     }
 
     @Override
-    protected int getMockAppTaskThumbnailResId(boolean forDarkMode) {
-        return R.drawable.mock_conversations_list;
+    protected int getMockAppTaskLayoutResId() {
+        return mTutorialFragment.isLargeScreen()
+                ? R.layout.gesture_tutorial_foldable_mock_conversation_list
+                : R.layout.gesture_tutorial_mock_conversation_list;
     }
 
     @Override
     public void onBackGestureAttempted(BackGestureResult result) {
-        if (mGestureCompleted) {
+        if (isGestureCompleted()) {
             return;
         }
         switch (mTutorialType) {
@@ -80,7 +85,7 @@
 
     @Override
     public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
-        if (mGestureCompleted) {
+        if (isGestureCompleted()) {
             return;
         }
         switch (mTutorialType) {
@@ -98,13 +103,8 @@
                         showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge);
                         break;
                     case OVERVIEW_GESTURE_COMPLETED:
-                        mTutorialFragment.releaseGestureVideoView();
-                        PendingAnimation anim = new PendingAnimation(300);
-                        anim.setFloat(mTaskViewSwipeUpAnimation
-                                .getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL);
-                        AnimatorSet animset = anim.buildAnim();
-                        animset.start();
-                        mRunningWindowAnim = SwipeUpAnimationLogic.RunningWindowAnim.wrap(animset);
+                        mTutorialFragment.releaseFeedbackAnimation();
+                        animateTaskViewToOverview();
                         onMotionPaused(true /*arbitrary value*/);
                         int subtitleResId = mTutorialFragment.getNumSteps() > 1
                                 && mTutorialFragment.isAtFinalStep()
@@ -126,4 +126,27 @@
                 break;
         }
     }
+
+    public void animateTaskViewToOverview() {
+        PendingAnimation anim = new PendingAnimation(TASK_VIEW_END_ANIMATION_DURATION_MILLIS);
+        anim.setFloat(mTaskViewSwipeUpAnimation
+                .getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL);
+
+        ArrayList<Animator> animators = new ArrayList<>();
+
+        if (mTutorialFragment.isLargeScreen()) {
+            Animator multiRowAnimation = mFakePreviousTaskView.createAnimationToMultiRowLayout();
+
+            if (multiRowAnimation != null) {
+                multiRowAnimation.setDuration(TASK_VIEW_END_ANIMATION_DURATION_MILLIS);
+                animators.add(multiRowAnimation);
+            }
+        }
+        animators.add(anim.buildAnim());
+
+        AnimatorSet animset = new AnimatorSet();
+        animset.playTogether(animators);
+        animset.start();
+        mRunningWindowAnim = SwipeUpAnimationLogic.RunningWindowAnim.wrap(animset);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
index d2ec327..57a76ca 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
@@ -15,25 +15,85 @@
  */
 package com.android.quickstep.interaction;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.view.MotionEvent;
+import android.view.View;
+
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.R;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
 
+import java.util.ArrayList;
+
 /** Shows the Overview gesture interactive tutorial. */
 public class OverviewGestureTutorialFragment extends TutorialFragment {
+
     @Nullable
     @Override
-    Integer getFeedbackVideoResId(boolean forDarkMode) {
-        return forDarkMode
-                ? R.drawable.gesture_tutorial_motion_overview_dark_mode
-                : R.drawable.gesture_tutorial_motion_overview_light_mode;
+    Integer getEdgeAnimationResId() {
+        return R.drawable.gesture_tutorial_loop_overview;
     }
 
     @Nullable
     @Override
-    Integer getGestureVideoResId() {
-        return R.drawable.gesture_tutorial_loop_overview;
+    protected Animator createGestureAnimation() {
+        if (!(mTutorialController instanceof OverviewGestureTutorialController)) {
+            return null;
+        }
+        float fingerDotStartTranslationY = (float) mRootView.getFullscreenHeight() / 2;
+        OverviewGestureTutorialController controller =
+                (OverviewGestureTutorialController) mTutorialController;
+
+        AnimatorSet fingerDotAppearanceAnimator = controller.createFingerDotAppearanceAnimatorSet();
+        fingerDotAppearanceAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
+
+                mFingerDotView.setTranslationY(fingerDotStartTranslationY);
+            }
+        });
+
+        AnimatorSet fingerDotDisappearanceAnimator =
+                controller.createFingerDotDisappearanceAnimatorSet();
+        fingerDotDisappearanceAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
+                controller.animateTaskViewToOverview();
+            }
+        });
+
+        Animator animationPause = controller.createAnimationPause();
+        animationPause.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                controller.resetFakeTaskView();
+            }
+        });
+        ArrayList<Animator> animators = new ArrayList<>();
+
+        animators.add(fingerDotAppearanceAnimator);
+        animators.add(controller.createFingerDotOverviewSwipeAnimator(fingerDotStartTranslationY));
+        animators.add(controller.createAnimationPause());
+        animators.add(fingerDotDisappearanceAnimator);
+        animators.add(animationPause);
+
+        AnimatorSet finalAnimation = new AnimatorSet();
+        finalAnimation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                super.onAnimationCancel(animation);
+                controller.resetFakeTaskView();
+            }
+        });
+        finalAnimation.playSequentially(animators);
+
+        return finalAnimation;
     }
 
     @Override
@@ -45,4 +105,10 @@
     Class<? extends TutorialController> getControllerClass() {
         return OverviewGestureTutorialController.class;
     }
+
+    @Override
+    public boolean onTouch(View view, MotionEvent motionEvent) {
+        releaseFeedbackAnimation();
+        return super.onTouch(view, motionEvent);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java b/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java
index db1afc2..ac0c17d 100644
--- a/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java
+++ b/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java
@@ -16,8 +16,10 @@
 package com.android.quickstep.interaction;
 
 import android.content.Context;
+import android.graphics.Insets;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
+import android.view.WindowInsets;
 import android.widget.RelativeLayout;
 
 import androidx.fragment.app.FragmentManager;
@@ -41,4 +43,13 @@
         return ((TutorialFragment) FragmentManager.findFragment(this))
                 .onInterceptTouch(motionEvent);
     }
+
+    /**
+     * Returns this view's fullscreen height. This method is agnostic of this view's actual height.
+     */
+    public int getFullscreenHeight() {
+        Insets insets = getRootWindowInsets().getInsets(WindowInsets.Type.systemBars());
+
+        return getHeight() + insets.top + insets.bottom;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index b2183d6..a923519 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -25,6 +25,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Outline;
@@ -32,7 +33,6 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Build;
-import android.util.DisplayMetrics;
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.ViewOutlineProvider;
@@ -50,6 +50,7 @@
 import com.android.quickstep.GestureState;
 import com.android.quickstep.OverviewComponentObserver;
 import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.RemoteTargetGluer;
 import com.android.quickstep.SwipeUpAnimationLogic;
 import com.android.quickstep.SwipeUpAnimationLogic.RunningWindowAnim;
 import com.android.quickstep.util.AppCloseConfig;
@@ -62,23 +63,25 @@
 
     private static final int FAKE_PREVIOUS_TASK_MARGIN = Utilities.dpToPx(12);
 
+    protected static final long TASK_VIEW_END_ANIMATION_DURATION_MILLIS = 300;
+    private static final long HOME_SWIPE_ANIMATION_DURATION_MILLIS = 625;
+    private static final long OVERVIEW_SWIPE_ANIMATION_DURATION_MILLIS = 1000;
+
     final ViewSwipeUpAnimation mTaskViewSwipeUpAnimation;
     private float mFakeTaskViewRadius;
-    private Rect mFakeTaskViewRect = new Rect();
+    private final Rect mFakeTaskViewRect = new Rect();
     RunningWindowAnim mRunningWindowAnim;
     private boolean mShowTasks = false;
     private boolean mShowPreviousTasks = false;
 
-    private AnimatorListenerAdapter mResetTaskView = new AnimatorListenerAdapter() {
+    private final AnimatorListenerAdapter mResetTaskView = new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
             mFakeHotseatView.setVisibility(View.INVISIBLE);
             mFakeIconView.setVisibility(View.INVISIBLE);
             if (mTutorialFragment.getActivity() != null) {
-                DisplayMetrics displayMetrics =
-                        mTutorialFragment.getResources().getDisplayMetrics();
-                int height = displayMetrics.heightPixels;
-                int width = displayMetrics.widthPixels;
+                int height = mTutorialFragment.getRootView().getFullscreenHeight();
+                int width = mTutorialFragment.getRootView().getWidth();
                 mFakeTaskViewRect.set(0, 0, width, height);
             }
             mFakeTaskViewRadius = 0;
@@ -87,6 +90,7 @@
             mFakeTaskView.setAlpha(1);
             mFakePreviousTaskView.setVisibility(View.INVISIBLE);
             mFakePreviousTaskView.setAlpha(1);
+            mFakePreviousTaskView.setToSingleRowLayout(false);
             mShowTasks = false;
             mShowPreviousTasks = false;
             mRunningWindowAnim = null;
@@ -107,9 +111,8 @@
                 .copy(mContext);
         mTaskViewSwipeUpAnimation.initDp(dp);
 
-        DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
-        int height = displayMetrics.heightPixels;
-        int width = displayMetrics.widthPixels;
+        int height = mTutorialFragment.getRootView().getFullscreenHeight();
+        int width = mTutorialFragment.getRootView().getWidth();
         mFakeTaskViewRect.set(0, 0, width, height);
         mFakeTaskViewRadius = 0;
 
@@ -137,7 +140,6 @@
     /** Fades the task view, optionally after animating to a fake Overview. */
     void fadeOutFakeTaskView(boolean toOverviewFirst, boolean reset,
                              @Nullable Runnable onEndRunnable) {
-        hideFeedback(true);
         cancelRunningAnimation();
         PendingAnimation anim = new PendingAnimation(300);
         if (toOverviewFirst) {
@@ -146,7 +148,8 @@
             anim.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation, boolean isReverse) {
-                    PendingAnimation fadeAnim = new PendingAnimation(300);
+                    PendingAnimation fadeAnim =
+                            new PendingAnimation(TASK_VIEW_END_ANIMATION_DURATION_MILLIS);
                     if (reset) {
                         fadeAnim.setFloat(mTaskViewSwipeUpAnimation
                                 .getCurrentShift(), AnimatedFloat.VALUE, 0, ACCEL);
@@ -159,6 +162,23 @@
                         fadeAnim.addListener(AnimatorListeners.forSuccessCallback(onEndRunnable));
                     }
                     AnimatorSet animset = fadeAnim.buildAnim();
+
+                    if (reset && mTutorialFragment.isLargeScreen()) {
+                        animset.addListener(new AnimatorListenerAdapter() {
+                            @Override
+                            public void onAnimationStart(Animator animation) {
+                                super.onAnimationStart(animation);
+                                Animator multiRowAnimation =
+                                        mFakePreviousTaskView.createAnimationToMultiRowLayout();
+
+                                if (multiRowAnimation != null) {
+                                    multiRowAnimation.setDuration(
+                                            TASK_VIEW_END_ANIMATION_DURATION_MILLIS).start();
+                                }
+                            }
+                        });
+                    }
+
                     animset.setStartDelay(100);
                     animset.start();
                     mRunningWindowAnim = RunningWindowAnim.wrap(animset);
@@ -183,6 +203,7 @@
     }
 
     void resetFakeTaskView() {
+        mFakeTaskView.setVisibility(View.VISIBLE);
         PendingAnimation anim = new PendingAnimation(300);
         anim.setFloat(mTaskViewSwipeUpAnimation
                 .getCurrentShift(), AnimatedFloat.VALUE, 0, ACCEL);
@@ -194,7 +215,6 @@
     }
 
     void animateFakeTaskViewHome(PointF finalVelocity, @Nullable Runnable onEndRunnable) {
-        hideFeedback(true);
         cancelRunningAnimation();
         mFakePreviousTaskView.setVisibility(View.INVISIBLE);
         mFakeHotseatView.setVisibility(View.VISIBLE);
@@ -214,12 +234,9 @@
 
     @Override
     public void setNavBarGestureProgress(@Nullable Float displacement) {
-        if (mGestureCompleted) {
+        if (isGestureCompleted()) {
             return;
         }
-        if (displacement != null) {
-            hideFeedback(true);
-        }
         if (mTutorialType == HOME_NAVIGATION_COMPLETE
                 || mTutorialType == OVERVIEW_NAVIGATION_COMPLETE) {
             mFakeTaskView.setVisibility(View.INVISIBLE);
@@ -238,7 +255,7 @@
 
     @Override
     public void onMotionPaused(boolean unused) {
-        if (mGestureCompleted) {
+        if (isGestureCompleted()) {
             return;
         }
         if (mShowTasks) {
@@ -258,19 +275,23 @@
 
         ViewSwipeUpAnimation(Context context, RecentsAnimationDeviceState deviceState,
                              GestureState gestureState) {
-            super(context, deviceState, gestureState, new FakeTransformParams());
+            super(context, deviceState, gestureState);
+            mRemoteTargetHandles[0] = new RemoteTargetGluer.RemoteTargetHandle(
+                    mRemoteTargetHandles[0].getTaskViewSimulator(), new FakeTransformParams());
         }
 
         void initDp(DeviceProfile dp) {
             initTransitionEndpoints(dp);
-            mTaskViewSimulator.setPreviewBounds(
+            mRemoteTargetHandles[0].getTaskViewSimulator().setPreviewBounds(
                     new Rect(0, 0, dp.widthPx, dp.heightPx), dp.getInsets());
         }
 
         @Override
         public void updateFinalShift() {
-            mWindowTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor);
-            mTaskViewSimulator.apply(mTransformParams);
+            mRemoteTargetHandles[0].getPlaybackController()
+                    .setProgress(mCurrentShift.value, mDragLengthFactor);
+            mRemoteTargetHandles[0].getTaskViewSimulator().apply(
+                    mRemoteTargetHandles[0].getTransformParams());
         }
 
         AnimatedFloat getCurrentShift() {
@@ -299,8 +320,8 @@
                 @Override
                 public RectF getWindowTargetRect() {
                     int fakeHomeIconSizePx = Utilities.dpToPx(60);
-                    int fakeHomeIconLeft = mFakeHotseatView.getLeft();
-                    int fakeHomeIconTop = mDp.heightPx - Utilities.dpToPx(216);
+                    int fakeHomeIconLeft = getHotseatIconLeft();
+                    int fakeHomeIconTop = getHotseatIconTop();
                     return new RectF(fakeHomeIconLeft, fakeHomeIconTop,
                             fakeHomeIconLeft + fakeHomeIconSizePx,
                             fakeHomeIconTop + fakeHomeIconSizePx);
@@ -326,12 +347,62 @@
                     mFakeIconView.setVisibility(View.INVISIBLE);
                 }
             };
-            RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift, homeAnimFactory);
+            RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift,
+                    homeAnimFactory)[0];
             windowAnim.start(mContext, velocityPxPerMs);
             return windowAnim;
         }
     }
 
+    protected Animator createFingerDotHomeSwipeAnimator(float fingerDotStartTranslationY) {
+        Animator homeSwipeAnimator = createFingerDotSwipeUpAnimator(fingerDotStartTranslationY)
+                .setDuration(HOME_SWIPE_ANIMATION_DURATION_MILLIS);
+
+        homeSwipeAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                animateFakeTaskViewHome(
+                        new PointF(
+                                0f,
+                                fingerDotStartTranslationY / HOME_SWIPE_ANIMATION_DURATION_MILLIS),
+                        null);
+            }
+        });
+
+        return homeSwipeAnimator;
+    }
+
+    protected Animator createFingerDotOverviewSwipeAnimator(float fingerDotStartTranslationY) {
+        Animator overviewSwipeAnimator = createFingerDotSwipeUpAnimator(fingerDotStartTranslationY)
+                .setDuration(OVERVIEW_SWIPE_ANIMATION_DURATION_MILLIS);
+
+        overviewSwipeAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                mFakePreviousTaskView.setVisibility(View.VISIBLE);
+                onMotionPaused(true /*arbitrary value*/);
+            }
+        });
+
+        return overviewSwipeAnimator;
+    }
+
+
+    private Animator createFingerDotSwipeUpAnimator(float fingerDotStartTranslationY) {
+        ValueAnimator swipeAnimator = ValueAnimator.ofFloat(0f, 1f);
+
+        swipeAnimator.addUpdateListener(valueAnimator -> {
+            float gestureProgress =
+                    -fingerDotStartTranslationY * valueAnimator.getAnimatedFraction();
+            setNavBarGestureProgress(gestureProgress);
+            mFingerDotView.setTranslationY(fingerDotStartTranslationY + gestureProgress);
+        });
+
+        return swipeAnimator;
+    }
+
     private class FakeTransformParams extends TransformParams {
 
         @Override
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 4b4e7e6..9c1ff4d 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -16,25 +16,33 @@
 package com.android.quickstep.interaction;
 
 import static android.view.View.GONE;
+import static android.view.View.NO_ID;
+import static android.view.View.inflate;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.annotation.ColorRes;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.graphics.drawable.Animatable2;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
 import android.graphics.drawable.RippleDrawable;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.Button;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
 import androidx.annotation.CallSuper;
 import androidx.annotation.DrawableRes;
+import androidx.annotation.LayoutRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
@@ -48,18 +56,25 @@
 import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureAttemptCallback;
 import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureAttemptCallback;
 
+import java.util.ArrayList;
+
 abstract class TutorialController implements BackGestureAttemptCallback,
         NavBarGestureAttemptCallback {
 
     private static final String TAG = "TutorialController";
 
+    private static final float FINGER_DOT_VISIBLE_ALPHA = 0.6f;
+    private static final float FINGER_DOT_SMALL_SCALE = 0.7f;
+    private static final int FINGER_DOT_ANIMATION_DURATION_MILLIS = 500;
+
     private static final String PIXEL_TIPS_APP_PACKAGE_NAME = "com.google.android.apps.tips";
     private static final CharSequence DEFAULT_PIXEL_TIPS_APP_NAME = "Pixel Tips";
 
-    private static final int FEEDBACK_ANIMATION_MS = 250;
+    private static final int FEEDBACK_ANIMATION_MS = 133;
     private static final int RIPPLE_VISIBLE_MS = 300;
     private static final int GESTURE_ANIMATION_DELAY_MS = 1500;
     private static final int ADVANCE_TUTORIAL_TIMEOUT_MS = 4000;
+    private static final long GESTURE_ANIMATION_PAUSE_DURATION_MILLIS = 1000;
 
     final TutorialFragment mTutorialFragment;
     TutorialType mTutorialType;
@@ -68,26 +83,28 @@
     final TextView mCloseButton;
     final ViewGroup mFeedbackView;
     final TextView mFeedbackTitleView;
-    final ImageView mFeedbackVideoView;
-    final ImageView mGestureVideoView;
+    final ImageView mEdgeGestureVideoView;
     final RelativeLayout mFakeLauncherView;
-    final ImageView mFakeHotseatView;
+    final FrameLayout mFakeHotseatView;
+    @Nullable View mHotseatIconView;
     final ClipIconView mFakeIconView;
-    final View mFakeTaskView;
-    final View mFakePreviousTaskView;
+    final FrameLayout mFakeTaskView;
+    final AnimatedTaskView mFakePreviousTaskView;
     final View mRippleView;
     final RippleDrawable mRippleDrawable;
     final Button mActionButton;
     final TutorialStepIndicator mTutorialStepView;
+    final ImageView mFingerDotView;
     private final AlertDialog mSkipTutorialDialog;
 
-    protected boolean mGestureCompleted = false;
+    private boolean mGestureCompleted = false;
 
     // These runnables  should be used when posting callbacks to their views and cleared from their
     // views before posting new callbacks.
     private final Runnable mTitleViewCallback;
     @Nullable private Runnable mFeedbackViewCallback;
-    @Nullable private Runnable mFeedbackVideoViewCallback;
+    @Nullable private Runnable mFakeTaskViewCallback;
+    private final Runnable mShowFeedbackRunnable;
 
     TutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) {
         mTutorialFragment = tutorialFragment;
@@ -100,8 +117,7 @@
         mFeedbackView = rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_view);
         mFeedbackTitleView = mFeedbackView.findViewById(
                 R.id.gesture_tutorial_fragment_feedback_title);
-        mFeedbackVideoView = rootView.findViewById(R.id.gesture_tutorial_feedback_video);
-        mGestureVideoView = rootView.findViewById(R.id.gesture_tutorial_gesture_video);
+        mEdgeGestureVideoView = rootView.findViewById(R.id.gesture_tutorial_edge_gesture_video);
         mFakeLauncherView = rootView.findViewById(R.id.gesture_tutorial_fake_launcher_view);
         mFakeHotseatView = rootView.findViewById(R.id.gesture_tutorial_fake_hotseat_view);
         mFakeIconView = rootView.findViewById(R.id.gesture_tutorial_fake_icon_view);
@@ -113,10 +129,34 @@
         mActionButton = 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();
 
         mTitleViewCallback = () -> mFeedbackTitleView.sendAccessibilityEvent(
                 AccessibilityEvent.TYPE_VIEW_FOCUSED);
+        mShowFeedbackRunnable = () -> {
+            mFeedbackView.setAlpha(0f);
+            mFeedbackView.setScaleX(0.95f);
+            mFeedbackView.setScaleY(0.95f);
+            mFeedbackView.setVisibility(View.VISIBLE);
+            mFeedbackView.animate()
+                    .setDuration(FEEDBACK_ANIMATION_MS)
+                    .alpha(1f)
+                    .scaleX(1f)
+                    .scaleY(1f)
+                    .withEndAction(() -> {
+                        if (mGestureCompleted && !mTutorialFragment.isAtFinalStep()) {
+                            if (mFeedbackViewCallback != null) {
+                                mFeedbackView.removeCallbacks(mFeedbackViewCallback);
+                            }
+                            mFeedbackViewCallback = mTutorialFragment::continueTutorial;
+                            mFeedbackView.postDelayed(mFeedbackViewCallback,
+                                    ADVANCE_TUTORIAL_TIMEOUT_MS);
+                        }
+                    })
+                    .start();
+            mFeedbackTitleView.postDelayed(mTitleViewCallback, FEEDBACK_ANIMATION_MS);
+        };
     }
 
     private void showSkipTutorialDialog() {
@@ -125,23 +165,35 @@
         }
     }
 
+    public int getHotseatIconTop() {
+        return mHotseatIconView == null
+                ? 0 : mFakeHotseatView.getTop() + mHotseatIconView.getTop();
+    }
+
+    public int getHotseatIconLeft() {
+        return mHotseatIconView == null
+                ? 0 : mFakeHotseatView.getLeft() + mHotseatIconView.getLeft();
+    }
+
     void setTutorialType(TutorialType tutorialType) {
         mTutorialType = tutorialType;
     }
 
-    @DrawableRes
+    @LayoutRes
     protected int getMockHotseatResId() {
-        return R.drawable.default_sandbox_mock_launcher;
+        return mTutorialFragment.isLargeScreen()
+                ? R.layout.gesture_tutorial_foldable_mock_hotseat
+                : R.layout.gesture_tutorial_mock_hotseat;
     }
 
-    @DrawableRes
-    protected int getMockAppTaskThumbnailResId(boolean forDarkMode) {
-        return R.drawable.default_sandbox_app_task_thumbnail;
+    @LayoutRes
+    protected int getMockAppTaskLayoutResId() {
+        return View.NO_ID;
     }
 
-    @DrawableRes
-    protected int getMockPreviousAppTaskThumbnailResId() {
-        return R.drawable.default_sandbox_app_previous_task_thumbnail;
+    @ColorRes
+    protected int getMockPreviousAppTaskThumbnailColorResId() {
+        return R.color.gesture_tutorial_fake_previous_task_view_color;
     }
 
     @DrawableRes
@@ -173,17 +225,10 @@
             mFeedbackView.setTranslationY(0);
             return;
         }
-        AnimatedVectorDrawable tutorialAnimation = mTutorialFragment.getTutorialAnimation();
-        AnimatedVectorDrawable gestureAnimation = mTutorialFragment.getGestureAnimation();
-
-        if (tutorialAnimation != null && gestureAnimation != null) {
-            TextView title = mFeedbackView.findViewById(
-                    R.id.gesture_tutorial_fragment_feedback_title);
-
-            playFeedbackVideo(tutorialAnimation, gestureAnimation, () -> {
-                mFeedbackView.setTranslationY(0);
-                title.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
-            }, true);
+        Animator gestureAnimation = mTutorialFragment.getGestureAnimation();
+        AnimatedVectorDrawable edgeAnimation = mTutorialFragment.getEdgeAnimation();
+        if (gestureAnimation != null && edgeAnimation != null) {
+            playFeedbackAnimation(gestureAnimation, edgeAnimation, mShowFeedbackRunnable, true);
         }
     }
 
@@ -215,8 +260,13 @@
             int subtitleResId,
             boolean isGestureSuccessful,
             boolean useGestureAnimationDelay) {
-        mFeedbackTitleView.setText(titleResId);
         mFeedbackTitleView.removeCallbacks(mTitleViewCallback);
+        if (mFeedbackViewCallback != null) {
+            mFeedbackView.removeCallbacks(mFeedbackViewCallback);
+            mFeedbackViewCallback = null;
+        }
+
+        mFeedbackTitleView.setText(titleResId);
         TextView subtitle =
                 mFeedbackView.findViewById(R.id.gesture_tutorial_fragment_feedback_subtitle);
         subtitle.setText(subtitleResId);
@@ -226,77 +276,72 @@
                 showActionButton();
             }
 
-            if (mFeedbackVideoViewCallback != null) {
-                mFeedbackVideoView.removeCallbacks(mFeedbackVideoViewCallback);
-                mFeedbackVideoViewCallback = null;
+            if (mFakeTaskViewCallback != null) {
+                mFakeTaskView.removeCallbacks(mFakeTaskViewCallback);
+                mFakeTaskViewCallback = null;
             }
         }
         mGestureCompleted = isGestureSuccessful;
 
-        AnimatedVectorDrawable tutorialAnimation = mTutorialFragment.getTutorialAnimation();
-        AnimatedVectorDrawable gestureAnimation = mTutorialFragment.getGestureAnimation();
-        if (tutorialAnimation != null && gestureAnimation != null) {
-            if (!isGestureSuccessful) {
-                playFeedbackVideo(tutorialAnimation, gestureAnimation, () -> {
-                    mFeedbackView.setTranslationY(
-                            -mFeedbackView.getHeight() - mFeedbackView.getTop());
-                    mFeedbackView.setVisibility(View.VISIBLE);
-                    mFeedbackView.animate()
-                            .setDuration(FEEDBACK_ANIMATION_MS)
-                            .translationY(0)
-                            .start();
-                    mFeedbackTitleView.postDelayed(mTitleViewCallback, FEEDBACK_ANIMATION_MS);
-                }, useGestureAnimationDelay);
-                return;
-            } else {
-                mTutorialFragment.releaseFeedbackVideoView();
-            }
+        Animator gestureAnimation = mTutorialFragment.getGestureAnimation();
+        AnimatedVectorDrawable edgeAnimation = mTutorialFragment.getEdgeAnimation();
+        if (!isGestureSuccessful && gestureAnimation != null && edgeAnimation != null) {
+            playFeedbackAnimation(
+                    gestureAnimation,
+                    edgeAnimation,
+                    mShowFeedbackRunnable,
+                    useGestureAnimationDelay);
+            return;
+        } else {
+            mTutorialFragment.releaseFeedbackAnimation();
         }
-        mFeedbackView.setTranslationY(-mFeedbackView.getHeight() - mFeedbackView.getTop());
-        mFeedbackView.setVisibility(View.VISIBLE);
-        mFeedbackView.animate()
-                .setDuration(FEEDBACK_ANIMATION_MS)
-                .translationY(0)
-                .withEndAction(() -> {
-                    if (isGestureSuccessful && !mTutorialFragment.isAtFinalStep()) {
-                        if (mFeedbackViewCallback != null) {
-                            mFeedbackView.removeCallbacks(mFeedbackViewCallback);
-                        }
-                        mFeedbackViewCallback = mTutorialFragment::continueTutorial;
-                        mFeedbackView.postDelayed(mFeedbackViewCallback,
-                                ADVANCE_TUTORIAL_TIMEOUT_MS);
-                    }
-                })
-                .start();
-        mFeedbackTitleView.postDelayed(mTitleViewCallback, FEEDBACK_ANIMATION_MS);
+        mFeedbackViewCallback = mShowFeedbackRunnable;
+
+        mFeedbackView.post(mFeedbackViewCallback);
     }
 
-    void hideFeedback(boolean releaseFeedbackVideo) {
+    public boolean isGestureCompleted() {
+        return mGestureCompleted;
+    }
+
+    void hideFeedback() {
+        cancelQueuedGestureAnimation();
         mFeedbackView.clearAnimation();
         mFeedbackView.setVisibility(View.INVISIBLE);
-        if (releaseFeedbackVideo) {
-            mTutorialFragment.releaseFeedbackVideoView();
-        }
     }
 
-    private void playFeedbackVideo(
-            @NonNull AnimatedVectorDrawable tutorialAnimation,
-            @NonNull AnimatedVectorDrawable gestureAnimation,
+    void cancelQueuedGestureAnimation() {
+        if (mFeedbackViewCallback != null) {
+            mFeedbackView.removeCallbacks(mFeedbackViewCallback);
+            mFeedbackViewCallback = null;
+        }
+        if (mFakeTaskViewCallback != null) {
+            mFakeTaskView.removeCallbacks(mFakeTaskViewCallback);
+            mFakeTaskViewCallback = null;
+        }
+        mFeedbackTitleView.removeCallbacks(mTitleViewCallback);
+    }
+
+    private void playFeedbackAnimation(
+            @NonNull Animator gestureAnimation,
+            @NonNull AnimatedVectorDrawable edgeAnimation,
             @NonNull Runnable onStartRunnable,
             boolean useGestureAnimationDelay) {
 
-        if (tutorialAnimation.isRunning()) {
-            tutorialAnimation.reset();
+        if (gestureAnimation.isRunning()) {
+            gestureAnimation.cancel();
         }
-        tutorialAnimation.registerAnimationCallback(new Animatable2.AnimationCallback() {
-
+        if (edgeAnimation.isRunning()) {
+            edgeAnimation.reset();
+        }
+        gestureAnimation.addListener(new AnimatorListenerAdapter() {
             @Override
-            public void onAnimationStart(Drawable drawable) {
-                super.onAnimationStart(drawable);
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
 
-                mGestureVideoView.setVisibility(GONE);
-                if (gestureAnimation.isRunning()) {
-                    gestureAnimation.stop();
+                mEdgeGestureVideoView.setVisibility(GONE);
+                if (edgeAnimation.isRunning()) {
+                    edgeAnimation.stop();
                 }
 
                 if (!useGestureAnimationDelay) {
@@ -305,37 +350,25 @@
             }
 
             @Override
-            public void onAnimationEnd(Drawable drawable) {
-                super.onAnimationEnd(drawable);
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
 
-                mGestureVideoView.setVisibility(View.VISIBLE);
-                gestureAnimation.start();
+                mEdgeGestureVideoView.setVisibility(View.VISIBLE);
+                edgeAnimation.start();
 
-                tutorialAnimation.unregisterAnimationCallback(this);
+                gestureAnimation.removeListener(this);
             }
         });
 
-        if (mFeedbackViewCallback != null) {
-            mFeedbackVideoView.removeCallbacks(mFeedbackViewCallback);
-            mFeedbackViewCallback = null;
-        }
-        if (mFeedbackVideoViewCallback != null) {
-            mFeedbackVideoView.removeCallbacks(mFeedbackVideoViewCallback);
-            mFeedbackVideoViewCallback = null;
-        }
+        cancelQueuedGestureAnimation();
         if (useGestureAnimationDelay) {
             mFeedbackViewCallback = onStartRunnable;
-            mFeedbackVideoViewCallback = () -> {
-                mFeedbackVideoView.setVisibility(View.VISIBLE);
-                tutorialAnimation.start();
-            };
+            mFakeTaskViewCallback = gestureAnimation::start;
 
-            mFeedbackVideoView.setVisibility(View.GONE);
             mFeedbackView.post(mFeedbackViewCallback);
-            mFeedbackVideoView.postDelayed(mFeedbackVideoViewCallback, GESTURE_ANIMATION_DELAY_MS);
+            mFakeTaskView.postDelayed(mFakeTaskViewCallback, GESTURE_ANIMATION_DELAY_MS);
         } else {
-            mFeedbackVideoView.setVisibility(View.VISIBLE);
-            tutorialAnimation.start();
+            gestureAnimation.start();
         }
     }
 
@@ -360,10 +393,11 @@
 
     @CallSuper
     void transitToController() {
-        hideFeedback(false);
+        hideFeedback();
         hideActionButton();
         updateSubtext();
         updateDrawables();
+        updateLayout();
 
         mGestureCompleted = false;
         if (mFakeHotseatView != null) {
@@ -395,6 +429,21 @@
         mActionButton.setOnClickListener(this::onActionButtonClicked);
     }
 
+    void updateFakeAppTaskViewLayout(@LayoutRes int mockAppTaskLayoutResId) {
+        updateFakeViewLayout(mFakeTaskView, mockAppTaskLayoutResId);
+    }
+
+    void updateFakeViewLayout(ViewGroup view, @LayoutRes int mockLayoutResId) {
+        view.removeAllViews();
+        if (mockLayoutResId != NO_ID) {
+            view.addView(
+                    inflate(mContext, mockLayoutResId, null),
+                    new FrameLayout.LayoutParams(
+                            ViewGroup.LayoutParams.MATCH_PARENT,
+                            ViewGroup.LayoutParams.MATCH_PARENT));
+        }
+    }
+
     private void updateSubtext() {
         mTutorialStepView.setTutorialProgress(
                 mTutorialFragment.getCurrentStep(), mTutorialFragment.getNumSteps());
@@ -404,24 +453,36 @@
         if (mContext != null) {
             mTutorialFragment.getRootView().setBackground(AppCompatResources.getDrawable(
                     mContext, getMockWallpaperResId()));
-            mTutorialFragment.updateFeedbackVideo();
+            mTutorialFragment.updateFeedbackAnimation();
             mFakeLauncherView.setBackgroundColor(
-                    mContext.getColor(Utilities.isDarkTheme(mContext)
-                            ? R.color.fake_wallpaper_color_dark_mode
-                            : R.color.fake_wallpaper_color_light_mode));
-            mFakeHotseatView.setImageDrawable(AppCompatResources.getDrawable(
-                    mContext, getMockHotseatResId()));
-            mFakeTaskView.setBackground(AppCompatResources.getDrawable(
-                    mContext, getMockAppTaskThumbnailResId(Utilities.isDarkTheme(mContext))));
+                    mContext.getColor(R.color.gesture_tutorial_fake_wallpaper_color));
+            updateFakeViewLayout(mFakeHotseatView, getMockHotseatResId());
+            mHotseatIconView = mFakeHotseatView.findViewById(R.id.hotseat_icon_1);
+            updateFakeViewLayout(mFakeTaskView, getMockAppTaskLayoutResId());
             mFakeTaskView.animate().alpha(1).setListener(
                     AnimatorListeners.forSuccessCallback(() -> mFakeTaskView.animate().cancel()));
-            mFakePreviousTaskView.setBackground(AppCompatResources.getDrawable(
-                    mContext, getMockPreviousAppTaskThumbnailResId()));
+            mFakePreviousTaskView.setFakeTaskViewFillColor(mContext.getResources().getColor(
+                    getMockPreviousAppTaskThumbnailColorResId()));
             mFakeIconView.setBackground(AppCompatResources.getDrawable(
                     mContext, getMockAppIconResId()));
         }
     }
 
+    private void updateLayout() {
+        if (mContext != null) {
+            RelativeLayout.LayoutParams feedbackLayoutParams =
+                    (RelativeLayout.LayoutParams) mFeedbackView.getLayoutParams();
+            feedbackLayoutParams.setMarginStart(mContext.getResources().getDimensionPixelSize(
+                    mTutorialFragment.isLargeScreen()
+                            ? R.dimen.gesture_tutorial_foldable_feedback_margin_start_end
+                            : R.dimen.gesture_tutorial_feedback_margin_start_end));
+            feedbackLayoutParams.setMarginEnd(mContext.getResources().getDimensionPixelSize(
+                    mTutorialFragment.isLargeScreen()
+                            ? R.dimen.gesture_tutorial_foldable_feedback_margin_start_end
+                            : R.dimen.gesture_tutorial_feedback_margin_start_end));
+        }
+    }
+
     private AlertDialog createSkipTutorialDialog() {
         if (mContext instanceof GestureSandboxActivity) {
             GestureSandboxActivity sandboxActivity = (GestureSandboxActivity) mContext;
@@ -485,6 +546,52 @@
         return null;
     }
 
+    protected AnimatorSet createFingerDotAppearanceAnimatorSet() {
+        ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(
+                mFingerDotView, View.ALPHA, 0f, FINGER_DOT_VISIBLE_ALPHA);
+        ObjectAnimator yScaleAnimator = ObjectAnimator.ofFloat(
+                mFingerDotView, View.SCALE_Y, FINGER_DOT_SMALL_SCALE, 1f);
+        ObjectAnimator xScaleAnimator = ObjectAnimator.ofFloat(
+                mFingerDotView, View.SCALE_X, FINGER_DOT_SMALL_SCALE, 1f);
+        ArrayList<Animator> animators = new ArrayList<>();
+
+        animators.add(alphaAnimator);
+        animators.add(xScaleAnimator);
+        animators.add(yScaleAnimator);
+
+        AnimatorSet appearanceAnimatorSet = new AnimatorSet();
+
+        appearanceAnimatorSet.playTogether(animators);
+        appearanceAnimatorSet.setDuration(FINGER_DOT_ANIMATION_DURATION_MILLIS);
+
+        return appearanceAnimatorSet;
+    }
+
+    protected AnimatorSet createFingerDotDisappearanceAnimatorSet() {
+        ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(
+                mFingerDotView, View.ALPHA, FINGER_DOT_VISIBLE_ALPHA, 0f);
+        ObjectAnimator yScaleAnimator = ObjectAnimator.ofFloat(
+                mFingerDotView, View.SCALE_Y, 1f, FINGER_DOT_SMALL_SCALE);
+        ObjectAnimator xScaleAnimator = ObjectAnimator.ofFloat(
+                mFingerDotView, View.SCALE_X, 1f, FINGER_DOT_SMALL_SCALE);
+        ArrayList<Animator> animators = new ArrayList<>();
+
+        animators.add(alphaAnimator);
+        animators.add(xScaleAnimator);
+        animators.add(yScaleAnimator);
+
+        AnimatorSet appearanceAnimatorSet = new AnimatorSet();
+
+        appearanceAnimatorSet.playTogether(animators);
+        appearanceAnimatorSet.setDuration(FINGER_DOT_ANIMATION_DURATION_MILLIS);
+
+        return appearanceAnimatorSet;
+    }
+
+    protected Animator createAnimationPause() {
+        return ValueAnimator.ofFloat(0f, 1f).setDuration(GESTURE_ANIMATION_PAUSE_DURATION_MILLIS);
+    }
+
     /** Denotes the type of the tutorial. */
     enum TutorialType {
         BACK_NAVIGATION,
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index 7637450..89be1a6 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -15,9 +15,12 @@
  */
 package com.android.quickstep.interaction;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.graphics.Insets;
 import android.graphics.drawable.Animatable2;
 import android.graphics.drawable.AnimatedVectorDrawable;
@@ -29,6 +32,7 @@
 import android.view.View;
 import android.view.View.OnTouchListener;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
 import android.widget.ImageView;
 
@@ -37,8 +41,8 @@
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentActivity;
 
+import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
 
 abstract class TutorialFragment extends Fragment implements OnTouchListener {
@@ -49,17 +53,20 @@
     TutorialType mTutorialType;
     @Nullable TutorialController mTutorialController = null;
     RootSandboxLayout mRootView;
+    View mFingerDotView;
+    View mFakePreviousTaskView;
     EdgeBackGestureHandler mEdgeBackGestureHandler;
     NavBarGestureHandler mNavBarGestureHandler;
-    private ImageView mFeedbackVideoView;
-    private ImageView mGestureVideoView;
+    private ImageView mEdgeGestureVideoView;
 
-    @Nullable private AnimatedVectorDrawable mTutorialAnimation = null;
-    @Nullable private AnimatedVectorDrawable mGestureAnimation = null;
+    @Nullable private Animator mGestureAnimation = null;
+    @Nullable private AnimatedVectorDrawable mEdgeAnimation = null;
     private boolean mIntroductionShown = false;
 
     private boolean mFragmentStopped = false;
 
+    private boolean mIsLargeScreen;
+
     public static TutorialFragment newInstance(TutorialType tutorialType) {
         TutorialFragment fragment = getFragmentForTutorialType(tutorialType);
         if (fragment == null) {
@@ -96,24 +103,26 @@
         return null;
     }
 
-    @Nullable Integer getFeedbackVideoResId(boolean forDarkMode) {
-        return null;
-    }
-
-    @Nullable Integer getGestureVideoResId() {
+    @Nullable Integer getEdgeAnimationResId() {
         return null;
     }
 
     @Nullable
-    AnimatedVectorDrawable getTutorialAnimation() {
-        return mTutorialAnimation;
-    }
-
-    @Nullable
-    AnimatedVectorDrawable getGestureAnimation() {
+    Animator getGestureAnimation() {
         return mGestureAnimation;
     }
 
+    @Nullable
+    AnimatedVectorDrawable getEdgeAnimation() {
+        return mEdgeAnimation;
+    }
+
+
+    @Nullable
+    protected Animator createGestureAnimation() {
+        return null;
+    }
+
     abstract TutorialController createController(TutorialType type);
 
     abstract Class<? extends TutorialController> getControllerClass();
@@ -125,6 +134,21 @@
         mTutorialType = (TutorialType) args.getSerializable(KEY_TUTORIAL_TYPE);
         mEdgeBackGestureHandler = new EdgeBackGestureHandler(getContext());
         mNavBarGestureHandler = new NavBarGestureHandler(getContext());
+
+        mIsLargeScreen = InvariantDeviceProfile.INSTANCE.get(getContext())
+                .getDeviceProfile(getContext()).isTablet;
+
+        if (mIsLargeScreen) {
+            ((Activity) getContext()).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
+        } else {
+            // Temporary until UI mocks for landscape mode for phones are created.
+            ((Activity) getContext()).setRequestedOrientation(
+                    ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+        }
+    }
+
+    public boolean isLargeScreen() {
+        return mIsLargeScreen;
     }
 
     @Override
@@ -147,21 +171,22 @@
             return insets;
         });
         mRootView.setOnTouchListener(this);
-        mFeedbackVideoView = mRootView.findViewById(R.id.gesture_tutorial_feedback_video);
-        mGestureVideoView = mRootView.findViewById(R.id.gesture_tutorial_gesture_video);
+        mEdgeGestureVideoView = mRootView.findViewById(R.id.gesture_tutorial_edge_gesture_video);
+        mFingerDotView = mRootView.findViewById(R.id.gesture_tutorial_finger_dot);
+        mFakePreviousTaskView = mRootView.findViewById(
+                R.id.gesture_tutorial_fake_previous_task_view);
         return mRootView;
     }
 
     @Override
     public void onStop() {
         super.onStop();
-        releaseFeedbackVideoView();
-        releaseGestureVideoView();
+        releaseFeedbackAnimation();
         mFragmentStopped = true;
     }
 
     void initializeFeedbackVideoView() {
-        if (!updateFeedbackVideo()) {
+        if (!updateFeedbackAnimation()) {
             return;
         }
 
@@ -176,87 +201,90 @@
         }
     }
 
-    boolean updateFeedbackVideo() {
-        if (getContext() == null) {
+    boolean updateFeedbackAnimation() {
+        if (!updateEdgeAnimation()) {
             return false;
         }
-        Integer feedbackVideoResId = getFeedbackVideoResId(Utilities.isDarkTheme(getContext()));
-
-        if (feedbackVideoResId == null || !updateGestureVideo()) {
-            return false;
-        }
-        mTutorialAnimation = (AnimatedVectorDrawable) getContext().getDrawable(feedbackVideoResId);
-
-        if (mTutorialAnimation != null) {
-            mTutorialAnimation.registerAnimationCallback(new Animatable2.AnimationCallback() {
-
-                @Override
-                public void onAnimationStart(Drawable drawable) {
-                    super.onAnimationStart(drawable);
-
-                    mFeedbackVideoView.setVisibility(View.VISIBLE);
-                }
-
-                @Override
-                public void onAnimationEnd(Drawable drawable) {
-                    super.onAnimationEnd(drawable);
-
-                    releaseFeedbackVideoView();
-                }
-            });
-        }
-        mFeedbackVideoView.setImageDrawable(mTutorialAnimation);
-
-        return true;
-    }
-
-    boolean updateGestureVideo() {
-        Integer gestureVideoResId = getGestureVideoResId();
-        if (gestureVideoResId == null || getContext() == null) {
-            return false;
-        }
-        mGestureAnimation = (AnimatedVectorDrawable) getContext().getDrawable(gestureVideoResId);
+        mGestureAnimation = createGestureAnimation();
 
         if (mGestureAnimation != null) {
-            mGestureAnimation.registerAnimationCallback(new Animatable2.AnimationCallback() {
+            mGestureAnimation.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    super.onAnimationStart(animation);
+                    mFingerDotView.setVisibility(View.VISIBLE);
+                }
+
+                @Override
+                public void onAnimationCancel(Animator animation) {
+                    super.onAnimationCancel(animation);
+                    mFingerDotView.setVisibility(View.GONE);
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    super.onAnimationEnd(animation);
+                    mFingerDotView.setVisibility(View.GONE);
+                }
+            });
+        }
+
+        return mGestureAnimation != null;
+    }
+
+    boolean updateEdgeAnimation() {
+        Integer edgeAnimationResId = getEdgeAnimationResId();
+        if (edgeAnimationResId == null || getContext() == null) {
+            return false;
+        }
+        mEdgeAnimation = (AnimatedVectorDrawable) getContext().getDrawable(edgeAnimationResId);
+
+        if (mEdgeAnimation != null) {
+            mEdgeAnimation.registerAnimationCallback(new Animatable2.AnimationCallback() {
 
                 @Override
                 public void onAnimationEnd(Drawable drawable) {
                     super.onAnimationEnd(drawable);
 
-                    mGestureAnimation.start();
+                    mEdgeAnimation.start();
                 }
             });
         }
-        mGestureVideoView.setImageDrawable(mGestureAnimation);
+        mEdgeGestureVideoView.setImageDrawable(mEdgeAnimation);
 
-        return true;
+        return mEdgeAnimation != null;
     }
 
-    void releaseFeedbackVideoView() {
-        if (mTutorialAnimation != null && mTutorialAnimation.isRunning()) {
-            mTutorialAnimation.stop();
+    void releaseFeedbackAnimation() {
+        if (mTutorialController != null && !mTutorialController.isGestureCompleted()) {
+            mTutorialController.cancelQueuedGestureAnimation();
         }
-
-        mFeedbackVideoView.setVisibility(View.GONE);
-    }
-
-    void releaseGestureVideoView() {
         if (mGestureAnimation != null && mGestureAnimation.isRunning()) {
-            mGestureAnimation.stop();
+            mGestureAnimation.cancel();
+        }
+        if (mEdgeAnimation != null && mEdgeAnimation.isRunning()) {
+            mEdgeAnimation.stop();
         }
 
-        mGestureVideoView.setVisibility(View.GONE);
+        mEdgeGestureVideoView.setVisibility(View.GONE);
     }
 
     @Override
     public void onResume() {
         super.onResume();
+        releaseFeedbackAnimation();
         if (mFragmentStopped && mTutorialController != null) {
             mTutorialController.showFeedback();
             mFragmentStopped = false;
         } else {
-            changeController(mTutorialType);
+            mRootView.getViewTreeObserver().addOnGlobalLayoutListener(
+                    new ViewTreeObserver.OnGlobalLayoutListener() {
+                        @Override
+                        public void onGlobalLayout() {
+                            changeController(mTutorialType);
+                            mRootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+                        }
+                    });
         }
     }
 
@@ -292,6 +320,7 @@
         mEdgeBackGestureHandler.registerBackGestureAttemptCallback(mTutorialController);
         mNavBarGestureHandler.registerNavBarGestureAttemptCallback(mTutorialController);
         mTutorialType = tutorialType;
+
         initializeFeedbackVideoView();
     }
 
diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
index 7eca360..7ae0fc8 100644
--- a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
+++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
@@ -16,19 +16,15 @@
 
 package com.android.quickstep.logging;
 
-import static com.android.launcher3.InvariantDeviceProfile.KEY_MIGRATION_SRC_HOTSEAT_COUNT;
 import static com.android.launcher3.Utilities.getDevicePrefs;
 import static com.android.launcher3.Utilities.getPrefs;
-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_HOME_SCREEN_SUGGESTIONS_DISABLED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_SCREEN_SUGGESTIONS_ENABLED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DOT_DISABLED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DOT_ENABLED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_THEMED_ICON_DISABLED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_THEMED_ICON_ENABLED;
+import static com.android.launcher3.model.DeviceGridState.KEY_WORKSPACE_SIZE;
 import static com.android.launcher3.model.QuickstepModelDelegate.LAST_PREDICTION_ENABLED_STATE;
 import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
 import static com.android.launcher3.util.Themes.KEY_THEMED_ICONS;
@@ -47,6 +43,7 @@
 import com.android.launcher3.logging.InstanceIdSequence;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.logging.StatsLogManager.StatsLogger;
+import com.android.launcher3.model.DeviceGridState;
 import com.android.launcher3.util.SettingsCache;
 import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.SysUINavigationMode.Mode;
@@ -133,7 +130,9 @@
 
     @Override
     public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
-        if (LAST_PREDICTION_ENABLED_STATE.equals(key) || KEY_MIGRATION_SRC_HOTSEAT_COUNT.equals(key)
+        if (LAST_PREDICTION_ENABLED_STATE.equals(key)
+                || KEY_WORKSPACE_SIZE.equals(key)
+                || KEY_THEMED_ICONS.equals(key)
                 || mLoggablePrefs.containsKey(key)) {
             dispatchUserEvent();
         }
@@ -151,30 +150,13 @@
                 ? LAUNCHER_HOME_SCREEN_SUGGESTIONS_ENABLED
                 : LAUNCHER_HOME_SCREEN_SUGGESTIONS_DISABLED);
 
-        SharedPreferences prefs = getPrefs(mContext);
-        StatsLogManager.LauncherEvent gridSizeChangedEvent = null;
-        // TODO(b/184981523): This doesn't work for 2-panel grid, which has 6 hotseat icons
-        switch (prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1)) {
-            case 5:
-                gridSizeChangedEvent = LAUNCHER_GRID_SIZE_5;
-                break;
-            case 4:
-                gridSizeChangedEvent = LAUNCHER_GRID_SIZE_4;
-                break;
-            case 3:
-                gridSizeChangedEvent = LAUNCHER_GRID_SIZE_3;
-                break;
-            case 2:
-                gridSizeChangedEvent = LAUNCHER_GRID_SIZE_2;
-                break;
-            default:
-                // Ignore illegal input.
-                break;
-        }
+        StatsLogManager.LauncherEvent gridSizeChangedEvent =
+                new DeviceGridState(mContext).getWorkspaceSizeEvent();
         if (gridSizeChangedEvent != null) {
             logger.log(gridSizeChangedEvent);
         }
 
+        SharedPreferences prefs = getPrefs(mContext);
         if (FeatureFlags.ENABLE_THEMED_ICONS.get()) {
             logger.log(prefs.getBoolean(KEY_THEMED_ICONS, false)
                     ? LAUNCHER_THEMED_ICON_ENABLED
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 6575996..676161e 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -31,9 +31,11 @@
 
 import android.content.Context;
 import android.util.Log;
+import android.util.StatsEvent;
 import android.view.View;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.WorkerThread;
 import androidx.slice.SliceItem;
 
@@ -56,6 +58,7 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.LogConfig;
+import com.android.launcher3.views.ActivityContext;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 import com.android.systemui.shared.system.SysUiStatsLog;
 
@@ -97,7 +100,7 @@
 
     @Override
     protected StatsLogger createLogger() {
-        return new StatsCompatLogger(mContext);
+        return new StatsCompatLogger(mContext, mActivityContext);
     }
 
     /**
@@ -135,13 +138,44 @@
     }
 
     /**
+     * Builds {@link StatsEvent} from {@link LauncherAtom.ItemInfo}. Used for pulled atom callback
+     * implementation.
+     */
+    public static StatsEvent buildStatsEvent(LauncherAtom.ItemInfo info,
+            @Nullable InstanceId instanceId) {
+        return SysUiStatsLog.buildStatsEvent(
+                SysUiStatsLog.LAUNCHER_LAYOUT_SNAPSHOT, // atom ID,
+                LAUNCHER_WORKSPACE_SNAPSHOT.getId(), // event_id = 1;
+                info.getAttribute().getNumber() * ATTRIBUTE_MULTIPLIER
+                        + info.getItemCase().getNumber(), // item_id = 2;
+                instanceId == null ? 0 : instanceId.getId(), //instance_id = 3;
+                0, //uid = 4 [(is_uid) = true];
+                getPackageName(info), // package_name = 5;
+                getComponentName(info), // component_name = 6;
+                getGridX(info, false), //grid_x = 7 [default = -1];
+                getGridY(info, false), //grid_y = 8 [default = -1];
+                getPageId(info), // page_id = 9 [default = -2];
+                getGridX(info, true), //grid_x_parent = 10 [default = -1];
+                getGridY(info, true), //grid_y_parent = 11 [default = -1];
+                getParentPageId(info), //page_id_parent = 12 [default = -2];
+                getHierarchy(info), // container_id = 13;
+                info.getIsWork(), // is_work_profile = 14;
+                info.getAttribute().getNumber(), // attribute_id = 15;
+                getCardinality(info), // cardinality = 16;
+                info.getWidget().getSpanX(), // span_x = 17 [default = 1];
+                info.getWidget().getSpanY() // span_y = 18 [default = 1];
+        );
+    }
+
+    /**
      * Helps to construct and write statsd compatible log message.
      */
     private static class StatsCompatLogger implements StatsLogger {
 
         private static final ItemInfo DEFAULT_ITEM_INFO = new ItemInfo();
 
-        private Context mContext;
+        private final Context mContext;
+        private final Optional<ActivityContext> mActivityContext;
         private ItemInfo mItemInfo = DEFAULT_ITEM_INFO;
         private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
         private OptionalInt mRank = OptionalInt.empty();
@@ -154,8 +188,9 @@
         private SliceItem mSliceItem;
         private LauncherAtom.Slice mSlice;
 
-        StatsCompatLogger(Context context) {
+        StatsCompatLogger(Context context, ActivityContext activityContext) {
             mContext = context;
+            mActivityContext = Optional.ofNullable(activityContext);
         }
 
         @Override
@@ -307,6 +342,9 @@
             mRank.ifPresent(itemInfoBuilder::setRank);
             mContainerInfo.ifPresent(itemInfoBuilder::setContainerInfo);
 
+            mActivityContext.ifPresent(activityContext ->
+                    activityContext.applyOverwritesToLogItem(itemInfoBuilder));
+
             if (mFromState.isPresent() || mToState.isPresent() || mEditText.isPresent()) {
                 FolderIcon.Builder folderIconBuilder = itemInfoBuilder
                         .getFolderIcon()
@@ -375,6 +413,8 @@
         switch (info.getContainerInfo().getContainerCase()) {
             case PREDICTED_HOTSEAT_CONTAINER:
                 return info.getContainerInfo().getPredictedHotseatContainer().getCardinality();
+            case TASK_BAR_CONTAINER:
+                return info.getContainerInfo().getTaskBarContainer().getCardinality();
             case SEARCH_RESULT_CONTAINER:
                 return info.getContainerInfo().getSearchResultContainer().getQueryLength();
             case EXTENDED_CONTAINERS:
@@ -461,6 +501,8 @@
                 return info.getContainerInfo().getHotseat().getIndex();
             case PREDICTED_HOTSEAT_CONTAINER:
                 return info.getContainerInfo().getPredictedHotseatContainer().getIndex();
+            case TASK_BAR_CONTAINER:
+                return info.getContainerInfo().getTaskBarContainer().getIndex();
             default:
                 return info.getContainerInfo().getWorkspace().getPageIndex();
         }
diff --git a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
index 7f94839..7c83833 100644
--- a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
+++ b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
@@ -48,14 +48,16 @@
 public class AnimatorControllerWithResistance {
 
     private enum RecentsResistanceParams {
-        FROM_APP(0.75f, 0.5f, 1f),
-        FROM_OVERVIEW(1f, 0.75f, 0.5f);
+        FROM_APP(0.75f, 0.5f, 1f, false),
+        FROM_APP_TABLET(1f, 0.7f, 1f, true),
+        FROM_OVERVIEW(1f, 0.75f, 0.5f, false);
 
         RecentsResistanceParams(float scaleStartResist, float scaleMaxResist,
-                float translationFactor) {
+                float translationFactor, boolean stopScalingAtTop) {
             this.scaleStartResist = scaleStartResist;
             this.scaleMaxResist = scaleMaxResist;
             this.translationFactor = translationFactor;
+            this.stopScalingAtTop = stopScalingAtTop;
         }
 
         /**
@@ -73,6 +75,12 @@
          * where 0 will keep it centered and 1 will have it barely touch the top of the screen.
          */
         public final float translationFactor;
+
+        /**
+         * Whether to end scaling effect when the scaled down version of TaskView's top reaches the
+         * non-scaled version of TaskView's top.
+         */
+        public final boolean stopScalingAtTop;
     }
 
     private static final TimeInterpolator RECENTS_SCALE_RESIST_INTERPOLATOR = DEACCEL;
@@ -150,8 +158,7 @@
         Rect startRect = new Rect();
         PagedOrientationHandler orientationHandler = params.recentsOrientedState
                 .getOrientationHandler();
-        LauncherActivityInterface.INSTANCE.calculateTaskSize(params.context, params.dp, startRect,
-                orientationHandler);
+        LauncherActivityInterface.INSTANCE.calculateTaskSize(params.context, params.dp, startRect);
         long distanceToCover = startRect.bottom;
         PendingAnimation resistAnim = params.resistAnim != null
                 ? params.resistAnim
@@ -160,26 +167,6 @@
         PointF pivot = new PointF();
         float fullscreenScale = params.recentsOrientedState.getFullScreenScaleAndPivot(
                 startRect, params.dp, pivot);
-        float prevScaleRate = (fullscreenScale - params.startScale)
-                / (params.dp.heightPx - startRect.bottom);
-        // This is what the scale would be at the end of the drag if we didn't apply resistance.
-        float endScale = params.startScale - prevScaleRate * distanceToCover;
-        // Create an interpolator that resists the scale so the scale doesn't get smaller than
-        // RECENTS_SCALE_MAX_RESIST.
-        float startResist = Utilities.getProgress(params.resistanceParams.scaleStartResist,
-                params.startScale, endScale);
-        float maxResist = Utilities.getProgress(params.resistanceParams.scaleMaxResist,
-                params.startScale, endScale);
-        final TimeInterpolator scaleInterpolator = t -> {
-            if (t < startResist) {
-                return t;
-            }
-            float resistProgress = Utilities.getProgress(t, startResist, 1);
-            resistProgress = RECENTS_SCALE_RESIST_INTERPOLATOR.getInterpolation(resistProgress);
-            return startResist + resistProgress * (maxResist - startResist);
-        };
-        resistAnim.addFloat(params.scaleTarget, params.scaleProperty, params.startScale, endScale,
-                scaleInterpolator);
 
         // Compute where the task view would be based on the end scale.
         RectF endRectF = new RectF(startRect);
@@ -194,6 +181,32 @@
         resistAnim.addFloat(params.translationTarget, params.translationProperty,
                 params.startTranslation, endTranslation, RECENTS_TRANSLATE_RESIST_INTERPOLATOR);
 
+        float prevScaleRate = (fullscreenScale - params.startScale)
+                / (params.dp.heightPx - startRect.bottom);
+        // This is what the scale would be at the end of the drag if we didn't apply resistance.
+        float endScale = params.startScale - prevScaleRate * distanceToCover;
+        // Create an interpolator that resists the scale so the scale doesn't get smaller than
+        // RECENTS_SCALE_MAX_RESIST.
+        float startResist = Utilities.getProgress(params.resistanceParams.scaleStartResist,
+                params.startScale, endScale);
+        float maxResist = Utilities.getProgress(params.resistanceParams.scaleMaxResist,
+                params.startScale, endScale);
+        float stopResist =
+                params.resistanceParams.stopScalingAtTop ? 1f - startRect.top / endRectF.top : 1f;
+        final TimeInterpolator scaleInterpolator = t -> {
+            if (t < startResist) {
+                return t;
+            }
+            if (t > stopResist) {
+                return maxResist;
+            }
+            float resistProgress = Utilities.getProgress(t, startResist, stopResist);
+            resistProgress = RECENTS_SCALE_RESIST_INTERPOLATOR.getInterpolation(resistProgress);
+            return startResist + resistProgress * (maxResist - startResist);
+        };
+        resistAnim.addFloat(params.scaleTarget, params.scaleProperty, params.startScale, endScale,
+                scaleInterpolator);
+
         return resistAnim;
     }
 
@@ -228,7 +241,7 @@
 
         // These are not required, or can have a default value that is generally correct.
         @Nullable public PendingAnimation resistAnim = null;
-        public RecentsResistanceParams resistanceParams = RecentsResistanceParams.FROM_APP;
+        public RecentsResistanceParams resistanceParams;
         public float startScale = 1f;
         public float startTranslation = 0f;
 
@@ -242,6 +255,11 @@
             this.scaleProperty = scaleProperty;
             this.translationTarget = translationTarget;
             this.translationProperty = translationProperty;
+            if (dp.isTablet) {
+                resistanceParams = RecentsResistanceParams.FROM_APP_TABLET;
+            } else {
+                resistanceParams = RecentsResistanceParams.FROM_APP;
+            }
         }
 
         private RecentsParams setResistAnim(PendingAnimation resistAnim) {
diff --git a/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java b/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java
index 2e5b33a..c2101a8 100644
--- a/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java
+++ b/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java
@@ -19,12 +19,14 @@
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_UP;
 
+import android.content.Context;
 import android.util.Log;
 import android.view.InputEvent;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
 import com.android.quickstep.InputConsumer;
+import com.android.quickstep.SimpleOrientationTouchTransformer;
 import com.android.systemui.shared.system.InputConsumerController;
 
 import java.util.function.Supplier;
@@ -37,6 +39,8 @@
 
     private static final String TAG = "InputConsumerProxy";
 
+    private final Context mContext;
+    private final Supplier<Integer> mRotationSupplier;
     private final InputConsumerController mInputConsumerController;
     private Runnable mCallback;
     private Supplier<InputConsumer> mConsumerSupplier;
@@ -48,8 +52,11 @@
     private boolean mTouchInProgress = false;
     private boolean mDestroyPending = false;
 
-    public InputConsumerProxy(InputConsumerController inputConsumerController,
+    public InputConsumerProxy(Context context, Supplier<Integer> rotationSupplier,
+            InputConsumerController inputConsumerController,
             Runnable callback, Supplier<InputConsumer> consumerSupplier) {
+        mContext = context;
+        mRotationSupplier = rotationSupplier;
         mInputConsumerController = inputConsumerController;
         mCallback = callback;
         mConsumerSupplier = consumerSupplier;
@@ -98,6 +105,8 @@
             }
         }
         if (mInputConsumer != null) {
+            SimpleOrientationTouchTransformer.INSTANCE.get(mContext).transform(ev,
+                    mRotationSupplier.get());
             mInputConsumer.onMotionEvent(ev);
         }
 
diff --git a/quickstep/src/com/android/quickstep/util/LauncherSplitScreenListener.java b/quickstep/src/com/android/quickstep/util/LauncherSplitScreenListener.java
new file mode 100644
index 0000000..fa4cddc
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/LauncherSplitScreenListener.java
@@ -0,0 +1,164 @@
+package com.android.quickstep.util;
+
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+
+import android.content.Context;
+import android.os.IBinder;
+
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
+import com.android.launcher3.util.SplitConfigurationOptions.StageType;
+import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitTaskPosition;
+import com.android.quickstep.SystemUiProxy;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
+import com.android.wm.shell.splitscreen.ISplitScreenListener;
+
+/**
+ * Listeners for system wide split screen position and stage changes.
+ *
+ * Use {@link #getRunningSplitTaskIds()} to determine which tasks, if any, are actively in
+ * staged split.
+ *
+ * Use {@link #getPersistentSplitIds()} to know if tasks were in split screen before a quickswitch
+ * gesture happened.
+ */
+public class LauncherSplitScreenListener extends ISplitScreenListener.Stub {
+
+    public static final MainThreadInitializedObject<LauncherSplitScreenListener> INSTANCE =
+            new MainThreadInitializedObject<>(LauncherSplitScreenListener::new);
+
+    private static final int[] EMPTY_ARRAY = {};
+
+    private final StagedSplitTaskPosition mMainStagePosition = new StagedSplitTaskPosition();
+    private final StagedSplitTaskPosition mSideStagePosition = new StagedSplitTaskPosition();
+
+    private boolean mIsRecentsListFrozen = false;
+    private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
+        @Override
+        public void onRecentTaskListFrozenChanged(boolean frozen) {
+            super.onRecentTaskListFrozenChanged(frozen);
+            mIsRecentsListFrozen = frozen;
+
+            if (frozen) {
+                mPersistentGroupedIds = getRunningSplitTaskIds();
+            } else {
+                mPersistentGroupedIds = EMPTY_ARRAY;
+            }
+        }
+    };
+
+    /**
+     * Gets set to current split taskIDs whenever the task list is frozen, and set to empty array
+     * whenever task list unfreezes. This also gets set to empty array whenever the user swipes to
+     * home - in that case the task list does not unfreeze immediately after the gesture, so it's
+     * done via {@link #notifySwipingToHome()}.
+     *
+     * When not empty, this indicates that we need to load a GroupedTaskView as the most recent
+     * page, so user can quickswitch back to a grouped task.
+     */
+    private int[] mPersistentGroupedIds;
+
+    public LauncherSplitScreenListener(Context context) {
+        mMainStagePosition.stageType = SplitConfigurationOptions.STAGE_TYPE_MAIN;
+        mSideStagePosition.stageType = SplitConfigurationOptions.STAGE_TYPE_SIDE;
+    }
+
+    /** Also call {@link #destroy()} when done. */
+    public void init() {
+        SystemUiProxy.INSTANCE.getNoCreate().registerSplitScreenListener(this);
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
+    }
+
+    public void destroy() {
+        SystemUiProxy.INSTANCE.getNoCreate().unregisterSplitScreenListener(this);
+        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
+    }
+
+    /**
+     * This method returns the active split taskIDs that were active if a user quickswitched from
+     * split screen to a fullscreen app as long as the recents task list remains frozen.
+     */
+    public int[] getPersistentSplitIds() {
+        if (mIsRecentsListFrozen) {
+            return mPersistentGroupedIds;
+        } else {
+            return getRunningSplitTaskIds();
+        }
+    }
+    /**
+     * @return index 0 will be task in left/top position, index 1 in right/bottom position.
+     *         Will return empty array if device is not in staged split
+     */
+    public int[] getRunningSplitTaskIds() {
+        if (mMainStagePosition.taskId == -1 || mSideStagePosition.taskId == -1) {
+            return new int[]{};
+        }
+        int[] out = new int[2];
+        if (mMainStagePosition.stagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+            out[0] = mMainStagePosition.taskId;
+            out[1] = mSideStagePosition.taskId;
+        } else {
+            out[1] = mMainStagePosition.taskId;
+            out[0] = mSideStagePosition.taskId;
+        }
+        return out;
+    }
+
+    @Override
+    public void onStagePositionChanged(@StageType int stage, @StagePosition int position) {
+        if (stage == SplitConfigurationOptions.STAGE_TYPE_MAIN) {
+            mMainStagePosition.stagePosition = position;
+        } else {
+            mSideStagePosition.stagePosition = position;
+        }
+    }
+
+    @Override
+    public void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {
+        // If task is not visible but we are tracking it, stop tracking it
+        if (!visible) {
+            if (mMainStagePosition.taskId == taskId) {
+                resetTaskId(mMainStagePosition);
+            } else if (mSideStagePosition.taskId == taskId) {
+                resetTaskId(mSideStagePosition);
+            } // else it's an un-tracked child
+            return;
+        }
+
+        // If stage has moved to undefined, stop tracking the task
+        if (stage == SplitConfigurationOptions.STAGE_TYPE_UNDEFINED) {
+            resetTaskId(taskId == mMainStagePosition.taskId ?
+                    mMainStagePosition : mSideStagePosition);
+            return;
+        }
+
+        if (stage == SplitConfigurationOptions.STAGE_TYPE_MAIN) {
+            mMainStagePosition.taskId = taskId;
+        } else {
+            mSideStagePosition.taskId = taskId;
+        }
+    }
+
+    /** Notifies SystemUi to remove any split screen state */
+    public void notifySwipingToHome() {
+        boolean hasSplitTasks = LauncherSplitScreenListener.INSTANCE.getNoCreate()
+                .getPersistentSplitIds().length > 0;
+        if (!hasSplitTasks) {
+            return;
+        }
+
+        SystemUiProxy.INSTANCE.getNoCreate().exitSplitScreen(-1);
+        mPersistentGroupedIds = EMPTY_ARRAY;
+    }
+
+    private void resetTaskId(StagedSplitTaskPosition taskPosition) {
+        taskPosition.taskId = -1;
+    }
+
+    @Override
+    public IBinder asBinder() {
+        return this;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
new file mode 100644
index 0000000..b39412b
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
@@ -0,0 +1,118 @@
+/*
+ * 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.util;
+
+import static com.android.launcher3.Utilities.comp;
+
+import android.annotation.Nullable;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+
+import com.android.launcher3.Hotseat;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.util.HorizontalInsettableView;
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
+
+/**
+ * Controls animations that are happening during unfolding foldable devices
+ */
+public class LauncherUnfoldAnimationController {
+
+    // Percentage of the width of the quick search bar that will be reduced
+    // from the both sides of the bar when progress is 0
+    private static final float MAX_WIDTH_INSET_FRACTION = 0.15f;
+
+    private final Launcher mLauncher;
+
+    @Nullable
+    private HorizontalInsettableView mQsbInsettable;
+
+    private final ScopedUnfoldTransitionProgressProvider mProgressProvider;
+
+    public LauncherUnfoldAnimationController(
+            Launcher launcher,
+            WindowManager windowManager,
+            UnfoldTransitionProgressProvider unfoldTransitionProgressProvider) {
+        mLauncher = launcher;
+        mProgressProvider = new ScopedUnfoldTransitionProgressProvider(
+                unfoldTransitionProgressProvider);
+
+        mProgressProvider.addCallback(new UnfoldMoveFromCenterWorkspaceAnimator(launcher,
+                windowManager));
+        mProgressProvider.addCallback(new QsbAnimationListener());
+    }
+
+    /**
+     * Called when launcher is resumed
+     */
+    public void onResume() {
+        Hotseat hotseat = mLauncher.getHotseat();
+        if (hotseat != null && hotseat.getQsb() instanceof HorizontalInsettableView) {
+            mQsbInsettable = (HorizontalInsettableView) hotseat.getQsb();
+        }
+
+        final ViewTreeObserver obs = mLauncher.getWorkspace().getViewTreeObserver();
+        obs.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+            @Override
+            public boolean onPreDraw() {
+                if (obs.isAlive()) {
+                    mProgressProvider.setReadyToHandleTransition(true);
+                    obs.removeOnPreDrawListener(this);
+                }
+                return true;
+            }
+        });
+    }
+
+    /**
+     * Called when launcher activity is paused
+     */
+    public void onPause() {
+        mProgressProvider.setReadyToHandleTransition(false);
+        mQsbInsettable = null;
+    }
+
+    /**
+     * Called when launcher activity is destroyed
+     */
+    public void onDestroy() {
+        mProgressProvider.destroy();
+    }
+
+    private class QsbAnimationListener implements TransitionProgressListener {
+
+        @Override
+        public void onTransitionStarted() {
+        }
+
+        @Override
+        public void onTransitionFinished() {
+            if (mQsbInsettable != null) {
+                mQsbInsettable.setHorizontalInsets(0);
+            }
+        }
+
+        @Override
+        public void onTransitionProgress(float progress) {
+            if (mQsbInsettable != null) {
+                float insetPercentage = comp(progress) * MAX_WIDTH_INSET_FRACTION;
+                mQsbInsettable.setHorizontalInsets(insetPercentage);
+            }
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/LauncherViewsMoveFromCenterTranslationApplier.java b/quickstep/src/com/android/quickstep/util/LauncherViewsMoveFromCenterTranslationApplier.java
new file mode 100644
index 0000000..effdfdd
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/LauncherViewsMoveFromCenterTranslationApplier.java
@@ -0,0 +1,45 @@
+/*
+ * 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.util;
+
+import android.annotation.NonNull;
+import android.view.View;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.widget.NavigableAppWidgetHostView;
+import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator.TranslationApplier;
+
+/**
+ * Class that allows to set translations for move from center animation independently
+ * from other translations for certain launcher views
+ */
+public class LauncherViewsMoveFromCenterTranslationApplier implements TranslationApplier {
+
+    @Override
+    public void apply(@NonNull View view, float x, float y) {
+        if (view instanceof NavigableAppWidgetHostView) {
+            ((NavigableAppWidgetHostView) view).setTranslationForMoveFromCenterAnimation(x, y);
+        } else if (view instanceof BubbleTextView) {
+            ((BubbleTextView) view).setTranslationForMoveFromCenterAnimation(x, y);
+        } else if (view instanceof FolderIcon) {
+            ((FolderIcon) view).setTranslationForMoveFromCenterAnimation(x, y);
+        } else {
+            view.setTranslationX(x);
+            view.setTranslationY(y);
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index 8834dc2..302526d 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -42,8 +42,7 @@
             PagedOrientationHandler orientationHandler) {
         // Track the bottom of the window.
         Rect taskSize = new Rect();
-        LauncherActivityInterface.INSTANCE.calculateTaskSize(
-                context, dp, taskSize, orientationHandler);
+        LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, taskSize);
         return orientationHandler.getDistanceToBottomOfRect(dp, taskSize);
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index 1ed2da3..b83e26e 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -23,6 +23,7 @@
 import com.android.launcher3.Alarm;
 import com.android.launcher3.R;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.testing.TestProtocol;
 
 /**
  * Given positions along x- or y-axis, tracks velocity and acceleration and determines when there is
@@ -118,9 +119,10 @@
      * @param pointerIndex Index for the pointer being tracked in the motion event
      */
     public void addPosition(MotionEvent ev, int pointerIndex) {
-        mForcePauseTimeout.setAlarm(mMakePauseHarderToTrigger
-                ? HARDER_TRIGGER_TIMEOUT
-                : FORCE_PAUSE_TIMEOUT);
+        long timeoutMs = TestProtocol.sForcePauseTimeout != null
+                ? TestProtocol.sForcePauseTimeout
+                : mMakePauseHarderToTrigger ? HARDER_TRIGGER_TIMEOUT : FORCE_PAUSE_TIMEOUT;
+        mForcePauseTimeout.setAlarm(timeoutMs);
         float newVelocity = mVelocityProvider.addMotionEvent(ev, ev.getPointerId(pointerIndex));
         if (mPreviousVelocity != null) {
             checkMotionPaused(newVelocity, mPreviousVelocity, ev.getEventTime());
diff --git a/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java b/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java
new file mode 100644
index 0000000..3777c65
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java
@@ -0,0 +1,51 @@
+/*
+ * 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.util;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Screen status provider implementation that exposes methods to provide screen
+ * status updates to listeners. It is used to receive screen turned on event from
+ * SystemUI to Launcher.
+ */
+public class ProxyScreenStatusProvider implements ScreenStatusProvider {
+
+    public static final ProxyScreenStatusProvider INSTANCE = new ProxyScreenStatusProvider();
+    private final List<ScreenListener> mListeners = new ArrayList<>();
+
+    /**
+     * Called when the screen is on and ready (windows are drawn and screen blocker is removed)
+     */
+    public void onScreenTurnedOn() {
+        mListeners.forEach(ScreenListener::onScreenTurnedOn);
+    }
+
+    @Override
+    public void addCallback(@NonNull ScreenListener listener) {
+        mListeners.add(listener);
+    }
+
+    @Override
+    public void removeCallback(@NonNull ScreenListener listener) {
+        mListeners.remove(listener);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 7cfd151..9c6fd3d 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -218,8 +218,8 @@
 
     private boolean updateHandler() {
         mRecentsActivityRotation = inferRecentsActivityRotation(mDisplayRotation);
-        if (mRecentsActivityRotation == mTouchRotation
-                || (canRecentsActivityRotate() && (mFlags & FLAG_SWIPE_UP_NOT_RUNNING) != 0)) {
+        if (mRecentsActivityRotation == mTouchRotation || (isRecentsActivityRotationAllowed()
+                && (mFlags & FLAG_SWIPE_UP_NOT_RUNNING) != 0)) {
             mOrientationHandler = PagedOrientationHandler.PORTRAIT;
         } else if (mTouchRotation == ROTATION_90) {
             mOrientationHandler = PagedOrientationHandler.LANDSCAPE;
@@ -253,7 +253,7 @@
     private boolean setFlag(int mask, boolean enabled) {
         boolean wasRotationEnabled = !TestProtocol.sDisableSensorRotation
                 && (mFlags & VALUE_ROTATION_WATCHER_ENABLED) == VALUE_ROTATION_WATCHER_ENABLED
-                && !canRecentsActivityRotate();
+                && !isRecentsActivityRotationAllowed();
         if (enabled) {
             mFlags |= mask;
         } else {
@@ -262,7 +262,7 @@
 
         boolean isRotationEnabled = !TestProtocol.sDisableSensorRotation
                 && (mFlags & VALUE_ROTATION_WATCHER_ENABLED) == VALUE_ROTATION_WATCHER_ENABLED
-                && !canRecentsActivityRotate();
+                && !isRecentsActivityRotationAllowed();
         if (wasRotationEnabled != isRotationEnabled) {
             UI_HELPER_EXECUTOR.execute(() -> {
                 if (isRotationEnabled) {
@@ -376,13 +376,6 @@
     }
 
     /**
-     * Returns true if the activity can rotate, if allowed by system rotation settings
-     */
-    public boolean canRecentsActivityRotate() {
-        return (mFlags & FLAG_SYSTEM_ROTATION_ALLOWED) != 0 && isRecentsActivityRotationAllowed();
-    }
-
-    /**
      * Enables or disables the rotation watcher for listening to rotation callbacks
      */
     public void setRotationWatcherEnabled(boolean isEnabled) {
@@ -396,9 +389,17 @@
         Rect insets = dp.getInsets();
         float fullWidth = dp.widthPx;
         float fullHeight = dp.heightPx;
-        if (TaskView.CLIP_STATUS_AND_NAV_BARS) {
-            fullWidth -= insets.left + insets.right;
-            fullHeight -= insets.top + insets.bottom;
+        if (TaskView.clipLeft(dp)) {
+            fullWidth -= insets.left;
+        }
+        if (TaskView.clipRight(dp)) {
+            fullWidth -= insets.right;
+        }
+        if (TaskView.clipTop(dp)) {
+            fullHeight -= insets.top;
+        }
+        if (TaskView.clipBottom(dp)) {
+            fullHeight -= insets.bottom;
         }
 
         getTaskDimension(mContext, dp, outPivot);
@@ -592,17 +593,7 @@
             width = Math.min(currentSize.x, currentSize.y);
             height = Math.max(currentSize.x, currentSize.y);
         }
-
-        DeviceProfile bestMatch = idp.supportedProfiles.get(0);
-        float minDiff = Float.MAX_VALUE;
-        for (DeviceProfile profile : idp.supportedProfiles) {
-            float diff = Math.abs(profile.widthPx - width) + Math.abs(profile.heightPx - height);
-            if (diff < minDiff) {
-                minDiff = diff;
-                bestMatch = profile;
-            }
-        }
-        return bestMatch;
+        return idp.getBestMatch(width, height);
     }
 
     private static String nameAndAddress(Object obj) {
diff --git a/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java b/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
index 02ec68a..158fba9 100644
--- a/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
+++ b/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
@@ -15,24 +15,31 @@
  */
 package com.android.quickstep.util;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
 import android.animation.Animator;
 import android.content.Context;
 import android.graphics.PointF;
+import android.graphics.Rect;
 import android.graphics.RectF;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener;
 import androidx.dynamicanimation.animation.FloatPropertyCompat;
 import androidx.dynamicanimation.animation.SpringAnimation;
 import androidx.dynamicanimation.animation.SpringForce;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.FlingSpringAnim;
+import com.android.launcher3.touch.OverScroll;
 import com.android.launcher3.util.DynamicResource;
 import com.android.quickstep.RemoteAnimationTargets.ReleaseCheck;
 import com.android.systemui.plugins.ResourceProvider;
 
+import java.lang.annotation.Retention;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -94,7 +101,6 @@
     private float mCurrentCenterX;
     private float mCurrentY;
     // If true, tracking the bottom of the rects, else tracking the top.
-    private boolean mTrackingBottomY;
     private float mCurrentScaleProgress;
     private FlingSpringAnim mRectXAnim;
     private FlingSpringAnim mRectYAnim;
@@ -105,20 +111,68 @@
     private boolean mRectScaleAnimEnded;
 
     private float mMinVisChange;
-    private float mYOvershoot;
+    private int mMaxVelocityPxPerS;
 
-    public RectFSpringAnim(RectF startRect, RectF targetRect, Context context) {
+    /**
+     * Indicates which part of the start & target rects we are interpolating between.
+     */
+    public static final int TRACKING_TOP = 0;
+    public static final int TRACKING_CENTER = 1;
+    public static final int TRACKING_BOTTOM = 2;
+
+    @Retention(SOURCE)
+    @IntDef(value = {TRACKING_TOP,
+                    TRACKING_CENTER,
+                    TRACKING_BOTTOM})
+    public @interface Tracking{}
+
+    @Tracking
+    public final int mTracking;
+
+    public RectFSpringAnim(RectF startRect, RectF targetRect, Context context,
+            @Nullable DeviceProfile deviceProfile) {
         mStartRect = startRect;
         mTargetRect = targetRect;
         mCurrentCenterX = mStartRect.centerX();
 
-        mTrackingBottomY = startRect.bottom < targetRect.bottom;
-        mCurrentY = mTrackingBottomY ? mStartRect.bottom : mStartRect.top;
-
         ResourceProvider rp = DynamicResource.provider(context);
         mMinVisChange = rp.getDimension(R.dimen.swipe_up_fling_min_visible_change);
-        mYOvershoot = rp.getDimension(R.dimen.swipe_up_y_overshoot);
+        mMaxVelocityPxPerS = (int) rp.getDimension(R.dimen.swipe_up_max_velocity);
         setCanRelease(true);
+
+        if (deviceProfile == null) {
+            mTracking = startRect.bottom < targetRect.bottom
+                    ? TRACKING_BOTTOM
+                    : TRACKING_TOP;
+        } else {
+            int heightPx = deviceProfile.heightPx;
+            Rect padding = deviceProfile.workspacePadding;
+
+            final float topThreshold = heightPx / 3f;
+            final float bottomThreshold = deviceProfile.heightPx - padding.bottom;
+
+            if (targetRect.bottom > bottomThreshold) {
+                mTracking = TRACKING_BOTTOM;
+            } else if (targetRect.top < topThreshold) {
+                mTracking = TRACKING_TOP;
+            } else {
+                mTracking = TRACKING_CENTER;
+            }
+        }
+
+        mCurrentY = getTrackedYFromRect(mStartRect);
+    }
+
+    private float getTrackedYFromRect(RectF rect) {
+        switch (mTracking) {
+            case TRACKING_TOP:
+                return rect.top;
+            case TRACKING_BOTTOM:
+                return rect.bottom;
+            case TRACKING_CENTER:
+            default:
+                return rect.centerY();
+        }
     }
 
     public void onTargetPositionChanged() {
@@ -127,10 +181,22 @@
         }
 
         if (mRectYAnim != null) {
-            if (mTrackingBottomY && mRectYAnim.getTargetPosition() != mTargetRect.bottom) {
-                mRectYAnim.updatePosition(mCurrentY, mTargetRect.bottom);
-            } else if (!mTrackingBottomY && mRectYAnim.getTargetPosition() != mTargetRect.top) {
-                mRectYAnim.updatePosition(mCurrentY, mTargetRect.top);
+            switch (mTracking) {
+                case TRACKING_TOP:
+                    if (mRectYAnim.getTargetPosition() != mTargetRect.top) {
+                        mRectYAnim.updatePosition(mCurrentY, mTargetRect.top);
+                    }
+                    break;
+                case TRACKING_BOTTOM:
+                    if (mRectYAnim.getTargetPosition() != mTargetRect.bottom) {
+                        mRectYAnim.updatePosition(mCurrentY, mTargetRect.bottom);
+                    }
+                    break;
+                case TRACKING_CENTER:
+                    if (mRectYAnim.getTargetPosition() != mTargetRect.centerY()) {
+                        mRectYAnim.updatePosition(mCurrentY, mTargetRect.centerY());
+                    }
+                    break;
             }
         }
     }
@@ -159,22 +225,29 @@
             maybeOnEnd();
         });
 
+        // We dampen the user velocity here to keep the natural feeling and to prevent the
+        // rect from straying too from a linear path.
+        final float xVelocityPxPerS = velocityPxPerMs.x * 1000;
+        final float yVelocityPxPerS = velocityPxPerMs.y * 1000;
+        final float dampedXVelocityPxPerS = OverScroll.dampedScroll(
+                Math.abs(xVelocityPxPerS), mMaxVelocityPxPerS) * Math.signum(xVelocityPxPerS);
+        final float dampedYVelocityPxPerS = OverScroll.dampedScroll(
+                Math.abs(yVelocityPxPerS), mMaxVelocityPxPerS) * Math.signum(yVelocityPxPerS);
+
         float startX = mCurrentCenterX;
         float endX = mTargetRect.centerX();
         float minXValue = Math.min(startX, endX);
         float maxXValue = Math.max(startX, endX);
-        mRectXAnim = new FlingSpringAnim(this, context, RECT_CENTER_X, startX, endX,
-                velocityPxPerMs.x * 1000, mMinVisChange, minXValue, maxXValue, 1f, onXEndListener);
 
-        float startVelocityY = velocityPxPerMs.y * 1000;
-        // Scale the Y velocity based on the initial velocity to tune the curves.
-        float springVelocityFactor = 0.1f + 0.9f * Math.abs(startVelocityY) / 20000.0f;
+        mRectXAnim = new FlingSpringAnim(this, context, RECT_CENTER_X, startX, endX,
+                dampedXVelocityPxPerS, mMinVisChange, minXValue, maxXValue, onXEndListener);
+
         float startY = mCurrentY;
-        float endY = mTrackingBottomY ? mTargetRect.bottom : mTargetRect.top;
-        float minYValue = Math.min(startY, endY - mYOvershoot);
+        float endY = getTrackedYFromRect(mTargetRect);
+        float minYValue = Math.min(startY, endY);
         float maxYValue = Math.max(startY, endY);
-        mRectYAnim = new FlingSpringAnim(this, context, RECT_Y, startY, endY, startVelocityY,
-                mMinVisChange, minYValue, maxYValue, springVelocityFactor, onYEndListener);
+        mRectYAnim = new FlingSpringAnim(this, context, RECT_Y, startY, endY, dampedYVelocityPxPerS,
+                mMinVisChange, minYValue, maxYValue, onYEndListener);
 
         float minVisibleChange = Math.abs(1f / mStartRect.height());
         ResourceProvider rp = DynamicResource.provider(context);
@@ -234,12 +307,25 @@
                     mTargetRect.width());
             float currentHeight = Utilities.mapRange(mCurrentScaleProgress, mStartRect.height(),
                     mTargetRect.height());
-            if (mTrackingBottomY) {
-                mCurrentRect.set(mCurrentCenterX - currentWidth / 2, mCurrentY - currentHeight,
-                        mCurrentCenterX + currentWidth / 2, mCurrentY);
-            } else {
-                mCurrentRect.set(mCurrentCenterX - currentWidth / 2, mCurrentY,
-                        mCurrentCenterX + currentWidth / 2, mCurrentY + currentHeight);
+            switch (mTracking) {
+                case TRACKING_TOP:
+                    mCurrentRect.set(mCurrentCenterX - currentWidth / 2,
+                            mCurrentY,
+                            mCurrentCenterX + currentWidth / 2,
+                            mCurrentY + currentHeight);
+                    break;
+                case TRACKING_BOTTOM:
+                    mCurrentRect.set(mCurrentCenterX - currentWidth / 2,
+                            mCurrentY - currentHeight,
+                            mCurrentCenterX + currentWidth / 2,
+                            mCurrentY);
+                    break;
+                case TRACKING_CENTER:
+                    mCurrentRect.set(mCurrentCenterX - currentWidth / 2,
+                            mCurrentY - currentHeight / 2,
+                            mCurrentCenterX + currentWidth / 2,
+                            mCurrentY + currentHeight / 2);
+                    break;
             }
             for (OnUpdateListener onUpdateListener : mOnUpdateListeners) {
                 onUpdateListener.onUpdate(null, mCurrentRect, mCurrentScaleProgress);
diff --git a/quickstep/src/com/android/quickstep/util/RectFSpringAnim2.java b/quickstep/src/com/android/quickstep/util/RectFSpringAnim2.java
index c331a13..cb35809 100644
--- a/quickstep/src/com/android/quickstep/util/RectFSpringAnim2.java
+++ b/quickstep/src/com/android/quickstep/util/RectFSpringAnim2.java
@@ -132,7 +132,7 @@
 
     public RectFSpringAnim2(RectF startRect, RectF targetRect, Context context, float startRadius,
             float endRadius) {
-        super(startRect, targetRect, context);
+        super(startRect, targetRect, context, null);
         mStartRect = startRect;
         mTargetRect = targetRect;
 
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index a147b68..1dae2c8 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -16,158 +16,120 @@
 
 package com.android.quickstep.util;
 
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-
+import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 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 android.animation.AnimatorSet;
-import android.app.ActivityOptions;
-import android.content.res.Resources;
+import android.app.ActivityThread;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
-import android.util.Pair;
-import android.view.Gravity;
+import android.view.RemoteAnimationAdapter;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InsettableFrameLayout;
-import com.android.launcher3.LauncherAnimationRunner;
-import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
-import com.android.launcher3.R;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskAnimationManager;
 import com.android.quickstep.TaskViewUtils;
-import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
+import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.RemoteTransitionCompat;
 import com.android.systemui.shared.system.RemoteTransitionRunner;
 
+import java.util.function.Consumer;
+
 /**
  * Represent data needed for the transient state when user has selected one app for split screen
  * and is in the process of either a) selecting a second app or b) exiting intention to invoke split
  */
 public class SplitSelectStateController {
 
-    private final SystemUiProxy mSystemUiProxy;
-    private TaskView mInitialTaskView;
-    private SplitPositionOption mInitialPosition;
-    private Rect mInitialBounds;
     private final Handler mHandler;
+    private final SystemUiProxy mSystemUiProxy;
+    private @StagePosition int mStagePosition;
+    private Task mInitialTask;
+    private Task mSecondTask;
+    private Rect mInitialBounds;
 
     public SplitSelectStateController(Handler handler, SystemUiProxy systemUiProxy) {
-        mSystemUiProxy = systemUiProxy;
         mHandler = handler;
+        mSystemUiProxy = systemUiProxy;
     }
 
     /**
      * To be called after first task selected
      */
-    public void setInitialTaskSelect(TaskView taskView, SplitPositionOption positionOption,
+    public void setInitialTaskSelect(Task taskView, @StagePosition int stagePosition,
             Rect initialBounds) {
-        mInitialTaskView = taskView;
-        mInitialPosition = positionOption;
+        mInitialTask = taskView;
+        mStagePosition = stagePosition;
         mInitialBounds = initialBounds;
     }
 
     /**
      * To be called after second task selected
      */
-    public void setSecondTaskId(TaskView taskView) {
-        if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
-            // Assume initial task is for top/left part of screen
-            final int[] taskIds = mInitialPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT
-                    ? new int[]{mInitialTaskView.getTask().key.id, taskView.getTask().key.id}
-                    : new int[]{taskView.getTask().key.id, mInitialTaskView.getTask().key.id};
-
-            RemoteSplitLaunchAnimationRunner animationRunner =
-                    new RemoteSplitLaunchAnimationRunner(mInitialTaskView, taskView);
-            mSystemUiProxy.startTasks(taskIds[0], null /* mainOptions */, taskIds[1],
-                    null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
-                    new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR));
-            return;
-        }
-        // Assume initial mInitialTaskId is for top/left part of screen
-        RemoteAnimationFactory initialSplitRunnerWrapped =  new SplitLaunchAnimationRunner(
-                mInitialTaskView, 0);
-        RemoteAnimationFactory secondarySplitRunnerWrapped =  new SplitLaunchAnimationRunner(
-                taskView, 1);
-        RemoteAnimationRunnerCompat initialSplitRunner = new LauncherAnimationRunner(
-                new Handler(Looper.getMainLooper()), initialSplitRunnerWrapped,
-                true /* startAtFrontOfQueue */);
-        RemoteAnimationRunnerCompat secondarySplitRunner = new LauncherAnimationRunner(
-                new Handler(Looper.getMainLooper()), secondarySplitRunnerWrapped,
-                true /* startAtFrontOfQueue */);
-        ActivityOptions initialOptions = ActivityOptionsCompat.makeRemoteAnimation(
-                new RemoteAnimationAdapterCompat(initialSplitRunner, 300, 150));
-        ActivityOptions secondaryOptions = ActivityOptionsCompat.makeRemoteAnimation(
-                new RemoteAnimationAdapterCompat(secondarySplitRunner, 300, 150));
-        mSystemUiProxy.startTask(mInitialTaskView.getTask().key.id, mInitialPosition.mStageType,
-                mInitialPosition.mStagePosition,
-                /*null*/ initialOptions.toBundle());
-        Pair<Integer, Integer> compliment = getComplimentaryStageAndPosition(mInitialPosition);
-        mSystemUiProxy.startTask(taskView.getTask().key.id, compliment.first,
-                compliment.second,
-                /*null*/ secondaryOptions.toBundle());
-        // After successful launch, call resetState
-        resetState();
+    public void setSecondTaskId(Task taskView, Consumer<Boolean> callback) {
+        mSecondTask = taskView;
+        launchTasks(mInitialTask, mSecondTask, mStagePosition, callback);
     }
 
     /**
-     * @return {@link InsettableFrameLayout.LayoutParams} to correctly position the
-     * split placeholder view
+     * @param stagePosition representing location of task1
      */
-    public InsettableFrameLayout.LayoutParams getLayoutParamsForActivePosition(Resources resources,
-            DeviceProfile deviceProfile) {
-        InsettableFrameLayout.LayoutParams params =
-                new InsettableFrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT);
-        boolean topLeftPosition = mInitialPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT;
-        if (deviceProfile.isLandscape) {
-            params.width = (int) resources.getDimension(R.dimen.split_placeholder_size);
-            params.gravity = topLeftPosition ? Gravity.START : Gravity.END;
+    public void launchTasks(Task task1, Task task2, @StagePosition int stagePosition,
+            Consumer<Boolean> callback) {
+        // Assume initial task is for top/left part of screen
+        final int[] taskIds = stagePosition == STAGE_POSITION_TOP_OR_LEFT
+                ? new int[]{task1.key.id, task2.key.id}
+                : new int[]{task2.key.id, task1.key.id};
+        if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
+            RemoteSplitLaunchTransitionRunner animationRunner =
+                    new RemoteSplitLaunchTransitionRunner(task1, task2);
+            mSystemUiProxy.startTasks(taskIds[0], null /* mainOptions */, taskIds[1],
+                    null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
+                    new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR));
         } else {
-            params.height = (int) resources.getDimension(R.dimen.split_placeholder_size);
-            params.gravity = Gravity.TOP;
-        }
+            RemoteSplitLaunchAnimationRunner animationRunner =
+                    new RemoteSplitLaunchAnimationRunner(task1, task2, callback);
+            final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
+                    RemoteAnimationAdapterCompat.wrapRemoteAnimationRunner(animationRunner),
+                    300, 150,
+                    ActivityThread.currentActivityThread().getApplicationThread());
 
-        return params;
+            mSystemUiProxy.startTasksWithLegacyTransition(taskIds[0], null /* mainOptions */,
+                    taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, adapter);
+        }
     }
 
-    @Nullable
-    public SplitPositionOption getActiveSplitPositionOption() {
-        return mInitialPosition;
+    public @StagePosition int getActiveSplitStagePosition() {
+        return mStagePosition;
     }
 
     /**
      * Requires Shell Transitions
      */
-    private class RemoteSplitLaunchAnimationRunner implements RemoteTransitionRunner {
+    private class RemoteSplitLaunchTransitionRunner implements RemoteTransitionRunner {
 
-        private final TaskView mInitialTaskView;
-        private final TaskView mTaskView;
+        private final Task mInitialTask;
+        private final Task mSecondTask;
 
-        RemoteSplitLaunchAnimationRunner(TaskView initialTaskView, TaskView taskView) {
-            mInitialTaskView = initialTaskView;
-            mTaskView = taskView;
+        RemoteSplitLaunchTransitionRunner(Task initialTask, Task secondTask) {
+            mInitialTask = initialTask;
+            mSecondTask = secondTask;
         }
 
         @Override
         public void startAnimation(IBinder transition, TransitionInfo info,
                 SurfaceControl.Transaction t, Runnable finishCallback) {
-            TaskViewUtils.composeRecentsSplitLaunchAnimator(mInitialTaskView, mTaskView,
-                    info, t, finishCallback);
+            TaskViewUtils.composeRecentsSplitLaunchAnimator(mInitialTask,
+                    mSecondTask, info, t, finishCallback);
             // After successful launch, call resetState
             resetState();
         }
@@ -175,59 +137,63 @@
 
     /**
      * LEGACY
-     * @return the opposite stage and position from the {@param position} provided as first and
-     *         second object, respectively
-     * Ex. If position is has stage = Main and position = Top/Left, this will return
-     * Pair(stage=Side, position=Bottom/Left)
-     */
-    private Pair<Integer, Integer> getComplimentaryStageAndPosition(SplitPositionOption position) {
-        // Right now this is as simple as flipping between 0 and 1
-        int complimentStageType = position.mStageType ^ 1;
-        int complimentStagePosition = position.mStagePosition ^ 1;
-        return new Pair<>(complimentStageType, complimentStagePosition);
-    }
-
-    /**
-     * LEGACY
      * Remote animation runner for animation to launch an app.
      */
-    private class SplitLaunchAnimationRunner implements RemoteAnimationFactory {
+    private class RemoteSplitLaunchAnimationRunner implements RemoteAnimationRunnerCompat {
 
-        private final TaskView mV;
-        private final int mTargetState;
+        private final Task mInitialTask;
+        private final Task mSecondTask;
+        private final Consumer<Boolean> mSuccessCallback;
 
-        SplitLaunchAnimationRunner(TaskView v, int targetState) {
-            mV = v;
-            mTargetState = targetState;
+        RemoteSplitLaunchAnimationRunner(Task initialTask, Task secondTask,
+                Consumer<Boolean> successCallback) {
+            mInitialTask = initialTask;
+            mSecondTask = secondTask;
+            mSuccessCallback = successCallback;
         }
 
         @Override
-        public void onCreateAnimation(int transit,
-                RemoteAnimationTargetCompat[] appTargets,
-                RemoteAnimationTargetCompat[] wallpaperTargets,
-                RemoteAnimationTargetCompat[] nonAppTargets,
-                LauncherAnimationRunner.AnimationResult result) {
-            AnimatorSet anim = new AnimatorSet();
-            BaseQuickstepLauncher activity = BaseActivity.fromContext(mV.getContext());
-            TaskViewUtils.composeRecentsSplitLaunchAnimatorLegacy(anim, mV,
-                    appTargets, wallpaperTargets, nonAppTargets, true, activity.getStateManager(),
-                    activity.getDepthController(), mTargetState);
-            result.setAnimation(anim, activity);
+        public void onAnimationStart(int transit, RemoteAnimationTargetCompat[] apps,
+                RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps,
+                Runnable finishedCallback) {
+            postAsyncCallback(mHandler,
+                    () -> TaskViewUtils.composeRecentsSplitLaunchAnimatorLegacy(mInitialTask,
+                            mSecondTask, apps, wallpapers, nonApps, () -> {
+                                finishedCallback.run();
+                                if (mSuccessCallback != null) {
+                                    mSuccessCallback.accept(true);
+                                }
+                                resetState();
+                            }));
+        }
+
+        @Override
+        public void onAnimationCancelled() {
+            postAsyncCallback(mHandler, () -> {
+                if (mSuccessCallback != null) {
+                    mSuccessCallback.accept(false);
+                }
+                resetState();
+            });
         }
     }
 
-
     /**
      * To be called if split select was cancelled
      */
     public void resetState() {
-        mInitialTaskView = null;
-        mInitialPosition = null;
+        mInitialTask = null;
+        mSecondTask = null;
+        mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
         mInitialBounds = null;
     }
 
+    /**
+     * @return {@code true} if first task has been selected and waiting for the second task to be
+     *         chosen
+     */
     public boolean isSplitSelectActive() {
-        return mInitialTaskView != null;
+        return mInitialTask != null && mSecondTask == null;
     }
 
     public Rect getInitialBounds() {
diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index ccc587c..44396fa 100644
--- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -197,7 +197,7 @@
         launcher.getStateManager().createAtomicAnimation(BACKGROUND_APP, NORMAL, config).start();
 
         // Stop scrolling so that it doesn't interfere with the translation offscreen.
-        launcher.<RecentsView>getOverviewPanel().getScroller().forceFinished(true);
+        launcher.<RecentsView>getOverviewPanel().forceFinishScroller();
 
         if (animateOverviewScrim) {
             launcher.getWorkspace().getStateTransitionAnimation()
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index c0f5c14..a30216c 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -40,6 +40,7 @@
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.util.Themes;
+import com.android.quickstep.TaskAnimationManager;
 import com.android.systemui.shared.pip.PipSurfaceTransactionHelper;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 
@@ -115,7 +116,7 @@
             @NonNull Rect destinationBoundsTransformed,
             int cornerRadius,
             @NonNull View view) {
-        super(startBounds, new RectF(destinationBoundsTransformed), context);
+        super(startBounds, new RectF(destinationBoundsTransformed), context, null);
         mTaskId = taskId;
         mComponentName = componentName;
         mLeash = leash;
@@ -278,19 +279,36 @@
 
     private RotatedPosition getRotatedPosition(float progress) {
         final float degree, positionX, positionY;
-        if (mFromRotation == Surface.ROTATION_90) {
-            degree = -90 * progress;
-            positionX = progress * (mDestinationBoundsTransformed.left - mStartBounds.left)
-                    + mStartBounds.left;
-            positionY = progress * (mDestinationBoundsTransformed.bottom - mStartBounds.top)
-                    + mStartBounds.top;
+        if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
+            if (mFromRotation == Surface.ROTATION_90) {
+                degree = -90 * (1 - progress);
+                positionX = progress * (mDestinationBoundsTransformed.left - mStartBounds.left)
+                        + mStartBounds.left;
+                positionY = progress * (mDestinationBoundsTransformed.top - mStartBounds.top)
+                        + mStartBounds.top + mStartBounds.bottom * (1 - progress);
+            } else {
+                degree = 90 * (1 - progress);
+                positionX = progress * (mDestinationBoundsTransformed.left - mStartBounds.left)
+                        + mStartBounds.left + mStartBounds.right * (1 - progress);
+                positionY = progress * (mDestinationBoundsTransformed.top - mStartBounds.top)
+                        + mStartBounds.top;
+            }
         } else {
-            degree = 90 * progress;
-            positionX = progress * (mDestinationBoundsTransformed.right - mStartBounds.left)
-                    + mStartBounds.left;
-            positionY = progress * (mDestinationBoundsTransformed.top - mStartBounds.top)
-                    + mStartBounds.top;
+            if (mFromRotation == Surface.ROTATION_90) {
+                degree = -90 * progress;
+                positionX = progress * (mDestinationBoundsTransformed.left - mStartBounds.left)
+                        + mStartBounds.left;
+                positionY = progress * (mDestinationBoundsTransformed.bottom - mStartBounds.top)
+                        + mStartBounds.top;
+            } else {
+                degree = 90 * progress;
+                positionX = progress * (mDestinationBoundsTransformed.right - mStartBounds.left)
+                        + mStartBounds.left;
+                positionY = progress * (mDestinationBoundsTransformed.top - mStartBounds.top)
+                        + mStartBounds.top;
+            }
         }
+
         return new RotatedPosition(degree, positionX, positionY);
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/TISBindHelper.java b/quickstep/src/com/android/quickstep/util/TISBindHelper.java
new file mode 100644
index 0000000..92c60c8
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/TISBindHelper.java
@@ -0,0 +1,124 @@
+/*
+ * 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.util;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.quickstep.TouchInteractionService;
+import com.android.quickstep.TouchInteractionService.TISBinder;
+
+import java.util.function.Consumer;
+
+/**
+ * Utility class to simplify binding to {@link TouchInteractionService}
+ */
+public class TISBindHelper implements ServiceConnection {
+
+    private static final String TAG = "TISBindHelper";
+
+    private static final long BACKOFF_MILLIS = 1000;
+
+    // Max backoff caps at 5 mins
+    private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000;
+
+    private final Handler mHandler = new Handler();
+    private final Runnable mConnectionRunnable = this::internalBindToTIS;
+    private final Context mContext;
+    private final Consumer<TISBinder> mConnectionCallback;
+
+    private short mConnectionAttempts;
+    private boolean mTisServiceBound;
+
+    public TISBindHelper(Context context, Consumer<TISBinder> connectionCallback) {
+        mContext = context;
+        mConnectionCallback = connectionCallback;
+        internalBindToTIS();
+    }
+
+    @Override
+    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+        if (!(iBinder instanceof TISBinder)) {
+            // Seems like there can be a race condition when user unlocks, which kills the TIS
+            // process and re-starts it. I guess in the meantime service can be connected to
+            // a killed TIS? Either way, unbind and try to re-connect in that case.
+            internalUnbindToTIS();
+            mHandler.postDelayed(mConnectionRunnable, BACKOFF_MILLIS);
+            return;
+        }
+
+        Log.d(TAG, "TIS service connected");
+        mConnectionCallback.accept((TISBinder) iBinder);
+        resetServiceBindRetryState();
+    }
+
+    @Override
+    public void onServiceDisconnected(ComponentName componentName) { }
+
+    @Override
+    public void onBindingDied(ComponentName name) {
+        Log.w(TAG, "TIS binding died");
+        internalBindToTIS();
+    }
+
+
+    /**
+     * Binds to {@link TouchInteractionService}. If the binding fails, attempts to retry via
+     * {@link #mConnectionRunnable}. Unbind via {@link #internalUnbindToTIS()}
+     */
+    private void internalBindToTIS() {
+        mTisServiceBound = mContext.bindService(new Intent(mContext, TouchInteractionService.class),
+                this, 0);
+        if (mTisServiceBound) {
+            resetServiceBindRetryState();
+            return;
+        }
+
+        Log.w(TAG, "Retrying TIS Binder connection attempt: " + mConnectionAttempts);
+        final long timeoutMs = (long) Math.min(
+                Math.scalb(BACKOFF_MILLIS, mConnectionAttempts), MAX_BACKOFF_MILLIS);
+        mHandler.postDelayed(mConnectionRunnable, timeoutMs);
+        mConnectionAttempts++;
+    }
+
+    /** See {@link #internalBindToTIS()} */
+    private void internalUnbindToTIS() {
+        if (mTisServiceBound) {
+            mContext.unbindService(this);
+            mTisServiceBound = false;
+        }
+    }
+
+    private void resetServiceBindRetryState() {
+        if (mHandler.hasCallbacks(mConnectionRunnable)) {
+            mHandler.removeCallbacks(mConnectionRunnable);
+        }
+        mConnectionAttempts = 0;
+    }
+
+    /**
+     * Called when the activity is destroyed to clear the binding
+     */
+    public void onDestroy() {
+        internalUnbindToTIS();
+        resetServiceBindRetryState();
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index cceb872..734c844 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -15,9 +15,13 @@
  */
 package com.android.quickstep.util;
 
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.states.RotationHelper.deltaRotation;
 import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TRANSLATE;
+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_POSITION_UNDEFINED;
+import static com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
+import static com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
 import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
 import static com.android.quickstep.util.RecentsOrientedState.preDisplayRotation;
 import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
@@ -26,10 +30,10 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Matrix;
-import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
 
@@ -50,26 +54,28 @@
  */
 public class TaskViewSimulator implements TransformParams.BuilderProxy {
 
+    private static final String TAG = "TaskViewSimulator";
+    private static final boolean DEBUG = false;
+
     private final Rect mTmpCropRect = new Rect();
     private final RectF mTempRectF = new RectF();
     private final float[] mTempPoint = new float[2];
 
     private final Context mContext;
     private final BaseActivityInterface mSizeStrategy;
-    private final boolean mIsForLiveTile;
 
     @NonNull
     private RecentsOrientedState mOrientationState;
     private final boolean mIsRecentsRtl;
 
     private final Rect mTaskRect = new Rect();
-    private boolean mDrawsBelowRecents;
     private final PointF mPivot = new PointF();
     private DeviceProfile mDp;
+    @StagePosition
+    private int mStagePosition = STAGE_POSITION_UNDEFINED;
 
     private final Matrix mMatrix = new Matrix();
     private final Matrix mMatrixTmp = new Matrix();
-    private final Point mRunningTargetWindowPosition = new Point();
 
     // Thumbnail view properties
     private final Rect mThumbnailPosition = new Rect();
@@ -92,16 +98,11 @@
     // Cached calculations
     private boolean mLayoutValid = false;
     private int mOrientationStateId;
+    private StagedSplitBounds mStagedSplitBounds;
 
     public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy) {
-        this(context, sizeStrategy, false);
-    }
-
-    public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy,
-            boolean isForLiveTile) {
         mContext = context;
         mSizeStrategy = sizeStrategy;
-        mIsForLiveTile = isForLiveTile;
 
         // TODO(b/187074722): Don't create this per-TaskViewSimulator
         mOrientationState = TraceHelper.allowIpcs("",
@@ -137,9 +138,20 @@
         if (mDp == null) {
             return 1;
         }
-        mSizeStrategy.calculateTaskSize(mContext, mDp, mTaskRect,
-                mOrientationState.getOrientationHandler());
-        return mOrientationState.getFullScreenScaleAndPivot(mTaskRect, mDp, mPivot);
+        Rect fullTaskSize = new Rect();
+        mSizeStrategy.calculateTaskSize(mContext, mDp, fullTaskSize);
+
+        if (mStagedSplitBounds != null) {
+            // The task rect changes according to the staged split task sizes, but recents
+            // fullscreen scale and pivot remains the same since the task fits into the existing
+            // sized task space bounds
+            mSizeStrategy.calculateTaskSize(mContext, mDp, mTaskRect);
+            mOrientationState.getOrientationHandler()
+                    .setSplitTaskSwipeRect(mDp, mTaskRect, mStagedSplitBounds, mStagePosition);
+        } else {
+            mTaskRect.set(fullTaskSize);
+        }
+        return mOrientationState.getFullScreenScaleAndPivot(fullTaskSize, mDp, mPivot);
     }
 
     /**
@@ -147,8 +159,24 @@
      */
     public void setPreview(RemoteAnimationTargetCompat runningTarget) {
         setPreviewBounds(runningTarget.screenSpaceBounds, runningTarget.contentInsets);
-        mRunningTargetWindowPosition.set(runningTarget.screenSpaceBounds.left,
-                runningTarget.screenSpaceBounds.top);
+    }
+
+    /**
+     * Sets the targets which the simulator will control specifically for targets to animate when
+     * in split screen
+     *
+     * @param splitInfo set to {@code null} when not in staged split mode
+     */
+    public void setPreview(RemoteAnimationTargetCompat runningTarget, StagedSplitBounds splitInfo) {
+        setPreview(runningTarget);
+        mStagedSplitBounds = splitInfo;
+        if (mStagedSplitBounds == null) {
+            mStagePosition = STAGE_POSITION_UNDEFINED;
+            return;
+        }
+        mStagePosition = mThumbnailPosition.equals(splitInfo.leftTopBounds) ?
+                STAGE_POSITION_TOP_OR_LEFT :
+                STAGE_POSITION_BOTTOM_OR_RIGHT;
     }
 
     /**
@@ -170,10 +198,6 @@
         recentsViewScroll.value = scroll;
     }
 
-    public void setDrawsBelowRecents(boolean drawsBelowRecents) {
-        mDrawsBelowRecents = drawsBelowRecents;
-    }
-
     /**
      * Adds animation for all the components corresponding to transition from an app to overview.
      */
@@ -230,12 +254,11 @@
      * window coordinate space.
      */
     public void applyWindowToHomeRotation(Matrix matrix) {
-        mMatrix.postTranslate(mDp.windowX, mDp.windowY);
+        matrix.postTranslate(mDp.windowX, mDp.windowY);
         postDisplayRotation(deltaRotation(
                 mOrientationState.getRecentsActivityRotation(),
                 mOrientationState.getDisplayRotation()),
                 mDp.widthPx, mDp.heightPx, matrix);
-        matrix.postTranslate(-mRunningTargetWindowPosition.x, -mRunningTargetWindowPosition.y);
     }
 
     /**
@@ -259,12 +282,14 @@
                     mTaskRect.width(), mTaskRect.height(),
                     mDp, mOrientationState.getRecentsActivityRotation(), isRtlEnabled);
             mPositionHelper.getMatrix().invert(mInversePositionMatrix);
+            if (DEBUG) {
+                Log.d(TAG, " taskRect: " + mTaskRect);
+            }
         }
 
         float fullScreenProgress = Utilities.boundToRange(this.fullScreenProgress.value, 0, 1);
-        mCurrentFullscreenParams.setProgress(
-                fullScreenProgress, recentsViewScale.value, mTaskRect.width(), mDp,
-                mPositionHelper);
+        mCurrentFullscreenParams.setProgress(fullScreenProgress, recentsViewScale.value,
+                /* taskViewScale= */1f, mTaskRect.width(), mDp, mPositionHelper);
 
         // Apply thumbnail matrix
         RectF insets = mCurrentFullscreenParams.mCurrentDrawnInsets;
@@ -300,6 +325,24 @@
         mTempRectF.roundOut(mTmpCropRect);
 
         params.applySurfaceParams(params.createSurfaceParams(this));
+
+        if (!DEBUG) {
+            return;
+        }
+        Log.d(TAG, "progress: " + fullScreenProgress
+                + " scale: " + scale
+                + " recentsViewScale: " + recentsViewScale.value
+                + " crop: " + mTmpCropRect
+                + " radius: " + getCurrentCornerRadius()
+                + " taskW: " + taskWidth + " H: " + taskHeight
+                + " taskRect: " + mTaskRect
+                + " taskPrimaryT: " + taskPrimaryTranslation.value
+                + " recentsPrimaryT: " + recentsViewPrimaryTranslation.value
+                + " recentsSecondaryT: " + recentsViewSecondaryTranslation.value
+                + " taskSecondaryT: " + taskSecondaryTranslation.value
+                + " recentsScroll: " + recentsViewScroll.value
+                + " pivot: " + mPivot
+        );
     }
 
     @Override
@@ -308,13 +351,6 @@
         builder.withMatrix(mMatrix)
                 .withWindowCrop(mTmpCropRect)
                 .withCornerRadius(getCurrentCornerRadius());
-
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mIsForLiveTile
-                && params.getRecentsSurface() != null) {
-            // When relativeLayer = 0, it reverts the surfaces back to the original order.
-            builder.withRelativeLayerTo(params.getRecentsSurface(),
-                    mDrawsBelowRecents ? Integer.MIN_VALUE : 0);
-        }
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
new file mode 100644
index 0000000..95403b2
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
@@ -0,0 +1,121 @@
+/*
+ * 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.util;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.Hotseat;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.Workspace;
+import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator;
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Animation that moves launcher icons and widgets from center to the sides (final position)
+ */
+public class UnfoldMoveFromCenterWorkspaceAnimator
+        implements UnfoldTransitionProgressProvider.TransitionProgressListener {
+
+    private final Launcher mLauncher;
+    private final UnfoldMoveFromCenterAnimator mMoveFromCenterAnimation;
+
+    private final Map<ViewGroup, Boolean> mOriginalClipToPadding = new HashMap<>();
+    private final Map<ViewGroup, Boolean> mOriginalClipChildren = new HashMap<>();
+
+    public UnfoldMoveFromCenterWorkspaceAnimator(Launcher launcher, WindowManager windowManager) {
+        mLauncher = launcher;
+        mMoveFromCenterAnimation = new UnfoldMoveFromCenterAnimator(windowManager,
+                new LauncherViewsMoveFromCenterTranslationApplier());
+    }
+
+    @Override
+    public void onTransitionStarted() {
+        mMoveFromCenterAnimation.updateDisplayProperties();
+
+        Workspace workspace = mLauncher.getWorkspace();
+        Hotseat hotseat = mLauncher.getHotseat();
+
+        // App icons and widgets
+        workspace
+                .forEachVisiblePage(page -> {
+                    final CellLayout cellLayout = (CellLayout) page;
+                    ShortcutAndWidgetContainer itemsContainer = cellLayout
+                            .getShortcutsAndWidgets();
+                    disableClipping(cellLayout);
+
+                    for (int i = 0; i < itemsContainer.getChildCount(); i++) {
+                        View child = itemsContainer.getChildAt(i);
+                        mMoveFromCenterAnimation.registerViewForAnimation(child);
+                    }
+                });
+
+        disableClipping(workspace);
+
+        // Hotseat icons
+        ViewGroup hotseatIcons = hotseat.getShortcutsAndWidgets();
+        disableClipping(hotseat);
+
+        for (int i = 0; i < hotseatIcons.getChildCount(); i++) {
+            View child = hotseatIcons.getChildAt(i);
+            mMoveFromCenterAnimation.registerViewForAnimation(child);
+        }
+
+        onTransitionProgress(0f);
+    }
+
+    @Override
+    public void onTransitionProgress(float progress) {
+        mMoveFromCenterAnimation.onTransitionProgress(progress);
+    }
+
+    @Override
+    public void onTransitionFinished() {
+        mMoveFromCenterAnimation.onTransitionFinished();
+        mMoveFromCenterAnimation.clearRegisteredViews();
+
+        restoreClipping(mLauncher.getWorkspace());
+        mLauncher.getWorkspace().forEachVisiblePage(page -> restoreClipping((CellLayout) page));
+        restoreClipping(mLauncher.getHotseat());
+
+        mOriginalClipChildren.clear();
+        mOriginalClipToPadding.clear();
+    }
+
+    private void disableClipping(ViewGroup view) {
+        mOriginalClipToPadding.put(view, view.getClipToPadding());
+        mOriginalClipChildren.put(view, view.getClipChildren());
+        view.setClipToPadding(false);
+        view.setClipChildren(false);
+    }
+
+    private void restoreClipping(ViewGroup view) {
+        final Boolean originalClipToPadding = mOriginalClipToPadding.get(view);
+        if (originalClipToPadding != null) {
+            view.setClipToPadding(originalClipToPadding);
+        }
+        final Boolean originalClipChildren = mOriginalClipChildren.get(view);
+        if (originalClipChildren != null) {
+            view.setClipChildren(originalClipChildren);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/util/VibratorWrapper.java b/quickstep/src/com/android/quickstep/util/VibratorWrapper.java
similarity index 65%
rename from src/com/android/launcher3/util/VibratorWrapper.java
rename to quickstep/src/com/android/quickstep/util/VibratorWrapper.java
index b0defd4..211bd08 100644
--- a/src/com/android/launcher3/util/VibratorWrapper.java
+++ b/quickstep/src/com/android/quickstep/util/VibratorWrapper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.util;
+package com.android.quickstep.util;
 
 import static android.os.VibrationEffect.createPredefined;
 import static android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED;
@@ -21,15 +21,20 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
+import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
+import android.media.AudioAttributes;
 import android.os.Build;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.provider.Settings;
 
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.MainThreadInitializedObject;
+
 /**
  * Wrapper around {@link Vibrator} to easily perform haptic feedback where necessary.
  */
@@ -39,8 +44,15 @@
     public static final MainThreadInitializedObject<VibratorWrapper> INSTANCE =
             new MainThreadInitializedObject<>(VibratorWrapper::new);
 
+    public static final AudioAttributes VIBRATION_ATTRS = new AudioAttributes.Builder()
+            .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+            .build();
+
     public static final VibrationEffect EFFECT_CLICK =
             createPredefined(VibrationEffect.EFFECT_CLICK);
+    public static final VibrationEffect EFFECT_TEXTURE_TICK =
+            VibrationEffect.createPredefined(VibrationEffect.EFFECT_TEXTURE_TICK);
 
     /**
      * Haptic when entering overview.
@@ -78,7 +90,27 @@
     /** Vibrates with the given effect if haptic feedback is available and enabled. */
     public void vibrate(VibrationEffect vibrationEffect) {
         if (mHasVibrator && mIsHapticFeedbackEnabled) {
-            UI_HELPER_EXECUTOR.execute(() -> mVibrator.vibrate(vibrationEffect));
+            UI_HELPER_EXECUTOR.execute(() -> mVibrator.vibrate(vibrationEffect, VIBRATION_ATTRS));
+        }
+    }
+
+    /**
+     * Vibrates with a single primitive, if supported, or use a fallback effect instead. This only
+     * vibrates if haptic feedback is available and enabled.
+     */
+    @SuppressLint("NewApi")
+    public void vibrate(int primitiveId, float primitiveScale, VibrationEffect fallbackEffect) {
+        if (mHasVibrator && mIsHapticFeedbackEnabled) {
+            UI_HELPER_EXECUTOR.execute(() -> {
+                if (Utilities.ATLEAST_R && primitiveId >= 0
+                        && mVibrator.areAllPrimitivesSupported(primitiveId)) {
+                    mVibrator.vibrate(VibrationEffect.startComposition()
+                            .addPrimitive(primitiveId, primitiveScale)
+                            .compose(), VIBRATION_ATTRS);
+                } else {
+                    mVibrator.vibrate(fallbackEffect, VIBRATION_ATTRS);
+                }
+            });
         }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java b/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java
index df94d0b..7ae6cb7 100644
--- a/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java
+++ b/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java
@@ -120,7 +120,7 @@
         launcher.getStateManager().createAtomicAnimation(BACKGROUND_APP, NORMAL, config).start();
 
         // Stop scrolling so that it doesn't interfere with the translation offscreen.
-        launcher.<RecentsView>getOverviewPanel().getScroller().forceFinished(true);
+        launcher.<RecentsView>getOverviewPanel().forceFinishScroller();
 
         if (animateOverviewScrim) {
             launcher.getWorkspace().getStateTransitionAnimation()
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
index b9a9006..86be210 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -39,10 +39,24 @@
                 }
             };
 
+    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 final StatefulActivity mActivity;
     private float mScrollAlpha = 1;
     private float mContentAlpha = 1;
     private float mVisibilityAlpha = 1;
+    private float mDismissAlpha = 1;
     private float mFullscreenProgress = 1;
     private float mGridProgress = 1;
 
@@ -52,6 +66,7 @@
     private float mGridTranslationPrimary;
     private float mGridScrollOffset;
     private float mScrollOffsetPrimary;
+    private float mSplitSelectScrollOffsetPrimary;
 
     private int mSidePadding;
 
@@ -97,6 +112,13 @@
         }
     }
 
+    public void setDismissAlpha(float alpha) {
+        if (mDismissAlpha != alpha) {
+            mDismissAlpha = alpha;
+            updateAlpha();
+        }
+    }
+
     public void onRecentsViewScroll(int scroll, boolean gridEnabled) {
         RecentsView recentsView = getRecentsView();
         if (recentsView == null) {
@@ -123,7 +145,7 @@
     }
 
     private void updateAlpha() {
-        final float alpha = mScrollAlpha * mContentAlpha * mVisibilityAlpha;
+        final float alpha = mScrollAlpha * mContentAlpha * mVisibilityAlpha * mDismissAlpha;
         setAlpha(alpha);
         setClickable(Math.min(alpha, 1) == 1);
     }
@@ -146,6 +168,10 @@
         mScrollOffsetPrimary = scrollOffsetPrimary;
     }
 
+    public void setSplitSelectScrollOffsetPrimary(float splitSelectScrollOffsetPrimary) {
+        mSplitSelectScrollOffsetPrimary = splitSelectScrollOffsetPrimary;
+    }
+
     public float getScrollAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
         float scrollAdjustment = 0;
         if (fullscreenEnabled) {
@@ -155,6 +181,7 @@
             scrollAdjustment += mGridTranslationPrimary + mGridScrollOffset;
         }
         scrollAdjustment += mScrollOffsetPrimary;
+        scrollAdjustment += mSplitSelectScrollOffsetPrimary;
         return scrollAdjustment;
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
new file mode 100644
index 0000000..5a86464
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -0,0 +1,242 @@
+package com.android.quickstep.views;
+
+import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
+
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.launcher3.InsettableFrameLayout;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.util.MultiValueUpdateListener;
+
+/**
+ * Create an instance via {@link #getFloatingTaskView(StatefulActivity, TaskView, RectF)} to
+ * which will have the thumbnail from the provided existing TaskView overlaying the taskview itself.
+ *
+ * Can then animate the taskview using
+ * {@link #addAnimation(PendingAnimation, RectF, Rect, View, boolean)}
+ * giving a starting and ending bounds. Currently this is set to use the split placeholder view,
+ * but it could be generified.
+ *
+ * TODO: Figure out how to copy thumbnail data from existing TaskView to this view.
+ */
+public class FloatingTaskView extends FrameLayout {
+
+    private SplitPlaceholderView mSplitPlaceholderView;
+    private RectF mStartingPosition;
+    private final Launcher mLauncher;
+    private final boolean mIsRtl;
+    private final Rect mOutline = new Rect();
+    private PagedOrientationHandler mOrientationHandler;
+    private ImageView mImageView;
+
+    public FloatingTaskView(Context context) {
+        this(context, null);
+    }
+
+    public FloatingTaskView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public FloatingTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mLauncher = Launcher.getLauncher(context);
+        mIsRtl = Utilities.isRtl(getResources());
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mImageView = findViewById(R.id.thumbnail);
+        mImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
+        mImageView.setLayerType(LAYER_TYPE_HARDWARE, null);
+        mSplitPlaceholderView = findViewById(R.id.split_placeholder);
+        mSplitPlaceholderView.setAlpha(0);
+        mSplitPlaceholderView.setBackgroundColor(getResources().getColor(android.R.color.white));
+    }
+
+    public static FloatingTaskView getFloatingTaskView(StatefulActivity launcher,
+            TaskView originalView, RectF positionOut) {
+        final BaseDragLayer dragLayer = launcher.getDragLayer();
+        ViewGroup parent = (ViewGroup) dragLayer.getParent();
+        final FloatingTaskView floatingView = (FloatingTaskView) launcher.getLayoutInflater()
+                .inflate(R.layout.floating_split_select_view, parent, false);
+
+        floatingView.mStartingPosition = positionOut;
+        floatingView.updateInitialPositionForView(originalView);
+        final InsettableFrameLayout.LayoutParams lp =
+                (InsettableFrameLayout.LayoutParams) floatingView.getLayoutParams();
+
+        floatingView.mSplitPlaceholderView.setLayoutParams(
+                new FrameLayout.LayoutParams(lp.width, lp.height));
+        positionOut.round(floatingView.mOutline);
+        floatingView.setPivotX(0);
+        floatingView.setPivotY(0);
+
+        // Copy bounds of exiting thumbnail into ImageView
+        TaskThumbnailView thumbnail = originalView.getThumbnail();
+        floatingView.mImageView.setImageBitmap(thumbnail.getThumbnail());
+        floatingView.mImageView.setVisibility(VISIBLE);
+
+        floatingView.mOrientationHandler =
+                originalView.getRecentsView().getPagedOrientationHandler();
+        floatingView.mSplitPlaceholderView.setIconView(originalView.getIconView(),
+                launcher.getDeviceProfile().overviewTaskIconDrawableSizePx);
+        floatingView.mSplitPlaceholderView.getIconView()
+                .setRotation(floatingView.mOrientationHandler.getDegreesRotated());
+        parent.addView(floatingView);
+        return floatingView;
+    }
+
+    public void updateInitialPositionForView(TaskView originalView) {
+        View thumbnail = originalView.getThumbnail();
+        Rect viewBounds = new Rect(0, 0, thumbnail.getWidth(), thumbnail.getHeight());
+        Utilities.getBoundsForViewInDragLayer(mLauncher.getDragLayer(), thumbnail, viewBounds,
+                true /* ignoreTransform */, null /* recycle */,
+                mStartingPosition);
+        mStartingPosition.offset(originalView.getTranslationX(), originalView.getTranslationY());
+        final InsettableFrameLayout.LayoutParams lp = new InsettableFrameLayout.LayoutParams(
+                Math.round(mStartingPosition.width()),
+                Math.round(mStartingPosition.height()));
+        initPosition(mStartingPosition, lp);
+        setLayoutParams(lp);
+    }
+
+    // TODO(194414938) set correct corner radii
+    public void update(RectF position, float progress, float windowRadius) {
+        MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
+
+        float dX = mIsRtl
+                ? position.left - (lp.getMarginStart() - lp.width)
+                : position.left - lp.getMarginStart();
+        float dY = position.top - lp.topMargin;
+
+        setTranslationX(dX);
+        setTranslationY(dY);
+
+        float scaleX = position.width() / lp.width;
+        float scaleY = position.height() / lp.height;
+        setScaleX(scaleX);
+        setScaleY(scaleY);
+        float childScaleX = 1f / scaleX;
+        float childScaleY = 1f / scaleY;
+
+        invalidate();
+        // TODO(194414938) seems like this scale value could be fine tuned, some stretchiness
+        mImageView.setScaleX(1f / scaleX + scaleX * progress);
+        mImageView.setScaleY(1f / scaleY + scaleY * progress);
+        mOrientationHandler.setPrimaryScale(mSplitPlaceholderView.getIconView(), childScaleX);
+        mOrientationHandler.setSecondaryScale(mSplitPlaceholderView.getIconView(), childScaleY);
+    }
+
+    protected void initPosition(RectF pos, InsettableFrameLayout.LayoutParams lp) {
+        mStartingPosition.set(pos);
+        lp.ignoreInsets = true;
+        // Position the floating view exactly on top of the original
+        lp.topMargin = Math.round(pos.top);
+        if (mIsRtl) {
+            lp.setMarginStart(Math.round(mLauncher.getDeviceProfile().widthPx - pos.right));
+        } else {
+            lp.setMarginStart(Math.round(pos.left));
+        }
+        // Set the properties here already to make sure they are available when running the first
+        // animation frame.
+        int left = mIsRtl
+                ? mLauncher.getDeviceProfile().widthPx - lp.getMarginStart() - lp.width
+                : lp.leftMargin;
+        layout(left, lp.topMargin, left + lp.width, lp.topMargin + lp.height);
+    }
+
+    public void addAnimation(PendingAnimation animation, RectF startingBounds, Rect endBounds,
+            View viewToCover, boolean fadeWithThumbnail) {
+        final BaseDragLayer dragLayer = mLauncher.getDragLayer();
+        int[] dragLayerBounds = new int[2];
+        dragLayer.getLocationOnScreen(dragLayerBounds);
+        SplitOverlayProperties prop = new SplitOverlayProperties(endBounds,
+                startingBounds, viewToCover, dragLayerBounds[0],
+                dragLayerBounds[1]);
+
+        ValueAnimator transitionAnimator = ValueAnimator.ofFloat(0, 1);
+        animation.add(transitionAnimator);
+        long animDuration = animation.getDuration();
+        Rect crop = new Rect();
+        RectF floatingTaskViewBounds = new RectF();
+        final float initialWindowRadius = supportsRoundedCornersOnWindows(getResources())
+                ? Math.max(crop.width(), crop.height()) / 2f
+                : 0f;
+
+        if (fadeWithThumbnail) {
+            animation.addFloat(mSplitPlaceholderView, SplitPlaceholderView.ALPHA_FLOAT,
+                    0, 1, ACCEL);
+            animation.addFloat(mImageView, LauncherAnimUtils.VIEW_ALPHA,
+                    1, 0, DEACCEL_3);
+        }
+
+        MultiValueUpdateListener listener = new MultiValueUpdateListener() {
+            final FloatProp mWindowRadius = new FloatProp(initialWindowRadius,
+                    initialWindowRadius, 0, animDuration, LINEAR);
+            final FloatProp mDx = new FloatProp(0, prop.dX, 0, animDuration, LINEAR);
+            final FloatProp mDy = new FloatProp(0, prop.dY, 0, animDuration, LINEAR);
+            final FloatProp mTaskViewScaleX = new FloatProp(prop.initialTaskViewScaleX,
+                    prop.finalTaskViewScaleX, 0, animDuration, LINEAR);
+            final FloatProp mTaskViewScaleY = new FloatProp(prop.initialTaskViewScaleY,
+                    prop.finalTaskViewScaleY, 0, animDuration, LINEAR);
+            @Override
+            public void onUpdate(float percent, boolean initOnly) {
+                // Calculate the icon position.
+                floatingTaskViewBounds.set(startingBounds);
+                floatingTaskViewBounds.offset(mDx.value, mDy.value);
+                Utilities.scaleRectFAboutCenter(floatingTaskViewBounds, mTaskViewScaleX.value,
+                        mTaskViewScaleY.value);
+
+                update(floatingTaskViewBounds, percent, mWindowRadius.value * 1);
+            }
+        };
+        transitionAnimator.addUpdateListener(listener);
+    }
+
+    private static class SplitOverlayProperties {
+
+        private final float initialTaskViewScaleX;
+        private final float initialTaskViewScaleY;
+        private final float finalTaskViewScaleX;
+        private final float finalTaskViewScaleY;
+        private final float dX;
+        private final float dY;
+
+        SplitOverlayProperties(Rect endBounds, RectF startTaskViewBounds, View view,
+                int dragLayerLeft, int dragLayerTop) {
+            float maxScaleX = endBounds.width() / startTaskViewBounds.width();
+            float maxScaleY = endBounds.height() / startTaskViewBounds.height();
+
+            initialTaskViewScaleX = view.getScaleX();
+            initialTaskViewScaleY = view.getScaleY();
+            finalTaskViewScaleX = maxScaleX;
+            finalTaskViewScaleY = maxScaleY;
+
+            // Animate the app icon to the center of the window bounds in screen coordinates.
+            float centerX = endBounds.centerX() - dragLayerLeft;
+            float centerY = endBounds.centerY() - dragLayerTop;
+
+            dX = centerX - startTaskViewBounds.centerX();
+            dY = centerY - startTaskViewBounds.centerY();
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
new file mode 100644
index 0000000..72d3731
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -0,0 +1,224 @@
+package com.android.quickstep.views;
+
+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 android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
+import com.android.launcher3.util.TransformingTouchDelegate;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.TaskIconCache;
+import com.android.quickstep.TaskThumbnailCache;
+import com.android.quickstep.util.CancellableTask;
+import com.android.quickstep.util.RecentsOrientedState;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+import java.util.function.Consumer;
+
+/**
+ * TaskView that contains and shows thumbnails for not one, BUT TWO(!!) tasks
+ *
+ * That's right. If you call within the next 5 minutes we'll go ahead and double your order and
+ * send you !! TWO !! Tasks along with their TaskThumbnailViews complimentary. On. The. House.
+ * And not only that, we'll even clean up your thumbnail request if you don't like it.
+ * All the benefits of one TaskView, except DOUBLED!
+ *
+ * (Icon loading sold separately, fees may apply. Shipping & Handling for Overlays not included).
+ */
+public class GroupedTaskView extends TaskView {
+
+    private Task mSecondaryTask;
+    private TaskThumbnailView mSnapshotView2;
+    private IconView mIconView2;
+    private CancellableTask<ThumbnailData> mThumbnailLoadRequest2;
+    private CancellableTask mIconLoadRequest2;
+    private final float[] mIcon2CenterCoords = new float[2];
+    private TransformingTouchDelegate mIcon2TouchDelegate;
+    @Nullable private StagedSplitBounds mSplitBoundsConfig;
+    private final Rect mPrimaryTempRect = new Rect();
+    private final Rect mSecondaryTempRect = new Rect();
+
+    public GroupedTaskView(Context context) {
+        super(context);
+    }
+
+    public GroupedTaskView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public GroupedTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mSnapshotView2 = findViewById(R.id.bottomright_snapshot);
+        mIconView2 = findViewById(R.id.bottomRight_icon);
+        mIcon2TouchDelegate = new TransformingTouchDelegate(mIconView2);
+    }
+
+    public void bind(Task primary, Task secondary, RecentsOrientedState orientedState,
+            StagedSplitBounds splitBoundsConfig) {
+        super.bind(primary, orientedState);
+        mSecondaryTask = secondary;
+        mTaskIdContainer[1] = secondary.key.id;
+        mTaskIdAttributeContainer[1] = new TaskIdAttributeContainer(secondary, mSnapshotView2,
+                STAGE_POSITION_BOTTOM_OR_RIGHT);
+        mTaskIdAttributeContainer[0].setStagePosition(STAGE_POSITION_TOP_OR_LEFT);
+        mSnapshotView2.bind(secondary);
+        mSplitBoundsConfig = splitBoundsConfig;
+    }
+
+    @Override
+    public void onTaskListVisibilityChanged(boolean visible, int changes) {
+        super.onTaskListVisibilityChanged(visible, changes);
+        if (visible) {
+            RecentsModel model = RecentsModel.INSTANCE.get(getContext());
+            TaskThumbnailCache thumbnailCache = model.getThumbnailCache();
+            TaskIconCache iconCache = model.getIconCache();
+
+            if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
+                mThumbnailLoadRequest2 = thumbnailCache.updateThumbnailInBackground(mSecondaryTask,
+                        thumbnailData -> mSnapshotView2.setThumbnail(
+                                mSecondaryTask, thumbnailData
+                        ));
+            }
+
+            if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
+                mIconLoadRequest2 = iconCache.updateIconInBackground(mSecondaryTask,
+                        (task) -> {
+                            setIcon(mIconView2, task.icon);
+                            // TODO(199936292) Digital Wellbeing for individual tasks?
+                        });
+            }
+        } else {
+            if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
+                mSnapshotView2.setThumbnail(null, null);
+                // Reset the task thumbnail reference as well (it will be fetched from the cache or
+                // reloaded next time we need it)
+                mSecondaryTask.thumbnail = null;
+            }
+            if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
+                setIcon(mIconView2, null);
+            }
+        }
+    }
+
+    protected boolean showTaskMenuWithContainer(IconView iconView) {
+        return TaskMenuView.showForTask(mTaskIdAttributeContainer[iconView == mIconView ? 0 : 1]);
+    }
+
+    public void updateSplitBoundsConfig(StagedSplitBounds stagedSplitBounds) {
+        mSplitBoundsConfig = stagedSplitBounds;
+        invalidate();
+    }
+
+    @Override
+    public boolean offerTouchToChildren(MotionEvent event) {
+        computeAndSetIconTouchDelegate(mIconView2, mIcon2CenterCoords, mIcon2TouchDelegate);
+        if (mIcon2TouchDelegate.onTouchEvent(event)) {
+            return true;
+        }
+
+        return super.offerTouchToChildren(event);
+    }
+
+    @Override
+    protected void cancelPendingLoadTasks() {
+        super.cancelPendingLoadTasks();
+        if (mThumbnailLoadRequest2 != null) {
+            mThumbnailLoadRequest2.cancel();
+            mThumbnailLoadRequest2 = null;
+        }
+        if (mIconLoadRequest2 != null) {
+            mIconLoadRequest2.cancel();
+            mIconLoadRequest2 = null;
+        }
+    }
+
+    @Override
+    public RunnableList launchTaskAnimated() {
+        getRecentsView().getSplitPlaceholder().launchTasks(mTask, mSecondaryTask,
+                STAGE_POSITION_TOP_OR_LEFT, null /*callback*/);
+        return null;
+    }
+
+    @Override
+    public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
+        getRecentsView().getSplitPlaceholder().launchTasks(mTask, mSecondaryTask,
+                STAGE_POSITION_TOP_OR_LEFT, callback);
+    }
+
+    @Override
+    public TaskThumbnailView[] getThumbnails() {
+        return new TaskThumbnailView[]{mSnapshotView, mSnapshotView2};
+    }
+
+    @Override
+    public void onRecycle() {
+        super.onRecycle();
+        mSnapshotView2.setThumbnail(mSecondaryTask, null);
+        mSplitBoundsConfig = null;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+        setMeasuredDimension(widthSize, heightSize);
+        if (mSplitBoundsConfig == null || mSnapshotView == null || mSnapshotView2 == null) {
+            return;
+        }
+        getPagedOrientationHandler().measureGroupedTaskViewThumbnailBounds(mSnapshotView,
+                mSnapshotView2, widthSize, heightSize, mSplitBoundsConfig,
+                mActivity.getDeviceProfile());
+        updateIconPlacement();
+    }
+
+    @Override
+    public void setOverlayEnabled(boolean overlayEnabled) {
+        super.setOverlayEnabled(overlayEnabled);
+        mSnapshotView2.setOverlayEnabled(overlayEnabled);
+    }
+
+    @Override
+    public void setOrientationState(RecentsOrientedState orientationState) {
+        super.setOrientationState(orientationState);
+        DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+        boolean isGridTask = deviceProfile.overviewShowAsGrid && !isFocusedTask();
+        int iconDrawableSize = isGridTask ? deviceProfile.overviewTaskIconDrawableSizeGridPx
+                : deviceProfile.overviewTaskIconDrawableSizePx;
+        mIconView2.setDrawableSize(iconDrawableSize, iconDrawableSize);
+        mIconView2.setRotation(getPagedOrientationHandler().getDegreesRotated());
+        updateIconPlacement();
+    }
+
+    private void updateIconPlacement() {
+        if (mSplitBoundsConfig == null) {
+            return;
+        }
+
+        DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+        int taskIconHeight = deviceProfile.overviewTaskIconSizePx;
+        boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+
+        mSnapshotView.getBoundsOnScreen(mPrimaryTempRect);
+        mSnapshotView2.getBoundsOnScreen(mSecondaryTempRect);
+        getPagedOrientationHandler().setSplitIconParams(mIconView, mIconView2,
+                taskIconHeight, mPrimaryTempRect, mSecondaryTempRect,
+                isRtl, deviceProfile, mSplitBoundsConfig);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/IconView.java b/quickstep/src/com/android/quickstep/views/IconView.java
index 5b0ade0..813e653 100644
--- a/quickstep/src/com/android/quickstep/views/IconView.java
+++ b/quickstep/src/com/android/quickstep/views/IconView.java
@@ -17,8 +17,10 @@
 
 import android.content.Context;
 import android.graphics.Canvas;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.view.Gravity;
 import android.view.View;
 
 import com.android.launcher3.Utilities;
@@ -30,6 +32,7 @@
 public class IconView extends View {
 
     private Drawable mDrawable;
+    private int mDrawableWidth, mDrawableHeight;
 
     public IconView(Context context) {
         super(context);
@@ -50,11 +53,29 @@
         mDrawable = d;
         if (mDrawable != null) {
             mDrawable.setCallback(this);
-            mDrawable.setBounds(0, 0, getWidth(), getHeight());
+            setDrawableSizeInternal(getWidth(), getHeight());
         }
         invalidate();
     }
 
+    /**
+     * Sets the size of the icon drawable.
+     */
+    public void setDrawableSize(int iconWidth, int iconHeight) {
+        mDrawableWidth = iconWidth;
+        mDrawableHeight = iconHeight;
+        if (mDrawable != null) {
+            setDrawableSizeInternal(getWidth(), getHeight());
+        }
+    }
+
+    private void setDrawableSizeInternal(int selfWidth, int selfHeight) {
+        Rect selfRect = new Rect(0, 0, selfWidth, selfHeight);
+        Rect drawableRect = new Rect();
+        Gravity.apply(Gravity.CENTER, mDrawableWidth, mDrawableHeight, selfRect, drawableRect);
+        mDrawable.setBounds(drawableRect);
+    }
+
     public Drawable getDrawable() {
         return mDrawable;
     }
@@ -63,7 +84,7 @@
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         super.onSizeChanged(w, h, oldw, oldh);
         if (mDrawable != null) {
-            mDrawable.setBounds(0, 0, w, h);
+            setDrawableSizeInternal(w, h);
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 65956d5..5ca5c94 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -29,17 +29,14 @@
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.Surface;
-import android.widget.FrameLayout;
 
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.StateManager.StateListener;
-import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.quickstep.LauncherActivityInterface;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.RecentsExtraCard;
+import com.android.quickstep.util.SplitSelectStateController;
 
 /**
  * {@link RecentsView} used in Launcher activity
@@ -48,25 +45,6 @@
 public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher, LauncherState>
         implements StateListener<LauncherState> {
 
-    private RecentsExtraCard mRecentsExtraCardPlugin;
-    private RecentsExtraViewContainer mRecentsExtraViewContainer;
-    private PluginListener<RecentsExtraCard> mRecentsExtraCardPluginListener =
-            new PluginListener<RecentsExtraCard>() {
-        @Override
-        public void onPluginConnected(RecentsExtraCard recentsExtraCard, Context context) {
-            createRecentsExtraCard();
-            mRecentsExtraCardPlugin = recentsExtraCard;
-            mRecentsExtraCardPlugin.setupView(context, mRecentsExtraViewContainer, mActivity);
-        }
-
-        @Override
-        public void onPluginDisconnected(RecentsExtraCard plugin) {
-            removeView(mRecentsExtraViewContainer);
-            mRecentsExtraCardPlugin = null;
-            mRecentsExtraViewContainer = null;
-        }
-    };
-
     public LauncherRecentsView(Context context) {
         this(context, null);
     }
@@ -81,7 +59,8 @@
     }
 
     @Override
-    public void init(OverviewActionsView actionsView, SplitPlaceholderView splitPlaceholderView) {
+    public void init(OverviewActionsView actionsView,
+            SplitSelectStateController splitPlaceholderView) {
         super.init(actionsView, splitPlaceholderView);
         setContentAlpha(0);
     }
@@ -146,73 +125,6 @@
     }
 
     @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        PluginManagerWrapper.INSTANCE.get(getContext()).addPluginListener(
-                mRecentsExtraCardPluginListener, RecentsExtraCard.class);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        PluginManagerWrapper.INSTANCE.get(getContext()).removePluginListener(
-                mRecentsExtraCardPluginListener);
-    }
-
-    @Override
-    protected int computeMinScroll() {
-        if (canComputeScrollX() && !mIsRtl) {
-            return computeScrollX();
-        }
-        return super.computeMinScroll();
-    }
-
-    @Override
-    protected int computeMaxScroll() {
-        if (canComputeScrollX() && mIsRtl) {
-            return computeScrollX();
-        }
-        return super.computeMaxScroll();
-    }
-
-    private boolean canComputeScrollX() {
-        return mRecentsExtraCardPlugin != null && getTaskViewCount() > 0
-                && !mDisallowScrollToClearAll;
-    }
-
-    private int computeScrollX() {
-        int scrollIndex = getTaskViewStartIndex() - 1;
-        while (scrollIndex >= 0 && getChildAt(scrollIndex) instanceof RecentsExtraViewContainer
-                && ((RecentsExtraViewContainer) getChildAt(scrollIndex)).isScrollable()) {
-            scrollIndex--;
-        }
-        return getScrollForPage(scrollIndex + 1);
-    }
-
-    private void createRecentsExtraCard() {
-        mRecentsExtraViewContainer = new RecentsExtraViewContainer(getContext());
-        FrameLayout.LayoutParams helpCardParams =
-                new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
-                        FrameLayout.LayoutParams.MATCH_PARENT);
-        mRecentsExtraViewContainer.setLayoutParams(helpCardParams);
-        mRecentsExtraViewContainer.setScrollable(true);
-        addView(mRecentsExtraViewContainer, 0);
-    }
-
-    @Override
-    public boolean hasRecentsExtraCard() {
-        return mRecentsExtraViewContainer != null;
-    }
-
-    @Override
-    public void setContentAlpha(float alpha) {
-        super.setContentAlpha(alpha);
-        if (mRecentsExtraViewContainer != null) {
-            mRecentsExtraViewContainer.setAlpha(alpha);
-        }
-    }
-
-    @Override
     protected DepthController getDepthController() {
         return mActivity.getDepthController();
     }
@@ -225,6 +137,7 @@
         } else {
             if (mActivity.isInState(LauncherState.OVERVIEW_MODAL_TASK)) {
                 mActivity.getStateManager().goToState(LauncherState.OVERVIEW);
+                resetModalVisuals();
             }
         }
     }
@@ -242,8 +155,8 @@
 
     @Override
     public void initiateSplitSelect(TaskView taskView,
-            SplitConfigurationOptions.SplitPositionOption splitPositionOption) {
-        super.initiateSplitSelect(taskView, splitPositionOption);
+            @SplitConfigurationOptions.StagePosition int stagePosition) {
+        super.initiateSplitSelect(taskView, stagePosition);
         mActivity.getStateManager().goToState(LauncherState.OVERVIEW_SPLIT_SELECT);
     }
 
@@ -252,9 +165,6 @@
         super.onConfigurationChanged(newConfig);
         // If overview is in modal state when rotate, reset it to overview state without running
         // animation.
-        if (mActivity.isInState(OVERVIEW_MODAL_TASK)) {
-            mActivity.getStateManager().goToState(LauncherState.OVERVIEW, false);
-            resetModalVisuals();
-        }
+        setModalStateEnabled(false);
     }
 }
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 8c115e5..76d3591 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -16,9 +16,6 @@
 
 package com.android.quickstep.views;
 
-import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_SHARE;
-
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -33,7 +30,6 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.SysUINavigationMode;
@@ -55,13 +51,17 @@
     @IntDef(flag = true, value = {
             HIDDEN_NON_ZERO_ROTATION,
             HIDDEN_NO_TASKS,
-            HIDDEN_NO_RECENTS})
+            HIDDEN_NO_RECENTS,
+            HIDDEN_FOCUSED_SCROLL,
+            HIDDEN_SPLIT_SCREEN})
     @Retention(RetentionPolicy.SOURCE)
     public @interface ActionsHiddenFlags { }
 
     public static final int HIDDEN_NON_ZERO_ROTATION = 1 << 0;
     public static final int HIDDEN_NO_TASKS = 1 << 1;
     public static final int HIDDEN_NO_RECENTS = 1 << 2;
+    public static final int HIDDEN_FOCUSED_SCROLL = 1 << 3;
+    public static final int HIDDEN_SPLIT_SCREEN = 1 << 4;
 
     @IntDef(flag = true, value = {
             DISABLED_SCROLLING,
@@ -78,9 +78,9 @@
     private static final int INDEX_VISIBILITY_ALPHA = 1;
     private static final int INDEX_FULLSCREEN_ALPHA = 2;
     private static final int INDEX_HIDDEN_FLAGS_ALPHA = 3;
-    private static final int INDEX_SCROLL_ALPHA = 4;
 
     private final MultiValueAlpha mMultiValueAlpha;
+    private View mSplitButton;
 
     @ActionsHiddenFlags
     private int mHiddenFlags;
@@ -90,9 +90,6 @@
 
     protected T mCallbacks;
 
-    private float mModalness;
-    private float mModalTransformY;
-
     protected DeviceProfile mDp;
 
     public OverviewActionsView(Context context) {
@@ -112,13 +109,9 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        View share = findViewById(R.id.action_share);
-        share.setOnClickListener(this);
         findViewById(R.id.action_screenshot).setOnClickListener(this);
-        if (ENABLE_OVERVIEW_SHARE.get()) {
-            share.setVisibility(VISIBLE);
-            findViewById(R.id.oav_three_button_space).setVisibility(VISIBLE);
-        }
+        mSplitButton = findViewById(R.id.action_split);
+        mSplitButton.setOnClickListener(this);
     }
 
     /**
@@ -136,10 +129,10 @@
             return;
         }
         int id = view.getId();
-        if (id == R.id.action_share) {
-            mCallbacks.onShare();
-        } else if (id == R.id.action_screenshot) {
+        if (id == R.id.action_screenshot) {
             mCallbacks.onScreenshot();
+        } else if (id == R.id.action_split) {
+            mCallbacks.onSplit();
         }
     }
 
@@ -180,7 +173,6 @@
         } else {
             mDisabledFlags &= ~disabledFlags;
         }
-        //
         boolean isEnabled = (mDisabledFlags & ~DISABLED_ROTATED) == 0;
         LayoutUtils.setViewEnabled(this, isEnabled);
     }
@@ -197,10 +189,6 @@
         return mMultiValueAlpha.getProperty(INDEX_FULLSCREEN_ALPHA);
     }
 
-    public AlphaProperty getScrollAlpha() {
-        return mMultiValueAlpha.getProperty(INDEX_SCROLL_ALPHA);
-    }
-
     private void updateHorizontalPadding() {
         setPadding(mInsets.left, 0, mInsets.right, 0);
     }
@@ -226,27 +214,13 @@
         requestLayout();
     }
 
-    /**
-     * The current task is fully modal (modalness = 1) when it is shown on its own in a modal
-     * way. Modalness 0 means the task is shown in context with all the other tasks.
-     */
-    public void setTaskModalness(float modalness) {
-        mModalness = modalness;
-        applyTranslationY();
-    }
+    public void setSplitButtonVisible(boolean visible) {
+        if (mSplitButton == null) {
+            return;
+        }
 
-    public void setModalTransformY(float modalTransformY) {
-        mModalTransformY = modalTransformY;
-        applyTranslationY();
-    }
-
-    private void applyTranslationY() {
-        setTranslationY(getModalTrans(mModalTransformY));
-    }
-
-    private float getModalTrans(float endTranslation) {
-        float progress = ACCEL_DEACCEL.getInterpolation(mModalness);
-        return Utilities.mapRange(progress, 0, endTranslation);
+        mSplitButton.setVisibility(visible ? VISIBLE : GONE);
+        findViewById(R.id.action_split_space).setVisibility(visible ? VISIBLE : GONE);
     }
 
     /** Get the top margin associated with the action buttons in Overview. */
@@ -261,7 +235,7 @@
             return dp.overviewActionsMarginThreeButtonPx;
         }
 
-        return dp.overviewActionsMarginGesturePx;
+        return dp.overviewActionsTopMarginGesturePx;
     }
 
     /** Get the bottom margin associated with the action buttons in Overview. */
@@ -277,6 +251,6 @@
             return dp.overviewActionsMarginThreeButtonPx + inset;
         }
 
-        return dp.overviewActionsMarginGesturePx + inset;
+        return dp.overviewActionsBottomMarginGesturePx + inset;
     }
 }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsExtraViewContainer.java b/quickstep/src/com/android/quickstep/views/RecentsExtraViewContainer.java
deleted file mode 100644
index 16bc3bc..0000000
--- a/quickstep/src/com/android/quickstep/views/RecentsExtraViewContainer.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.views;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.FrameLayout;
-
-/**
- * Empty view to house recents overview extra card
- */
-public class RecentsExtraViewContainer extends FrameLayout {
-
-    private boolean mScrollable = false;
-
-    public RecentsExtraViewContainer(Context context) {
-        super(context);
-    }
-
-    public RecentsExtraViewContainer(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public RecentsExtraViewContainer(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    /**
-     * Determine whether the view should be scrolled to in the recents overview, similar to the
-     * taskviews.
-     * @return true if viewed should be scrolled to, false if not
-     */
-    public boolean isScrollable() {
-        return mScrollable;
-    }
-
-    public void setScrollable(boolean scrollable) {
-        this.mScrollable = scrollable;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 070714a..f7a9562 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -32,10 +32,10 @@
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.Utilities.squaredTouchSlop;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.launcher3.anim.Interpolators.ACCEL_0_5;
 import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
 import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
@@ -43,14 +43,17 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
+import static com.android.launcher3.testing.TestProtocol.TASK_VIEW_ID_CRASH;
 import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
 import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
+import static com.android.quickstep.views.ClearAllButton.DISMISS_ALPHA;
 import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NON_ZERO_ROTATION;
 import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_RECENTS;
 import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_TASKS;
+import static com.android.quickstep.views.OverviewActionsView.HIDDEN_SPLIT_SCREEN;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -77,14 +80,16 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.SystemClock;
 import android.os.UserHandle;
+import android.os.VibrationEffect;
 import android.text.Layout;
 import android.text.StaticLayout;
 import android.text.TextPaint;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
+import android.util.Log;
 import android.util.SparseBooleanArray;
-import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -96,9 +101,9 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.Interpolator;
-import android.widget.FrameLayout;
 import android.widget.ListView;
 import android.widget.OverScroller;
+import android.widget.Toast;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
@@ -112,7 +117,6 @@
 import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
@@ -126,12 +130,13 @@
 import com.android.launcher3.touch.OverScroll;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.DynamicResource;
+import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.ResourceBasedOverride.Overrides;
-import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.launcher3.util.RunnableList;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.TranslateEdgeEffect;
 import com.android.launcher3.util.ViewPool;
@@ -143,11 +148,14 @@
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.RecentsModel.TaskVisualsChangeListener;
 import com.android.quickstep.RemoteAnimationTargets;
+import com.android.quickstep.RemoteTargetGluer;
+import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskOverlayFactory;
 import com.android.quickstep.TaskThumbnailCache;
 import com.android.quickstep.TaskViewUtils;
 import com.android.quickstep.ViewUtils;
+import com.android.quickstep.util.LauncherSplitScreenListener;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.util.SplitScreenBounds;
@@ -155,6 +163,7 @@
 import com.android.quickstep.util.SurfaceTransactionApplier;
 import com.android.quickstep.util.TaskViewSimulator;
 import com.android.quickstep.util.TransformParams;
+import com.android.quickstep.util.VibratorWrapper;
 import com.android.systemui.plugins.ResourceProvider;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.Task.TaskKey;
@@ -180,6 +189,9 @@
         TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
         TaskVisualsChangeListener, SplitScreenBounds.OnChangeListener {
 
+    private static final String TAG = "RecentsView";
+    private static final boolean DEBUG = false;
+
     // TODO(b/184899234): We use this timeout to wait a fixed period after switching to the
     // screenshot when dismissing the current live task to ensure the app can try and get stopped.
     private static final int REMOVE_TASK_WAIT_FOR_APP_STOP_MS = 100;
@@ -239,6 +251,12 @@
                 }
             };
 
+    public static final int SCROLL_VIBRATION_PRIMITIVE =
+            Utilities.ATLEAST_S ? VibrationEffect.Composition.PRIMITIVE_LOW_TICK : -1;
+    public static final float SCROLL_VIBRATION_PRIMITIVE_SCALE = 0.6f;
+    public static final VibrationEffect SCROLL_VIBRATION_FALLBACK =
+            VibratorWrapper.EFFECT_TEXTURE_TICK;
+
     /**
      * Can be used to tint the color of the RecentsView to simulate a scrim that can views
      * excluded from. Really should be a proper scrim.
@@ -317,8 +335,15 @@
                     view.setScaleY(scale);
                     view.mLastComputedTaskStartPushOutDistance = null;
                     view.mLastComputedTaskEndPushOutDistance = null;
-                    view.mLiveTileTaskViewSimulator.recentsViewScale.value = scale;
+                    view.runActionOnRemoteHandles(new Consumer<RemoteTargetHandle>() {
+                        @Override
+                        public void accept(RemoteTargetHandle remoteTargetHandle) {
+                            remoteTargetHandle.getTaskViewSimulator().recentsViewScale.value =
+                                    scale;
+                        }
+                    });
                     view.setTaskViewsResistanceTranslation(view.mTaskViewsSecondaryTranslation);
+                    view.updateTaskViewsSnapshotRadius();
                     view.updatePageOffsets();
                 }
 
@@ -348,6 +373,10 @@
     private static final int ADDITION_TASK_DURATION = 200;
     private static final float INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET = 0.55f;
     private static final float ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET = 0.05f;
+    private static final float ANIMATION_DISMISS_PROGRESS_MIDPOINT = 0.5f;
+    private static final float END_DISMISS_TRANSLATION_INTERPOLATION_OFFSET = 0.75f;
+
+    private static final float SIGNIFICANT_MOVE_THRESHOLD_TABLET = 0.15f;
 
     protected final RecentsOrientedState mOrientationState;
     protected final BaseActivityInterface<STATE_TYPE, ACTIVITY_TYPE> mSizeStrategy;
@@ -355,8 +384,14 @@
     protected SurfaceTransactionApplier mSyncTransactionApplier;
     protected int mTaskWidth;
     protected int mTaskHeight;
-    protected final TransformParams mLiveTileParams = new TransformParams();
-    protected final TaskViewSimulator mLiveTileTaskViewSimulator;
+    // Used to position the top of a task in the top row of the grid
+    private float mTaskGridVerticalDiff;
+    // The vertical space one grid task takes + space between top and bottom row.
+    private float mTopBottomRowHeightDiff;
+    // mTaskGridVerticalDiff and mTopBottomRowHeightDiff summed together provides the top
+    // position for bottom row of grid tasks.
+
+    protected RemoteTargetHandle[] mRemoteTargetHandles;
     protected final Rect mLastComputedTaskSize = new Rect();
     protected final Rect mLastComputedGridSize = new Rect();
     protected final Rect mLastComputedGridTaskSize = new Rect();
@@ -367,6 +402,7 @@
     protected final Rect mTempRect = new Rect();
     protected final RectF mTempRectF = new RectF();
     private final PointF mTempPointF = new PointF();
+    private final Matrix mTempMatrix = new Matrix();
     private final float[] mTempFloat = new float[1];
     private final List<OnScrollChangedListener> mScrollListeners = new ArrayList<>();
 
@@ -375,9 +411,9 @@
 
     protected final ACTIVITY_TYPE mActivity;
     private final float mFastFlingVelocity;
+    private final int mScrollHapticMinGapMillis;
     private final RecentsModel mModel;
-    private final int mRowSpacing;
-    private final int mGridSideMargin;
+    private final int mSplitPlaceholderSize;
     private final ClearAllButton mClearAllButton;
     private final Rect mClearAllButtonDeadZoneRect = new Rect();
     private final Rect mTaskViewDeadZoneRect = new Rect();
@@ -391,7 +427,11 @@
 
     private final InvariantDeviceProfile mIdp;
 
+    /**
+     * Getting views should be done via {@link #getTaskViewFromPool(boolean)}
+     */
     private final ViewPool<TaskView> mTaskViewPool;
+    private final ViewPool<GroupedTaskView> mGroupedTaskViewPool;
 
     private final TaskOverlayFactory mTaskOverlayFactory;
 
@@ -407,6 +447,7 @@
     protected float mTaskViewsSecondarySplitTranslation = 0;
     // Progress from 0 to 1 where 0 is a carousel and 1 is a 2 row grid.
     private float mGridProgress = 0;
+    private boolean mShowAsGridLastOnLayout = false;
     private final IntSet mTopRowIdSet = new IntSet();
 
     // The GestureEndTarget that is still in progress.
@@ -418,6 +459,7 @@
     private ObjectAnimator mTintingAnimator;
 
     private int mOverScrollShift = 0;
+    private long mScrollLastHapticTimestamp;
 
     /**
      * TODO: Call reloadIdNeeded in onTaskStackChanged.
@@ -434,7 +476,7 @@
             }
 
             // Remove the task immediately from the task list
-            TaskView taskView = getTaskView(taskId);
+            TaskView taskView = getTaskViewByTaskId(taskId);
             if (taskView != null) {
                 removeView(taskView);
             }
@@ -456,7 +498,7 @@
                 return;
             }
 
-            TaskView taskView = getTaskView(taskId);
+            TaskView taskView = getTaskViewByTaskId(taskId);
             if (taskView == null) {
                 return;
             }
@@ -489,13 +531,18 @@
     private int mTaskListChangeId = -1;
 
     // Only valid until the launcher state changes to NORMAL
-    protected int mRunningTaskId = -1;
+    /**
+     * ID for the current running TaskView view, unique amongst TaskView instances. ID's are set
+     * through {@link #getTaskViewFromPool(boolean)} and incremented by {@link #mTaskViewIdCount}
+     */
+    protected int mRunningTaskViewId = -1;
+    private int mTaskViewIdCount;
+    private final int[] INVALID_TASK_IDS = new int[]{-1, -1};
     protected boolean mRunningTaskTileHidden;
-    private Task mTmpRunningTask;
-    protected int mFocusedTaskId = -1;
-    private float mFocusedTaskRatio;
+    private Task[] mTmpRunningTasks;
+    protected int mFocusedTaskViewId = -1;
 
-    private boolean mRunningTaskIconScaledDown = false;
+    private boolean mTaskIconScaledDown = false;
     private boolean mRunningTaskShowScreenshot = false;
 
     private boolean mOverviewStateEnabled;
@@ -536,15 +583,21 @@
     /**
      * Placeholder view indicating where the first split screen selected app will be placed
      */
-    private SplitPlaceholderView mSplitPlaceholderView;
+    private SplitSelectStateController mSplitSelectStateController;
     /**
      * The first task that split screen selection was initiated with. When split select state is
      * initialized, we create a
-     * {@link #createTaskDismissAnimation(TaskView, boolean, boolean, long)} for this TaskView but
-     * don't actually remove the task since the user might back out. As such, we also ensure this
-     * View doesn't go back into the {@link #mTaskViewPool}, see {@link #onViewRemoved(View)}
+     * {@link #createTaskDismissAnimation(TaskView, boolean, boolean, long, boolean)} for this
+     * TaskView but don't actually remove the task since the user might back out. As such, we also
+     * ensure this View doesn't go back into the {@link #mTaskViewPool},
+     * see {@link #onViewRemoved(View)}
      */
     private TaskView mSplitHiddenTaskView;
+    private TaskView mSecondSplitHiddenTaskView;
+    private SplitConfigurationOptions.StagedSplitBounds mSplitBoundsConfig;
+    private final Toast mSplitToast = Toast.makeText(getContext(),
+            R.string.toast_split_select_app, Toast.LENGTH_SHORT);
+
     /**
      * Keeps track of the index of the TaskView that split screen was initialized with so we know
      * where to insert it back into list of taskViews in case user backs out of entering split
@@ -554,9 +607,14 @@
      * removed from recentsView
      */
     private int mSplitHiddenTaskViewIndex;
+    private FloatingTaskView mFirstFloatingTaskView;
+    private FloatingTaskView mSecondFloatingTaskView;
 
-    // Keeps track of the index where the first TaskView should be
-    private int mTaskViewStartIndex = 0;
+    /**
+     * The task to be removed and immediately re-added. Should not be added to task pool.
+     */
+    private TaskView mMovingTaskView;
+
     private OverviewActionsView mActionsView;
 
     private MultiWindowModeChangedListener mMultiWindowModeChangedListener =
@@ -575,11 +633,11 @@
             };
 
     private RunnableList mSideTaskLaunchCallback;
+    private TaskLaunchListener mTaskLaunchListener;
 
     public RecentsView(Context context, AttributeSet attrs, int defStyleAttr,
             BaseActivityInterface sizeStrategy) {
         super(context, attrs, defStyleAttr);
-        setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
         setEnableFreeScroll(true);
         mSizeStrategy = sizeStrategy;
         mActivity = BaseActivity.fromContext(context);
@@ -588,6 +646,8 @@
         final int rotation = mActivity.getDisplay().getRotation();
         mOrientationState.setRecentsRotation(rotation);
 
+        mScrollHapticMinGapMillis = getResources()
+                .getInteger(R.integer.recentsScrollHapticMinGapMillis);
         mFastFlingVelocity = getResources()
                 .getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
         mModel = RecentsModel.INSTANCE.get(context);
@@ -598,11 +658,14 @@
         mClearAllButton.setOnClickListener(this::dismissAllTasks);
         mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */,
                 10 /* initial size */);
+        // There's only one pair of grouped tasks we can envision at the moment
+        mGroupedTaskViewPool = new ViewPool<>(context, this,
+                R.layout.task_grouped, 2 /* max size */, 1 /* initial size */);
 
         mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
         setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
-        mRowSpacing = getResources().getDimensionPixelSize(R.dimen.overview_grid_row_spacing);
-        mGridSideMargin = getResources().getDimensionPixelSize(R.dimen.overview_grid_side_margin);
+        mSplitPlaceholderSize = getResources().getDimensionPixelSize(
+                R.dimen.split_placeholder_size);
         mSquaredTouchSlop = squaredTouchSlop(context);
 
         mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents);
@@ -629,12 +692,6 @@
         // Initialize quickstep specific cache params here, as this is constructed only once
         mActivity.getViewCache().setCacheSize(R.layout.digital_wellbeing_toast, 5);
 
-        mLiveTileTaskViewSimulator = new TaskViewSimulator(getContext(), getSizeStrategy(),
-                true /* isForLiveTile */);
-        mLiveTileTaskViewSimulator.recentsViewScale.value = 1;
-        mLiveTileTaskViewSimulator.setOrientationState(mOrientationState);
-        mLiveTileTaskViewSimulator.setDrawsBelowRecents(true);
-
         mTintingColor = getForegroundScrimDimColor(context);
     }
 
@@ -682,7 +739,7 @@
             super.dispatchDraw(canvas);
         }
         if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
-                && mLiveTileParams.getTargetSet() != null) {
+                && mRemoteTargetHandles != null) {
             redrawLiveTile();
         }
     }
@@ -722,11 +779,15 @@
     @Override
     public Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {
         if (mHandleTaskStackChanges) {
-            TaskView taskView = getTaskView(taskId);
+            TaskView taskView = getTaskViewByTaskId(taskId);
             if (taskView != null) {
-                Task task = taskView.getTask();
-                taskView.getThumbnail().setThumbnail(task, thumbnailData);
-                return task;
+                for (TaskView.TaskIdAttributeContainer container :
+                        taskView.getTaskIdAttributeContainers()) {
+                    if (container == null || taskId != container.getTask().key.id) {
+                        continue;
+                    }
+                    container.getThumbnailView().setThumbnail(container.getTask(), thumbnailData);
+                }
             }
         }
         return null;
@@ -752,7 +813,7 @@
      * @param refreshNow Refresh immediately if it's true.
      */
     public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData, boolean refreshNow) {
-        TaskView taskView = getTaskView(taskId);
+        TaskView taskView = getTaskViewByTaskId(taskId);
         if (taskView != null) {
             taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData, refreshNow);
         }
@@ -765,18 +826,18 @@
         updateTaskStackListenerState();
     }
 
-    public void init(OverviewActionsView actionsView, SplitPlaceholderView splitPlaceholderView) {
+    public void init(OverviewActionsView actionsView, SplitSelectStateController splitController) {
         mActionsView = actionsView;
         mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
-        mSplitPlaceholderView = splitPlaceholderView;
+        mSplitSelectStateController = splitController;
     }
 
-    public SplitPlaceholderView getSplitPlaceholder() {
-        return mSplitPlaceholderView;
+    public SplitSelectStateController getSplitPlaceholder() {
+        return mSplitSelectStateController;
     }
 
     public boolean isSplitSelectionActive() {
-        return mSplitPlaceholderView.getSplitController().isSplitSelectActive();
+        return mSplitSelectStateController.isSplitSelectActive();
     }
 
     @Override
@@ -787,7 +848,8 @@
         mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
         TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
         mSyncTransactionApplier = new SurfaceTransactionApplier(this);
-        mLiveTileParams.setSyncTransactionApplier(mSyncTransactionApplier);
+        runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
+                .setSyncTransactionApplier(mSyncTransactionApplier));
         RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
         mIPipAnimationListener.setActivityAndRecentsView(mActivity, this);
         SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(
@@ -805,7 +867,8 @@
         mActivity.removeMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
         TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
         mSyncTransactionApplier = null;
-        mLiveTileParams.setSyncTransactionApplier(null);
+        runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
+                .setSyncTransactionApplier(null));
         executeSideTaskLaunchCallback();
         RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
         SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null);
@@ -819,15 +882,24 @@
     public void onViewRemoved(View child) {
         super.onViewRemoved(child);
 
-        // Clear the task data for the removed child if it was visible unless it's the initial
-        // taskview for entering split screen, we only pretend to dismiss the task
-        if (child instanceof TaskView && child != mSplitHiddenTaskView) {
+        // Clear the task data for the removed child if it was visible unless:
+        // - It's the initial taskview for entering split screen, we only pretend to dismiss the
+        // task
+        // - It's the focused task to be moved to the front, we immediately re-add the task
+        if (child instanceof TaskView && child != mSplitHiddenTaskView
+                && child != mMovingTaskView) {
             TaskView taskView = (TaskView) child;
-            mHasVisibleTaskData.delete(taskView.getTask().key.id);
-            mTaskViewPool.recycle(taskView);
+            for (int i : taskView.getTaskIds()) {
+                mHasVisibleTaskData.delete(i);
+            }
+            if (child instanceof GroupedTaskView) {
+                mGroupedTaskViewPool.recycle((GroupedTaskView)taskView);
+            } else {
+                mTaskViewPool.recycle(taskView);
+            }
+            taskView.setTaskViewId(-1);
             mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
         }
-        updateTaskStartIndex(child);
     }
 
     @Override
@@ -837,7 +909,6 @@
         // RecentsView is set to RTL in the constructor when system is using LTR. Here we set the
         // child direction back to match system settings.
         child.setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_LTR : View.LAYOUT_DIRECTION_RTL);
-        updateTaskStartIndex(child);
         mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, false);
         updateEmptyMessage();
     }
@@ -855,6 +926,21 @@
         mSideTaskLaunchCallback.add(callback::executeAllAndDestroy);
     }
 
+    /**
+     * This is a one-time callback when touching in live tile mode. It's reset to null right
+     * after it's called.
+     */
+    public void setTaskLaunchListener(TaskLaunchListener taskLaunchListener) {
+        mTaskLaunchListener = taskLaunchListener;
+    }
+
+    public void onTaskLaunchedInLiveTileMode() {
+        if (mTaskLaunchListener != null) {
+            mTaskLaunchListener.onTaskLaunched();
+            mTaskLaunchListener = null;
+        }
+    }
+
     private void executeSideTaskLaunchCallback() {
         if (mSideTaskLaunchCallback != null) {
             mSideTaskLaunchCallback.executeAllAndDestroy();
@@ -862,9 +948,15 @@
         }
     }
 
+    /**
+     * TODO(b/195675206) Check both taskIDs from runningTaskViewId
+     *  and launch if either of them is {@param taskId}
+     */
     public void launchSideTaskInLiveTileModeForRestartedApp(int taskId) {
-        if (mRunningTaskId != -1 && mRunningTaskId == taskId) {
-            RemoteAnimationTargets targets = getLiveTileParams().getTargetSet();
+        int runningTaskViewId = getTaskViewIdFromTaskId(taskId);
+        if (mRunningTaskViewId != -1 && mRunningTaskViewId == runningTaskViewId) {
+            TransformParams params = mRemoteTargetHandles[0].getTransformParams();
+            RemoteAnimationTargets targets = params.getTargetSet();
             if (targets != null && targets.findTask(taskId) != null) {
                 launchSideTaskInLiveTileMode(taskId, targets.apps, targets.wallpapers,
                         targets.nonApps);
@@ -875,7 +967,7 @@
     public void launchSideTaskInLiveTileMode(int taskId, RemoteAnimationTargetCompat[] apps,
             RemoteAnimationTargetCompat[] wallpaper, RemoteAnimationTargetCompat[] nonApps) {
         AnimatorSet anim = new AnimatorSet();
-        TaskView taskView = getTaskView(taskId);
+        TaskView taskView = getTaskViewByTaskId(taskId);
         if (taskView == null || !isTaskViewVisible(taskView)) {
             // TODO: Refine this animation.
             SurfaceTransactionApplier surfaceApplier =
@@ -909,18 +1001,6 @@
         anim.start();
     }
 
-    private void updateTaskStartIndex(View affectingView) {
-        if (!(affectingView instanceof TaskView) && !(affectingView instanceof ClearAllButton)) {
-            int childCount = getChildCount();
-
-            mTaskViewStartIndex = 0;
-            while (mTaskViewStartIndex < childCount
-                    && !(getChildAt(mTaskViewStartIndex) instanceof TaskView)) {
-                mTaskViewStartIndex++;
-            }
-        }
-    }
-
     public boolean isTaskViewVisible(TaskView tv) {
         if (showAsGrid()) {
             int screenStart = mOrientationHandler.getPrimaryScroll(this);
@@ -932,6 +1012,43 @@
         }
     }
 
+    private boolean isLastGridTaskVisible() {
+        TaskView lastTaskView = getLastGridTaskView();
+        return lastTaskView != null && lastTaskView.isVisibleToUser();
+    }
+
+    private TaskView getLastGridTaskView() {
+        IntArray topRowIdArray = getTopRowIdArray();
+        IntArray bottomRowIdArray = getBottomRowIdArray();
+        if (topRowIdArray.isEmpty() && bottomRowIdArray.isEmpty()) {
+            return null;
+        }
+        int lastTaskViewId = topRowIdArray.size() >= bottomRowIdArray.size() ? topRowIdArray.get(
+                topRowIdArray.size() - 1) : bottomRowIdArray.get(bottomRowIdArray.size() - 1);
+        return getTaskViewFromTaskViewId(lastTaskViewId);
+    }
+
+    private int getSnapToLastTaskScrollDiff() {
+        // Snap to a position where ClearAll is just invisible.
+        int screenStart = mOrientationHandler.getPrimaryScroll(this);
+        int clearAllWidth = mOrientationHandler.getPrimarySize(mClearAllButton);
+        int clearAllScroll = getScrollForPage(indexOfChild(mClearAllButton));
+        int targetScroll = clearAllScroll + (mIsRtl ? clearAllWidth : -clearAllWidth);
+        return screenStart - targetScroll;
+    }
+
+    private int getSnapToFocusedTaskScrollDiff(boolean isClearAllHidden) {
+        int screenStart = mOrientationHandler.getPrimaryScroll(this);
+        int targetScroll = getScrollForPage(indexOfChild(getFocusedTaskView()));
+        if (!isClearAllHidden) {
+            int clearAllWidth = mOrientationHandler.getPrimarySize(mClearAllButton);
+            int taskGridHorizontalDiff = mLastComputedTaskSize.right - mLastComputedGridSize.right;
+            int clearAllFocusScrollDiff =  taskGridHorizontalDiff - clearAllWidth;
+            targetScroll += mIsRtl ? clearAllFocusScrollDiff : -clearAllFocusScrollDiff;
+        }
+        return screenStart - targetScroll;
+    }
+
     private boolean isTaskViewWithinBounds(TaskView tv, int start, int end) {
         int taskStart = mOrientationHandler.getChildStart(tv) + (int) tv.getOffsetAdjustment(
                 showAsFullscreen(), showAsGrid());
@@ -942,10 +1059,20 @@
                 && taskEnd <= end);
     }
 
-    public TaskView getTaskView(int taskId) {
+    /**
+     * Returns true if the task is in expected scroll position.
+     *
+     * @param taskIndex the index of the task
+     */
+    public boolean isTaskInExpectedScrollPosition(int taskIndex) {
+        return getScrollForPage(taskIndex) == getPagedOrientationHandler().getPrimaryScroll(this);
+    }
+
+    public TaskView getTaskViewByTaskId(int taskId) {
         for (int i = 0; i < getTaskViewCount(); i++) {
             TaskView taskView = getTaskViewAt(i);
-            if (taskView.hasTaskId(taskId)) {
+            int[] taskIds = taskView.getTaskIds();
+            if (taskIds[0] == taskId || taskIds[1] == taskId) {
                 return taskView;
             }
         }
@@ -959,10 +1086,22 @@
         if (!enabled) {
             // Reset the running task when leaving overview since it can still have a reference to
             // its thumbnail
-            mTmpRunningTask = null;
-            if (mSplitPlaceholderView.getSplitController().isSplitSelectActive()) {
+            mTmpRunningTasks = null;
+            if (mSplitSelectStateController.isSplitSelectActive()) {
                 cancelSplitSelect(false);
             }
+            // Remove grouped tasks and recycle once we exit overview
+            int taskCount = getTaskViewCount();
+            for (int i = 0; i < taskCount; i++) {
+                View v = getTaskViewAt(i);
+                if (!(v instanceof GroupedTaskView)) {
+                    return;
+                }
+                GroupedTaskView gtv = (GroupedTaskView) v;
+                gtv.onTaskListVisibilityChanged(false);
+                removeView(gtv);
+            }
+            mSplitBoundsConfig = null;
         }
         updateLocusId();
     }
@@ -996,6 +1135,12 @@
     }
 
     @Override
+    protected float getSignificantMoveThreshold() {
+        return mActivity.getDeviceProfile().isTablet ? SIGNIFICANT_MOVE_THRESHOLD_TABLET
+                : super.getSignificantMoveThreshold();
+    }
+
+    @Override
     public boolean onTouchEvent(MotionEvent ev) {
         super.onTouchEvent(ev);
 
@@ -1090,6 +1235,25 @@
     }
 
     @Override
+    protected void onEdgeAbsorbingScroll() {
+        vibrateForScroll();
+    }
+
+    @Override
+    protected void onScrollOverPageChanged() {
+        vibrateForScroll();
+    }
+
+    private void vibrateForScroll() {
+        long now = SystemClock.uptimeMillis();
+        if (now - mScrollLastHapticTimestamp > mScrollHapticMinGapMillis) {
+            mScrollLastHapticTimestamp = now;
+            VibratorWrapper.INSTANCE.get(mContext).vibrate(SCROLL_VIBRATION_PRIMITIVE,
+                    SCROLL_VIBRATION_PRIMITIVE_SCALE, SCROLL_VIBRATION_FALLBACK);
+        }
+    }
+
+    @Override
     protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
         // Enables swiping to the left or right only if the task overlay is not modal.
         if (!isModal()) {
@@ -1097,6 +1261,42 @@
         }
     }
 
+    /**
+     * Moves the focused task to the front of the carousel in tablets, to minimize animation
+     * required to focus the task in grid.
+     */
+    public void moveFocusedTaskToFront() {
+        if (!mActivity.getDeviceProfile().overviewShowAsGrid) {
+            return;
+        }
+
+        TaskView focusedTaskView = getFocusedTaskView();
+        if (focusedTaskView == null) {
+            return;
+        }
+
+        if (indexOfChild(focusedTaskView) != mCurrentPage) {
+            return;
+        }
+
+        if (mCurrentPage == 0) {
+            return;
+        }
+
+        int primaryScroll = mOrientationHandler.getPrimaryScroll(this);
+        int currentPageScroll = getScrollForPage(mCurrentPage);
+        mCurrentPageScrollDiff = primaryScroll - currentPageScroll;
+
+        mMovingTaskView = focusedTaskView;
+        removeView(focusedTaskView);
+        mMovingTaskView = null;
+        focusedTaskView.resetPersistentViewTransforms();
+        addView(focusedTaskView, 0);
+        setCurrentPage(0);
+
+        updateGridProperties();
+    }
+
     protected void applyLoadPlan(ArrayList<Task> tasks) {
         if (mPendingAnimation != null) {
             mPendingAnimation.addEndListener(success -> applyLoadPlan(tasks));
@@ -1110,7 +1310,7 @@
         }
 
         int currentTaskId = -1;
-        TaskView currentTaskView = getTaskViewAtByAbsoluteIndex(mCurrentPage);
+        TaskView currentTaskView = getTaskViewAt(mCurrentPage);
         if (currentTaskView != null) {
             currentTaskId = currentTaskView.getTask().key.id;
         }
@@ -1119,44 +1319,117 @@
         unloadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
 
         TaskView ignoreResetTaskView =
-                mIgnoreResetTaskId == -1 ? null : getTaskView(mIgnoreResetTaskId);
+                mIgnoreResetTaskId == -1 ? null : getTaskViewByTaskId(mIgnoreResetTaskId);
 
-        final int requiredTaskCount = tasks.size();
-        if (getTaskViewCount() != requiredTaskCount) {
+        int[] splitTaskIds =
+                LauncherSplitScreenListener.INSTANCE.getNoCreate().getPersistentSplitIds();
+        int requiredGroupTaskViews = splitTaskIds.length / 2;
+
+        // Subtract half the number of split tasks and not total number because we've already
+        // added a GroupedTaskView when swipe up gesture happens.
+        // This will need to change if we start showing GroupedTaskViews during swipe up from home
+        int requiredTaskViewCount = tasks.size() - requiredGroupTaskViews;
+
+        if (getTaskViewCount() != requiredTaskViewCount) {
             if (indexOfChild(mClearAllButton) != -1) {
                 removeView(mClearAllButton);
             }
-            for (int i = getTaskViewCount(); i < requiredTaskCount; i++) {
-                addView(mTaskViewPool.getView());
+
+            for (int i = getTaskViewCount(); i < requiredTaskViewCount; i++) {
+                addView(getTaskViewFromPool(false));
             }
-            while (getTaskViewCount() > requiredTaskCount) {
+            while (getTaskViewCount() > requiredTaskViewCount) {
                 removeView(getChildAt(getChildCount() - 1));
             }
-            if (requiredTaskCount > 0) {
+            int groupedTaskViewCount = getGroupedTaskViewCount();
+            while (requiredGroupTaskViews > groupedTaskViewCount) {
+                // Add to front of list
+                addView(getTaskViewFromPool(true), 0);
+                requiredGroupTaskViews--;
+            }
+            if (requiredTaskViewCount > 0) {
                 addView(mClearAllButton);
             }
         }
 
-        // Rebind and reset all task views
-        for (int i = requiredTaskCount - 1; i >= 0; i--) {
-            final int pageIndex = requiredTaskCount - i - 1 + mTaskViewStartIndex;
-            final Task task = tasks.get(i);
+        // Save running task ID if it exists before rebinding all taskViews, otherwise the task from
+        // the runningTaskView currently bound could get assigned to another TaskView
+        // TODO set these type to array and check all taskIDs? Maybe we can get away w/ only one
+        int runningTaskId = getTaskIdsForTaskViewId(mRunningTaskViewId)[0];
+        int focusedTaskId = getTaskIdsForTaskViewId(mFocusedTaskViewId)[0];
+        Log.d(TASK_VIEW_ID_CRASH, "runningTaskId beforeBind: " + runningTaskId
+                + " runningTaskViewId: " + mRunningTaskViewId
+                + " forTaskView: " + getTaskViewFromTaskViewId(mRunningTaskViewId));
+
+        for (int taskViewIndex = requiredTaskViewCount - 1, taskDataIndex = tasks.size() - 1;
+                taskViewIndex >= 0;
+                taskViewIndex--, taskDataIndex--) {
+            final int pageIndex = requiredTaskViewCount - taskViewIndex - 1;
+            final Task task = tasks.get(taskDataIndex);
             final TaskView taskView = (TaskView) getChildAt(pageIndex);
-            taskView.bind(task, mOrientationState);
+            if (taskView instanceof GroupedTaskView) {
+                Task leftTop;
+                Task rightBottom;
+                if (task.key.id == splitTaskIds[0]) {
+                    leftTop = task;
+                    taskDataIndex--;
+                    rightBottom = tasks.get(taskDataIndex);
+                } else {
+                    rightBottom = task;
+                    taskDataIndex--;
+                    leftTop = tasks.get(taskDataIndex);
+                }
+                ((GroupedTaskView) taskView).bind(leftTop, rightBottom, mOrientationState,
+                        mSplitBoundsConfig);
+            } else {
+                taskView.bind(task, mOrientationState);
+            }
         }
+
+        // Keep same previous focused task
+        TaskView newFocusedTaskView = getTaskViewByTaskId(focusedTaskId);
+        // If the list changed, maybe the focused task doesn't exist anymore
+        if (newFocusedTaskView == null && getTaskViewCount() > 0) {
+            newFocusedTaskView = getTaskViewAt(0);
+        }
+        mFocusedTaskViewId = newFocusedTaskView != null ?
+                newFocusedTaskView.getTaskViewId() : -1;
         updateTaskSize();
+        updateChildTaskOrientations();
+
+        TaskView newRunningTaskView = null;
+        if (runningTaskId != -1) {
+            // Update mRunningTaskViewId to be the new TaskView that was assigned by binding
+            // the full list of tasks to taskViews
+            newRunningTaskView = getTaskViewByTaskId(runningTaskId);
+            if (newRunningTaskView == null) {
+                StringBuilder sb = new StringBuilder();
+                for (int i = requiredTaskViewCount - 1; i >= 0; i--) {
+                    final int pageIndex = requiredTaskViewCount - i - 1;
+                    final TaskView taskView = (TaskView) getChildAt(pageIndex);
+                    int taskViewId = taskView.getTaskViewId();
+                    sb.append(" taskViewId: " + taskViewId
+                            + " taskId: " + getTaskIdsForTaskViewId(taskViewId)[0]
+                            + " for taskView: " + taskView + "\n");
+                }
+                Log.d(TASK_VIEW_ID_CRASH, "taskViewCount: " + getTaskViewCount()
+                        + " " + sb.toString());
+                mRunningTaskViewId = -1;
+            } else {
+                mRunningTaskViewId = newRunningTaskView.getTaskViewId();
+            }
+        }
 
         int targetPage = -1;
         if (mNextPage == INVALID_PAGE) {
             // Set the current page to the running task, but not if settling on new task.
-            TaskView runningTaskView = getRunningTaskView();
-            if (runningTaskView != null) {
-                targetPage = indexOfChild(runningTaskView);
+            if (runningTaskId != -1) {
+                targetPage = indexOfChild(newRunningTaskView);
             } else if (getTaskViewCount() > 0) {
                 targetPage = indexOfChild(getTaskViewAt(0));
             }
         } else if (currentTaskId != -1) {
-            currentTaskView = getTaskView(currentTaskId);
+            currentTaskView = getTaskViewByTaskId(currentTaskId);
             if (currentTaskView != null) {
                 targetPage = indexOfChild(currentTaskView);
             }
@@ -1165,7 +1438,8 @@
             setCurrentPage(targetPage);
         }
 
-        if (mIgnoreResetTaskId != -1 && getTaskView(mIgnoreResetTaskId) != ignoreResetTaskView) {
+        if (mIgnoreResetTaskId != -1 &&
+                getTaskViewByTaskId(mIgnoreResetTaskId) != ignoreResetTaskView) {
             // If the taskView mapping is changing, do not preserve the visuals. Since we are
             // mostly preserving the first task, and new taskViews are added to the end, it should
             // generally map to the same task.
@@ -1194,13 +1468,37 @@
     }
 
     public int getTaskViewCount() {
-        int taskViewCount = getChildCount() - mTaskViewStartIndex;
+        int taskViewCount = getChildCount();
         if (indexOfChild(mClearAllButton) != -1) {
             taskViewCount--;
         }
         return taskViewCount;
     }
 
+    public int getGroupedTaskViewCount() {
+        int groupViewCount = 0;
+        for (int i = 0; i < getChildCount(); i++) {
+            if (getChildAt(i) instanceof GroupedTaskView) {
+                groupViewCount++;
+            }
+        }
+        return groupViewCount;
+    }
+
+    /**
+     * Returns the number of tasks in the top row of the overview grid.
+     */
+    public int getTopRowTaskCountForTablet() {
+        return mTopRowIdSet.size();
+    }
+
+    /**
+     * Returns the number of tasks in the bottom row of the overview grid.
+     */
+    public int getBottomRowTaskCountForTablet() {
+        return getTaskViewCount() - mTopRowIdSet.size() - 1;
+    }
+
     protected void onTaskStackUpdated() {
         // Lazily update the empty message only when the task stack is reapplied
         updateEmptyMessage();
@@ -1209,8 +1507,9 @@
     public void resetTaskVisuals() {
         for (int i = getTaskViewCount() - 1; i >= 0; i--) {
             TaskView taskView = getTaskViewAt(i);
-            if (mIgnoreResetTaskId != taskView.getTask().key.id) {
+            if (mIgnoreResetTaskId != taskView.getTaskIds()[0]) {
                 taskView.resetViewTransforms();
+                taskView.setIconScaleAndDim(mTaskIconScaledDown ? 0 : 1);
                 taskView.setStableAlpha(mContentAlpha);
                 taskView.setFullscreenProgress(mFullscreenProgress);
                 taskView.setModalness(mTaskModalness);
@@ -1220,10 +1519,13 @@
             // Since we reuse the same mLiveTileTaskViewSimulator in the RecentsView, we need
             // to reset the params after it settles in Overview from swipe up so that we don't
             // render with obsolete param values.
-            mLiveTileTaskViewSimulator.taskPrimaryTranslation.value = 0;
-            mLiveTileTaskViewSimulator.taskSecondaryTranslation.value = 0;
-            mLiveTileTaskViewSimulator.fullScreenProgress.value = 0;
-            mLiveTileTaskViewSimulator.recentsViewScale.value = 1;
+            runActionOnRemoteHandles(remoteTargetHandle -> {
+                TaskViewSimulator simulator = remoteTargetHandle.getTaskViewSimulator();
+                simulator.taskPrimaryTranslation.value = 0;
+                simulator.taskSecondaryTranslation.value = 0;
+                simulator.fullScreenProgress.value = 0;
+                simulator.recentsViewScale.value = 1;
+            });
 
             // Similar to setRunningTaskHidden below, reapply the state before runningTaskView is
             // null.
@@ -1235,11 +1537,6 @@
             setRunningTaskHidden(mRunningTaskTileHidden);
         }
 
-        // Force apply the scale.
-        if (mIgnoreResetTaskId != mRunningTaskId) {
-            applyRunningTaskIconScale();
-        }
-
         updateCurveProperties();
         // Update the set of visible task's data
         loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
@@ -1279,13 +1576,15 @@
         DeviceProfile dp = mActivity.getDeviceProfile();
         setOverviewGridEnabled(
                 mActivity.getStateManager().getState().displayOverviewTasksAsGrid(dp));
+        setPageSpacing(dp.overviewPageSpacing);
 
         // Propagate DeviceProfile change event.
-        mLiveTileTaskViewSimulator.setDp(dp);
+        runActionOnRemoteHandles(
+                remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator().setDp(dp));
         mActionsView.setDp(dp);
         mOrientationState.setDeviceProfile(dp);
 
-        // Update RecentsView adn TaskView's DeviceProfile dependent layout.
+        // Update RecentsView and TaskView's DeviceProfile dependent layout.
         updateOrientationHandler();
     }
 
@@ -1311,12 +1610,13 @@
                 || !mOrientationHandler.equals(oldOrientationHandler)) {
             // Changed orientations, update controllers so they intercept accordingly.
             mActivity.getDragLayer().recreateControllers();
+            setModalStateEnabled(false);
         }
 
         boolean isInLandscape = mOrientationState.getTouchRotation() != ROTATION_0
                 || mOrientationState.getRecentsActivityRotation() != ROTATION_0;
         mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
-                !mOrientationState.canRecentsActivityRotate() && isInLandscape);
+                !mOrientationState.isRecentsActivityRotationAllowed() && isInLandscape);
 
         // Update TaskView's DeviceProfile dependent layout.
         updateChildTaskOrientations();
@@ -1346,36 +1646,28 @@
         mSizeStrategy.calculateGridTaskSize(mActivity, mActivity.getDeviceProfile(),
                 mLastComputedGridTaskSize, mOrientationHandler);
 
+        mTaskGridVerticalDiff = mLastComputedGridTaskSize.top - mLastComputedTaskSize.top;
+        mTopBottomRowHeightDiff =
+                mLastComputedGridTaskSize.height() + dp.overviewTaskThumbnailTopMarginPx
+                        + dp.overviewRowSpacing;
+
         // Force TaskView to update size from thumbnail
         updateTaskSize();
-
-        // Update ActionsView position
-        if (mActionsView != null) {
-            FrameLayout.LayoutParams layoutParams =
-                    (FrameLayout.LayoutParams) mActionsView.getLayoutParams();
-            if (dp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
-                layoutParams.gravity = Gravity.BOTTOM;
-                layoutParams.bottomMargin =
-                        dp.heightPx - mInsets.bottom - mLastComputedGridSize.bottom;
-                layoutParams.leftMargin = mLastComputedTaskSize.left;
-                layoutParams.rightMargin = dp.widthPx - mLastComputedTaskSize.right;
-                // When in modal state, remove bottom margin to avoid covering content.
-                mActionsView.setModalTransformY(layoutParams.bottomMargin);
-            } else {
-                layoutParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
-                layoutParams.bottomMargin = 0;
-                layoutParams.leftMargin = 0;
-                layoutParams.rightMargin = 0;
-                mActionsView.setModalTransformY(0);
-            }
-            mActionsView.setLayoutParams(layoutParams);
-        }
     }
 
     /**
      * Updates TaskView scaling and translation required to support variable width.
      */
     private void updateTaskSize() {
+        updateTaskSize(false);
+    }
+
+    /**
+     * Updates TaskView scaling and translation required to support variable width.
+     *
+     * @param isTaskDismissal indicates if update was called due to task dismissal
+     */
+    private void updateTaskSize(boolean isTaskDismissal) {
         final int taskCount = getTaskViewCount();
         if (taskCount == 0) {
             return;
@@ -1385,23 +1677,21 @@
         for (int i = 0; i < taskCount; i++) {
             TaskView taskView = getTaskViewAt(i);
             taskView.updateTaskSize();
-            taskView.getPrimaryFullscreenTranslationProperty().set(taskView,
-                    accumulatedTranslationX);
-            taskView.getSecondaryFullscreenTranslationProperty().set(taskView, 0f);
+            taskView.getPrimaryNonGridTranslationProperty().set(taskView, accumulatedTranslationX);
+            taskView.getSecondaryNonGridTranslationProperty().set(taskView, 0f);
             // Compensate space caused by TaskView scaling.
             float widthDiff =
-                    taskView.getLayoutParams().width * (1 - taskView.getFullscreenScale());
+                    taskView.getLayoutParams().width * (1 - taskView.getNonGridScale());
             accumulatedTranslationX += mIsRtl ? widthDiff : -widthDiff;
         }
 
         mClearAllButton.setFullscreenTranslationPrimary(accumulatedTranslationX);
 
-        updateGridProperties();
+        updateGridProperties(isTaskDismissal);
     }
 
     public void getTaskSize(Rect outRect) {
-        mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), outRect,
-                mOrientationHandler);
+        mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), outRect);
         mLastComputedTaskSize.set(outRect);
     }
 
@@ -1409,21 +1699,8 @@
      * Returns the size of task selected to enter modal state.
      */
     public Point getSelectedTaskSize() {
-        mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), mTempRect,
-                mOrientationHandler);
-        int taskWidth = mTempRect.width();
-        int taskHeight = mTempRect.height();
-        if (mFocusedTaskId != -1) {
-            int boxLength = Math.max(taskWidth, taskHeight);
-            if (mFocusedTaskRatio > 1) {
-                taskWidth = boxLength;
-                taskHeight = (int) (boxLength / mFocusedTaskRatio);
-            } else {
-                taskWidth = (int) (boxLength * mFocusedTaskRatio);
-                taskHeight = boxLength;
-            }
-        }
-        return new Point(taskWidth, taskHeight);
+        mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), mTempRect);
+        return new Point(mTempRect.width(), mTempRect.height());
     }
 
     /** Gets the last computed task size */
@@ -1453,22 +1730,29 @@
 
             // After scrolling, update the visible task's data
             loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
-
-            // After scrolling, update ActionsView's visibility.
-            TaskView focusedTaskView = getFocusedTaskView();
-            if (focusedTaskView != null) {
-                float scrollDiff = Math.abs(getScrollForPage(indexOfChild(focusedTaskView))
-                        - mOrientationHandler.getPrimaryScroll(this));
-                float delta = (mGridSideMargin - scrollDiff) / (float) mGridSideMargin;
-                mActionsView.getScrollAlpha().setValue(Utilities.boundToRange(delta, 0, 1));
-            }
         }
 
+        // Update ActionsView's visibility when scroll changes.
+        updateActionsViewFocusedScroll();
+
         // Update the high res thumbnail loader state
         mModel.getThumbnailCache().getHighResLoadingState().setFlingingFast(isFlingingFast);
         return scrolling;
     }
 
+    private void updateActionsViewFocusedScroll() {
+        boolean hiddenFocusedScroll;
+        if (showAsGrid()) {
+            TaskView focusedTaskView = getFocusedTaskView();
+            hiddenFocusedScroll = focusedTaskView == null
+                    || !isTaskInExpectedScrollPosition(indexOfChild(focusedTaskView));
+        } else {
+            hiddenFocusedScroll = false;
+        }
+        mActionsView.updateHiddenFlags(OverviewActionsView.HIDDEN_FOCUSED_SCROLL,
+                hiddenFocusedScroll);
+    }
+
     /**
      * Scales and adjusts translation of adjacent pages as if on a curved carousel.
      */
@@ -1482,7 +1766,7 @@
 
     @Override
     protected int getDestinationPage(int scaledScroll) {
-        if (!(mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get())) {
+        if (!mActivity.getDeviceProfile().overviewShowAsGrid) {
             return super.getDestinationPage(scaledScroll);
         }
 
@@ -1510,7 +1794,8 @@
      * and unloads the associated task data for tasks that are no longer visible.
      */
     public void loadVisibleTaskData(@TaskView.TaskDataChanges int dataChanges) {
-        if (!mOverviewStateEnabled || mTaskListChangeId == -1) {
+        boolean hasLeftOverview = !mOverviewStateEnabled && mScroller.isFinished();
+        if (hasLeftOverview || mTaskListChangeId == -1) {
             // Skip loading visible task data if we've already left the overview state, or if the
             // task list hasn't been loaded yet (the task views will not reflect the task list)
             return;
@@ -1546,8 +1831,17 @@
                 visible = lower <= index && index <= upper;
             }
             if (visible) {
-                if (task == mTmpRunningTask) {
-                    // Skip loading if this is the task that we are animating into
+                boolean skipLoadingTask = false;
+                if (mTmpRunningTasks != null) {
+                    for (Task t : mTmpRunningTasks) {
+                        if (task == t) {
+                            // Skip loading if this is the task that we are animating into
+                            skipLoadingTask = true;
+                            break;
+                        }
+                    }
+                }
+                if (skipLoadingTask) {
                     continue;
                 }
                 if (!mHasVisibleTaskData.get(task.key.id)) {
@@ -1575,7 +1869,7 @@
     private void unloadVisibleTaskData(@TaskView.TaskDataChanges int dataChanges) {
         for (int i = 0; i < mHasVisibleTaskData.size(); i++) {
             if (mHasVisibleTaskData.valueAt(i)) {
-                TaskView taskView = getTaskView(mHasVisibleTaskData.keyAt(i));
+                TaskView taskView = getTaskViewByTaskId(mHasVisibleTaskData.keyAt(i));
                 if (taskView != null) {
                     taskView.onTaskListVisibilityChanged(false /* visible */, dataChanges);
                 }
@@ -1590,7 +1884,7 @@
         // they want to updated their thumbnail state
         for (int i = 0; i < mHasVisibleTaskData.size(); i++) {
             if (mHasVisibleTaskData.valueAt(i)) {
-                TaskView taskView = getTaskView(mHasVisibleTaskData.keyAt(i));
+                TaskView taskView = getTaskViewByTaskId(mHasVisibleTaskData.keyAt(i));
                 if (taskView != null) {
                     // Poke the view again, which will trigger it to load high res if the state
                     // is enabled
@@ -1602,16 +1896,11 @@
 
     public abstract void startHome();
 
-    /** `true` if there is a +1 space available in overview. */
-    public boolean hasRecentsExtraCard() {
-        return false;
-    }
-
     public void reset() {
         setCurrentTask(-1);
         mIgnoreResetTaskId = -1;
         mTaskListChangeId = -1;
-        mFocusedTaskId = -1;
+        mFocusedTaskViewId = -1;
 
         if (mRecentsAnimationController != null) {
             if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile) {
@@ -1622,8 +1911,8 @@
             }
         }
         setEnableDrawingLiveTile(false);
-        mLiveTileParams.setTargetSet(null);
-        mLiveTileTaskViewSimulator.setDrawsBelowRecents(true);
+        runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
+                .setTargetSet(null));
 
         // These are relatively expensive and don't need to be done this frame (RecentsView isn't
         // visible anyway), so defer by a frame to get off the critical path, e.g. app to home.
@@ -1637,27 +1926,71 @@
         });
     }
 
-    public int getRunningTaskId() {
-        return mRunningTaskId;
+    public int getRunningTaskViewId() {
+        return mRunningTaskViewId;
+    }
+
+    protected int[] getTaskIdsForRunningTaskView() {
+        return getTaskIdsForTaskViewId(mRunningTaskViewId);
+    }
+
+    private int[] getTaskIdsForTaskViewId(int taskViewId) {
+        // For now 2 distinct task IDs is max for split screen
+        TaskView runningTaskView = getTaskViewFromTaskViewId(taskViewId);
+        if (runningTaskView == null) {
+            return INVALID_TASK_IDS;
+        }
+
+        return runningTaskView.getTaskIds();
     }
 
     public @Nullable TaskView getRunningTaskView() {
-        return getTaskView(mRunningTaskId);
-    }
-
-    public int getRunningTaskIndex() {
-        return getTaskIndexForId(mRunningTaskId);
+        return getTaskViewFromTaskViewId(mRunningTaskViewId);
     }
 
     public @Nullable TaskView getFocusedTaskView() {
-        return getTaskView(mFocusedTaskId);
+        return getTaskViewFromTaskViewId(mFocusedTaskViewId);
+    }
+
+    private TaskView getTaskViewFromTaskViewId(int taskViewId) {
+        if (taskViewId == -1) {
+            return null;
+        }
+
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            TaskView taskView = getTaskViewAt(i);
+            if (taskView.getTaskViewId() == taskViewId) {
+                return taskView;
+            }
+        }
+        return null;
+    }
+
+    public int getRunningTaskIndex() {
+        TaskView taskView = getRunningTaskView();
+        return taskView == null ? -1 : indexOfChild(taskView);
+    }
+
+    protected @Nullable TaskView getHomeTaskView() {
+        return null;
     }
 
     /**
-     * Returns the width to height ratio of the focused {@link TaskView}.
+     * Handle the edge case where Recents could increment task count very high over long
+     * period of device usage. Probably will never happen, but meh.
      */
-    public float getFocusedTaskRatio() {
-        return mFocusedTaskRatio;
+    private <T extends TaskView> T getTaskViewFromPool(boolean isGrouped) {
+        T taskView = isGrouped ?
+                (T) mGroupedTaskViewPool.getView() :
+                (T) mTaskViewPool.getView();
+        taskView.setTaskViewId(mTaskViewIdCount);
+        if (mTaskViewIdCount == Integer.MAX_VALUE) {
+            mTaskViewIdCount = 0;
+        } else {
+            mTaskViewIdCount++;
+        }
+
+        return taskView;
     }
 
     /**
@@ -1665,14 +1998,10 @@
      * @return -1 if there is no task view for the task id, else the index of the task view.
      */
     public int getTaskIndexForId(int taskId) {
-        TaskView tv = getTaskView(taskId);
+        TaskView tv = getTaskViewByTaskId(taskId);
         return tv == null ? -1 : indexOfChild(tv);
     }
 
-    public int getTaskViewStartIndex() {
-        return mTaskViewStartIndex;
-    }
-
     /**
      * Reloads the view if anything in recents changed.
      */
@@ -1685,7 +2014,7 @@
     /**
      * Called when a gesture from an app is starting.
      */
-    public void onGestureAnimationStart(RunningTaskInfo runningTaskInfo) {
+    public void onGestureAnimationStart(RunningTaskInfo[] runningTaskInfo) {
         mGestureActive = true;
         // This needs to be called before the other states are set since it can create the task view
         if (mOrientationState.setGestureActive(true)) {
@@ -1696,7 +2025,7 @@
         setEnableFreeScroll(false);
         setEnableDrawingLiveTile(false);
         setRunningTaskHidden(true);
-        setRunningTaskIconScaledDown(true);
+        setTaskIconScaledDown(true);
     }
 
     /**
@@ -1704,14 +2033,12 @@
      * {@link #onGestureAnimationStart} and {@link #onGestureAnimationEnd()}.
      */
     public void onSwipeUpAnimationSuccess() {
-        if (getRunningTaskView() != null) {
-            animateUpRunningTaskIconScale();
-        }
+        animateUpTaskIconScale();
         setSwipeDownShouldLaunchApp(true);
     }
 
     private void animateRecentsRotationInPlace(int newRotation) {
-        if (mOrientationState.canRecentsActivityRotate()) {
+        if (mOrientationState.isRecentsActivityRotationAllowed()) {
             // Let system take care of the rotation
             return;
         }
@@ -1738,7 +2065,6 @@
         return as;
     }
 
-
     private void updateChildTaskOrientations() {
         for (int i = 0; i < getTaskViewCount(); i++) {
             getTaskViewAt(i).setOrientationState(mOrientationState);
@@ -1753,21 +2079,37 @@
      * Called when a gesture from an app has finished, and an end target has been determined.
      */
     public void onPrepareGestureEndAnimation(
-            @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget) {
+            @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget,
+            TaskViewSimulator[] taskViewSimulators) {
+        mCurrentGestureEndTarget = endTarget;
+        if (endTarget == GestureState.GestureEndTarget.RECENTS) {
+            updateGridProperties();
+        }
+
         if (mSizeStrategy.stateFromGestureEndTarget(endTarget)
                 .displayOverviewTasksAsGrid(mActivity.getDeviceProfile())) {
-            if (animatorSet == null) {
-                setGridProgress(1);
-            } else {
-                animatorSet.play(ObjectAnimator.ofFloat(this, RECENTS_GRID_PROGRESS, 1));
+            TaskView runningTaskView = getRunningTaskView();
+            float runningTaskPrimaryGridTranslation = 0;
+            if (runningTaskView != null) {
+                // Apply the grid translation to running task unless it's being snapped to
+                // and removes the current translation applied to the running task.
+                runningTaskPrimaryGridTranslation = mOrientationHandler.getPrimaryValue(
+                        runningTaskView.getGridTranslationX(),
+                        runningTaskView.getGridTranslationY())
+                        - runningTaskView.getPrimaryNonGridTranslationProperty().get(
+                        runningTaskView);
             }
-        }
-        mCurrentGestureEndTarget = endTarget;
-        if (endTarget == GestureState.GestureEndTarget.NEW_TASK
-                || endTarget == GestureState.GestureEndTarget.LAST_TASK) {
-            // When switching to tasks in quick switch, ensures the snapped page's scroll maintain
-            // invariant between quick switch and overview, to ensure a smooth animation transition.
-            updateGridProperties();
+            for (TaskViewSimulator tvs : taskViewSimulators) {
+                if (animatorSet == null) {
+                    setGridProgress(1);
+                    tvs.taskPrimaryTranslation.value =
+                            runningTaskPrimaryGridTranslation;
+                } else {
+                    animatorSet.play(ObjectAnimator.ofFloat(this, RECENTS_GRID_PROGRESS, 1));
+                    animatorSet.play(tvs.taskPrimaryTranslation.animateToValue(
+                            runningTaskPrimaryGridTranslation));
+                }
+            }
         }
     }
 
@@ -1786,27 +2128,31 @@
             setRunningTaskViewShowScreenshot(true);
         }
         setRunningTaskHidden(false);
-        animateUpRunningTaskIconScale();
-
-        if (mCurrentGestureEndTarget == GestureState.GestureEndTarget.RECENTS
-                && (!showAsGrid() || getFocusedTaskView() != null)) {
-            animateActionsViewIn();
-        }
+        animateUpTaskIconScale();
+        animateActionsViewIn();
 
         mCurrentGestureEndTarget = null;
-
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mActivity.getDeviceProfile().isMultiWindowMode) {
-            switchToScreenshot(
-                    () -> finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
-                            null));
-        }
     }
 
     /**
      * Returns true if we should add a stub taskView for the running task id
      */
-    protected boolean shouldAddStubTaskView(RunningTaskInfo runningTaskInfo) {
-        return runningTaskInfo != null && getTaskView(runningTaskInfo.taskId) == null;
+    protected boolean shouldAddStubTaskView(RunningTaskInfo[] runningTaskInfos) {
+        if (runningTaskInfos.length > 1) {
+            // * Always create new view for GroupedTaskView
+            // * Remove existing associated taskViews for tasks currently in split
+            for (RunningTaskInfo rti : runningTaskInfos) {
+                TaskView taskView = getTaskViewByTaskId(rti.taskId);
+                if (taskView == null) {
+                    continue;
+                }
+                taskView.onTaskListVisibilityChanged(false);
+                removeView(taskView);
+            }
+            return true;
+        }
+        RunningTaskInfo runningTaskInfo = runningTaskInfos[0];
+        return runningTaskInfo != null && getTaskViewByTaskId(runningTaskInfo.taskId) == null;
     }
 
     /**
@@ -1815,38 +2161,58 @@
      * All subsequent calls to reload will keep the task as the first item until {@link #reset()}
      * is called.  Also scrolls the view to this task.
      */
-    public void showCurrentTask(RunningTaskInfo runningTaskInfo) {
+    private void showCurrentTask(RunningTaskInfo[] runningTaskInfo) {
+        int runningTaskViewId = -1;
+        boolean needGroupTaskView = runningTaskInfo.length > 1;
+        RunningTaskInfo taskInfo = runningTaskInfo[0];
         if (shouldAddStubTaskView(runningTaskInfo)) {
             boolean wasEmpty = getChildCount() == 0;
             // Add an empty view for now until the task plan is loaded and applied
-            final TaskView taskView = mTaskViewPool.getView();
-            addView(taskView, mTaskViewStartIndex);
+            final TaskView taskView;
+            if (needGroupTaskView) {
+                taskView = getTaskViewFromPool(true);
+                RunningTaskInfo secondaryTaskInfo = runningTaskInfo[1];
+                mTmpRunningTasks = new Task[]{
+                        Task.from(new TaskKey(taskInfo), taskInfo, false),
+                        Task.from(new TaskKey(secondaryTaskInfo), secondaryTaskInfo, false)
+                };
+                addView(taskView, 0);
+                // When we create a placeholder task view mSplitBoundsConfig will be null, but with
+                // the actual app running we won't need to show the thumbnail until all the tasks
+                // load later anyways
+                ((GroupedTaskView)taskView).bind(mTmpRunningTasks[0], mTmpRunningTasks[1],
+                        mOrientationState, mSplitBoundsConfig);
+            } else {
+                taskView = getTaskViewFromPool(false);
+                addView(taskView, 0);
+                // The temporary running task is only used for the duration between the start of the
+                // gesture and the task list is loaded and applied
+                mTmpRunningTasks = new Task[]{Task.from(new TaskKey(taskInfo), taskInfo, false)};
+                taskView.bind(mTmpRunningTasks[0], mOrientationState);
+            }
+            runningTaskViewId = taskView.getTaskViewId();
             if (wasEmpty) {
                 addView(mClearAllButton);
             }
-            // The temporary running task is only used for the duration between the start of the
-            // gesture and the task list is loaded and applied
-            mTmpRunningTask = Task.from(new TaskKey(runningTaskInfo), runningTaskInfo, false);
-            taskView.bind(mTmpRunningTask, mOrientationState);
 
             // Measure and layout immediately so that the scroll values is updated instantly
             // as the user might be quick-switching
             measure(makeMeasureSpec(getMeasuredWidth(), EXACTLY),
                     makeMeasureSpec(getMeasuredHeight(), EXACTLY));
             layout(getLeft(), getTop(), getRight(), getBottom());
+        } else if (!needGroupTaskView && getTaskViewByTaskId(taskInfo.taskId) != null) {
+            runningTaskViewId = getTaskViewByTaskId(taskInfo.taskId).getTaskViewId();
         }
 
         boolean runningTaskTileHidden = mRunningTaskTileHidden;
-        int runningTaskId = runningTaskInfo == null ? -1 : runningTaskInfo.taskId;
-        setCurrentTask(runningTaskId);
-        if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
-            setFocusedTask(runningTaskId);
-        }
+        setCurrentTask(runningTaskViewId);
+        mFocusedTaskViewId = runningTaskViewId;
         setCurrentPage(getRunningTaskIndex());
         setRunningTaskViewShowScreenshot(false);
         setRunningTaskHidden(runningTaskTileHidden);
         // Update task size after setting current task.
         updateTaskSize();
+        updateChildTaskOrientations();
 
         // Reload the task list
         mTaskListChangeId = mModel.getTasks(this::applyLoadPlan);
@@ -1855,31 +2221,29 @@
     /**
      * Sets the running task id, cleaning up the old running task if necessary.
      */
-    public void setCurrentTask(int runningTaskId) {
-        if (mRunningTaskId == runningTaskId) {
+    public void setCurrentTask(int runningTaskViewId) {
+        Log.d(TASK_VIEW_ID_CRASH, "currentRunningTaskViewId: " + mRunningTaskViewId
+                + " requestedTaskViewId: " + runningTaskViewId);
+        if (mRunningTaskViewId == runningTaskViewId) {
             return;
         }
 
-        if (mRunningTaskId != -1) {
+        if (mRunningTaskViewId != -1) {
             // Reset the state on the old running task view
-            setRunningTaskIconScaledDown(false);
+            setTaskIconScaledDown(false);
             setRunningTaskViewShowScreenshot(true);
             setRunningTaskHidden(false);
         }
-        mRunningTaskId = runningTaskId;
+        mRunningTaskViewId = runningTaskViewId;
+    }
+
+    private int getTaskViewIdFromTaskId(int taskId) {
+        TaskView taskView = getTaskViewByTaskId(taskId);
+        return taskView != null ? taskView.getTaskViewId() : -1;
     }
 
     /**
-     * Sets the focused task id and store the width to height ratio of the focused task.
-     */
-    protected void setFocusedTask(int focusedTaskId) {
-        mFocusedTaskId = focusedTaskId;
-        mFocusedTaskRatio =
-                mLastComputedTaskSize.width() / (float) mLastComputedTaskSize.height();
-    }
-
-    /**
-     * Hides the tile associated with {@link #mRunningTaskId}
+     * Hides the tile associated with {@link #mRunningTaskViewId}
      */
     public void setRunningTaskHidden(boolean isHidden) {
         mRunningTaskTileHidden = isHidden;
@@ -1903,21 +2267,13 @@
         }
     }
 
-    public void setRunningTaskIconScaledDown(boolean isScaledDown) {
-        if (mRunningTaskIconScaledDown != isScaledDown) {
-            mRunningTaskIconScaledDown = isScaledDown;
-            applyRunningTaskIconScale();
-        }
-    }
-
-    public boolean isTaskIconScaledDown(TaskView taskView) {
-        return mRunningTaskIconScaledDown && getRunningTaskView() == taskView;
-    }
-
-    private void applyRunningTaskIconScale() {
-        TaskView firstTask = getRunningTaskView();
-        if (firstTask != null) {
-            firstTask.setIconScaleAndDim(mRunningTaskIconScaledDown ? 0 : 1);
+    public void setTaskIconScaledDown(boolean isScaledDown) {
+        if (mTaskIconScaledDown != isScaledDown) {
+            mTaskIconScaledDown = isScaledDown;
+            int taskCount = getTaskViewCount();
+            for (int i = 0; i < taskCount; i++) {
+                getTaskViewAt(i).setIconScaleAndDim(mTaskIconScaledDown ? 0 : 1);
+            }
         }
     }
 
@@ -1928,65 +2284,63 @@
         anim.start();
     }
 
-    private void animateActionsViewOut() {
-        ObjectAnimator anim = ObjectAnimator.ofFloat(
-                mActionsView.getVisibilityAlpha(), MultiValueAlpha.VALUE, 1, 0);
-        anim.setDuration(TaskView.SCALE_ICON_DURATION);
-        anim.start();
-    }
-
-    public void animateUpRunningTaskIconScale() {
-        mRunningTaskIconScaledDown = false;
-        TaskView firstTask = getRunningTaskView();
-        if (firstTask != null) {
-            firstTask.setIconScaleAnimStartProgress(0f);
-            firstTask.animateIconScaleAndDimIntoView();
+    public void animateUpTaskIconScale() {
+        mTaskIconScaledDown = false;
+        int taskCount = getTaskViewCount();
+        for (int i = 0; i < taskCount; i++) {
+            TaskView taskView = getTaskViewAt(i);
+            taskView.setIconScaleAnimStartProgress(0f);
+            taskView.animateIconScaleAndDimIntoView();
         }
     }
 
-    /** Updates TaskView and ClearAllButtion scaling and translation required to turn into grid
+    /**
+     * Updates TaskView and ClearAllButtion scaling and translation required to turn into grid
      * layout.
      * This method is used when no task dismissal has occurred.
      */
     private void updateGridProperties() {
-        updateGridProperties(false);
+        updateGridProperties(false, Integer.MAX_VALUE);
+    }
+
+    /**
+     * Updates TaskView and ClearAllButtion scaling and translation required to turn into grid
+     * layout.
+     *
+     * This method is used when task dismissal has occurred, but rebalance is not needed.
+     *
+     * @param isTaskDismissal indicates if update was called due to task dismissal
+     */
+    private void updateGridProperties(boolean isTaskDismissal) {
+        updateGridProperties(isTaskDismissal, Integer.MAX_VALUE);
     }
 
     /**
      * Updates TaskView and ClearAllButton scaling and translation required to turn into grid
      * layout.
+     *
      * This method only calculates the potential position and depends on {@link #setGridProgress} to
      * apply the actual scaling and translation.
      *
-     * @param isTaskDismissal indicates if update was called due to task dismissal
+     * @param isTaskDismissal    indicates if update was called due to task dismissal
+     * @param startRebalanceAfter which view index to start rebalancing from. Use Integer.MAX_VALUE
+     *                           to skip rebalance
      */
-    private void updateGridProperties(boolean isTaskDismissal) {
+    private void updateGridProperties(boolean isTaskDismissal, int startRebalanceAfter) {
         int taskCount = getTaskViewCount();
         if (taskCount == 0) {
             return;
         }
 
-        final int boxLength = Math.max(mLastComputedGridTaskSize.width(),
-                mLastComputedGridTaskSize.height());
         int taskTopMargin = mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
 
-        /*
-         * taskGridVerticalDiff is used to position the top of a task in the top row of the grid
-         * heightOffset is the vertical space one grid task takes + space between top and
-         *   bottom row
-         * Summed together they provide the top position for bottom row of grid tasks
-         */
-        final float taskGridVerticalDiff =
-                mLastComputedGridTaskSize.top - mLastComputedTaskSize.top;
-        final float heightOffset = (boxLength + taskTopMargin) + mRowSpacing;
-
         int topRowWidth = 0;
         int bottomRowWidth = 0;
         float topAccumulatedTranslationX = 0;
         float bottomAccumulatedTranslationX = 0;
 
         // Contains whether the child index is in top or bottom of grid (for non-focused task)
-        // Different from mTopRowIdSet, which contains the taskId of what task is in top row
+        // Different from mTopRowIdSet, which contains the taskViewId of what task is in top row
         IntSet topSet = new IntSet();
         IntSet bottomSet = new IntSet();
 
@@ -1998,7 +2352,9 @@
         int focusedTaskWidthAndSpacing = 0;
         int snappedTaskRowWidth = 0;
         int snappedPage = getNextPage();
-        TaskView snappedTaskView = getTaskViewAtByAbsoluteIndex(snappedPage);
+        TaskView snappedTaskView = getTaskViewAt(snappedPage);
+        TaskView homeTaskView = getHomeTaskView();
+        TaskView nextFocusedTaskView = null;
 
         if (!isTaskDismissal) {
             mTopRowIdSet.clear();
@@ -2036,15 +2392,32 @@
                     // calculate the distance focused task need to shift.
                     focusedTaskShift += mIsRtl ? taskWidthAndSpacing : -taskWidthAndSpacing;
                 }
-                int taskId = taskView.getTask().key.id;
-                boolean isTopRow = isTaskDismissal ? mTopRowIdSet.contains(taskId)
-                        : topRowWidth <= bottomRowWidth;
-                if (isTopRow) {
-                    topRowWidth += taskWidthAndSpacing;
-                    topSet.add(i);
-                    mTopRowIdSet.add(taskId);
+                int taskViewId = taskView.getTaskViewId();
 
-                    taskView.setGridTranslationY(taskGridVerticalDiff);
+                // Rebalance the grid starting after a certain index
+                boolean isTopRow;
+                if (isTaskDismissal) {
+                    if (i > startRebalanceAfter) {
+                        mTopRowIdSet.remove(taskViewId);
+                        isTopRow = topRowWidth <= bottomRowWidth;
+                    } else {
+                        isTopRow = mTopRowIdSet.contains(taskViewId);
+                    }
+                } else {
+                    isTopRow = topRowWidth <= bottomRowWidth;
+                }
+
+                if (isTopRow) {
+                    if (homeTaskView != null && nextFocusedTaskView == null) {
+                        // TaskView will be focused when swipe up, don't count towards row width.
+                        nextFocusedTaskView = taskView;
+                    } else {
+                        topRowWidth += taskWidthAndSpacing;
+                    }
+                    topSet.add(i);
+                    mTopRowIdSet.add(taskViewId);
+
+                    taskView.setGridTranslationY(mTaskGridVerticalDiff);
 
                     // Move horizontally into empty space.
                     float widthOffset = 0;
@@ -2063,7 +2436,7 @@
                     bottomSet.add(i);
 
                     // Move into bottom row.
-                    taskView.setGridTranslationY(heightOffset + taskGridVerticalDiff);
+                    taskView.setGridTranslationY(mTopBottomRowHeightDiff + mTaskGridVerticalDiff);
 
                     // Move horizontally into empty space.
                     float widthOffset = 0;
@@ -2087,20 +2460,12 @@
         // We need to maintain snapped task's page scroll invariant between quick switch and
         // overview, so we sure snapped task's grid translation is 0, and add a non-fullscreen
         // translationX that is the same as snapped task's full scroll adjustment.
-        float snappedTaskFullscreenScrollAdjustment = 0;
+        float snappedTaskNonGridScrollAdjustment = 0;
         float snappedTaskGridTranslationX = 0;
         if (snappedTaskView != null) {
-            snappedTaskFullscreenScrollAdjustment = snappedTaskView.getScrollAdjustment(
+            snappedTaskNonGridScrollAdjustment = snappedTaskView.getScrollAdjustment(
                     /*fullscreenEnabled=*/true, /*gridEnabled=*/false);
-            snappedTaskGridTranslationX = gridTranslations[snappedPage - mTaskViewStartIndex];
-        }
-
-        for (int i = 0; i < taskCount; i++) {
-            TaskView taskView = getTaskViewAt(i);
-            taskView.setGridTranslationX(gridTranslations[i] - snappedTaskGridTranslationX);
-            taskView.getPrimaryNonFullscreenTranslationProperty().set(taskView,
-                    snappedTaskFullscreenScrollAdjustment);
-            taskView.getSecondaryNonFullscreenTranslationProperty().set(taskView, 0f);
+            snappedTaskGridTranslationX = gridTranslations[snappedPage];
         }
 
         // Use the accumulated translation of the row containing the last task.
@@ -2135,7 +2500,7 @@
 
         float clearAllTotalTranslationX =
                 clearAllAccumulatedTranslation + clearAllShorterRowCompensation
-                        + clearAllShortTotalCompensation + snappedTaskFullscreenScrollAdjustment;
+                        + clearAllShortTotalCompensation + snappedTaskNonGridScrollAdjustment;
         if (focusedTaskIndex < taskCount) {
             // Shift by focused task's width and spacing if a task is focused.
             clearAllTotalTranslationX +=
@@ -2145,15 +2510,23 @@
         // Make sure there are enough space between snapped page and ClearAllButton, for the case
         // of swiping up after quick switch.
         if (snappedTaskView != null) {
-            int distanceFromClearAll = longRowWidth - snappedTaskRowWidth;
+            int distanceFromClearAll = longRowWidth - snappedTaskRowWidth + mPageSpacing;
+            // ClearAllButton should be off screen when snapped task is in its snapped position.
             int minimumDistance =
-                    mLastComputedGridSize.width() - snappedTaskView.getLayoutParams().width;
+                    mTaskWidth - snappedTaskView.getLayoutParams().width
+                            + (mLastComputedGridSize.width() - mTaskWidth) / 2;
             if (distanceFromClearAll < minimumDistance) {
                 int distanceDifference = minimumDistance - distanceFromClearAll;
-                clearAllTotalTranslationX += mIsRtl ? -distanceDifference : distanceDifference;
+                snappedTaskGridTranslationX += mIsRtl ? distanceDifference : -distanceDifference;
             }
         }
 
+        for (int i = 0; i < taskCount; i++) {
+            TaskView taskView = getTaskViewAt(i);
+            taskView.setGridTranslationX(gridTranslations[i] - snappedTaskGridTranslationX
+                    + snappedTaskNonGridScrollAdjustment);
+        }
+
         mClearAllButton.setGridTranslationPrimary(
                 clearAllTotalTranslationX - snappedTaskGridTranslationX);
         mClearAllButton.setGridScrollOffset(
@@ -2167,13 +2540,13 @@
         if (taskView1 == null || taskView2 == null) {
             return false;
         }
-        int taskId1 = taskView1.getTask().key.id;
-        int taskId2 = taskView2.getTask().key.id;
-        if (taskId1 == mFocusedTaskId || taskId2 == mFocusedTaskId) {
+        int taskViewId1 = taskView1.getTaskViewId();
+        int taskViewId2 = taskView2.getTaskViewId();
+        if (taskViewId1 == mFocusedTaskViewId || taskViewId2 == mFocusedTaskViewId) {
             return false;
         }
-        return (mTopRowIdSet.contains(taskId1) && mTopRowIdSet.contains(taskId2)) || (
-                !mTopRowIdSet.contains(taskId1) && !mTopRowIdSet.contains(taskId2));
+        return (mTopRowIdSet.contains(taskViewId1) && mTopRowIdSet.contains(taskViewId2)) || (
+                !mTopRowIdSet.contains(taskViewId1) && !mTopRowIdSet.contains(taskViewId2));
     }
 
     /**
@@ -2245,61 +2618,80 @@
             PendingAnimation anim) {
         // Use setFloat instead of setViewAlpha as we want to keep the view visible even when it's
         // alpha is set to 0 so that it can be recycled in the view pool properly
-        anim.setFloat(taskView, VIEW_ALPHA, 0, clampToProgress(ACCEL, 0, 0.5f));
-        SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && taskView.isRunningTask()) {
+            runActionOnRemoteHandles(remoteTargetHandle -> {
+                TransformParams params = remoteTargetHandle.getTransformParams();
+                anim.setFloat(params, TransformParams.TARGET_ALPHA, 0,
+                        clampToProgress(FINAL_FRAME, 0, 0.5f));
+            });
+        }
+        boolean isTaskInBottomGridRow = showAsGrid() && !mTopRowIdSet.contains(
+                taskView.getTaskViewId()) && taskView.getTaskViewId() != mFocusedTaskViewId;
+        anim.setFloat(taskView, VIEW_ALPHA, 0,
+                clampToProgress(isTaskInBottomGridRow ? ACCEL : FINAL_FRAME, 0, 0.5f));
+        FloatProperty<TaskView> secondaryViewTranslate =
+                taskView.getSecondaryDissmissTranslationProperty();
+        int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView);
+        int verticalFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor();
 
         ResourceProvider rp = DynamicResource.provider(mActivity);
         SpringProperty sp = new SpringProperty(SpringProperty.FLAG_CAN_SPRING_ON_START)
                 .setDampingRatio(rp.getFloat(R.dimen.dismiss_task_trans_y_damping_ratio))
                 .setStiffness(rp.getFloat(R.dimen.dismiss_task_trans_y_stiffness));
-        FloatProperty<TaskView> dismissingTaskViewTranslate =
-                taskView.getSecondaryDissmissTranslationProperty();
-        // TODO(b/186800707) translate entire grid size distance
-        int translateDistance = mOrientationHandler.getSecondaryDimension(taskView);
-        int positiveNegativeFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor();
-        if (splitController.isSplitSelectActive()) {
-            // Have the task translate towards whatever side was just pinned
-            int dir = mOrientationHandler.getSplitTaskViewDismissDirection(splitController
-                    .getActiveSplitPositionOption(), mActivity.getDeviceProfile());
-            switch (dir) {
-                case PagedOrientationHandler.SPLIT_TRANSLATE_SECONDARY_NEGATIVE:
-                    dismissingTaskViewTranslate = taskView
-                            .getSecondaryDissmissTranslationProperty();
-                    positiveNegativeFactor = -1;
-                    break;
 
-                case PagedOrientationHandler.SPLIT_TRANSLATE_PRIMARY_POSITIVE:
-                    dismissingTaskViewTranslate = taskView.getPrimaryDismissTranslationProperty();
-                    positiveNegativeFactor = 1;
-                    break;
-
-                case PagedOrientationHandler.SPLIT_TRANSLATE_PRIMARY_NEGATIVE:
-                    dismissingTaskViewTranslate = taskView.getPrimaryDismissTranslationProperty();
-                    positiveNegativeFactor = -1;
-                    break;
-                default:
-                    throw new IllegalStateException("Invalid split task translation: " + dir);
-            }
-        }
-        // Double translation distance so dismissal drag is the full height, as we only animate
-        // the drag for the first half of the progress.
-        anim.add(ObjectAnimator.ofFloat(taskView, dismissingTaskViewTranslate,
-                positiveNegativeFactor * translateDistance * 2).setDuration(duration), LINEAR, sp);
+        anim.add(ObjectAnimator.ofFloat(taskView, secondaryViewTranslate,
+                verticalFactor * secondaryTaskDimension * 2).setDuration(duration), LINEAR, sp);
 
         if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
                 && taskView.isRunningTask()) {
             anim.addOnFrameCallback(() -> {
-                mLiveTileTaskViewSimulator.taskSecondaryTranslation.value =
-                        mOrientationHandler.getSecondaryValue(
-                                taskView.getTranslationX(),
-                                taskView.getTranslationY());
+                runActionOnRemoteHandles(
+                        remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
+                                .taskSecondaryTranslation.value = mOrientationHandler
+                                .getSecondaryValue(taskView.getTranslationX(),
+                                        taskView.getTranslationY()
+                                ));
                 redrawLiveTile();
             });
         }
     }
 
-    public PendingAnimation createTaskDismissAnimation(TaskView taskView, boolean animateTaskView,
-            boolean shouldRemoveTask, long duration) {
+    /**
+     * Places an {@link FloatingTaskView} on top of the thumbnail for {@link #mSplitHiddenTaskView}
+     * and then animates it into the split position that was desired
+     */
+    private void createInitialSplitSelectAnimation(PendingAnimation anim) {
+        mOrientationHandler.getInitialSplitPlaceholderBounds(mSplitPlaceholderSize,
+                mActivity.getDeviceProfile(),
+                mSplitSelectStateController.getActiveSplitStagePosition(), mTempRect);
+
+        RectF startingTaskRect = new RectF();
+        mSplitHiddenTaskView.setVisibility(INVISIBLE);
+        mFirstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
+                mSplitHiddenTaskView, startingTaskRect);
+        mFirstFloatingTaskView.setAlpha(1);
+        mFirstFloatingTaskView.addAnimation(anim, startingTaskRect,
+                mTempRect, mSplitHiddenTaskView, true /*fadeWithThumbnail*/);
+        anim.addEndListener(success -> {
+            if (success) {
+                mSplitToast.show();
+            }
+        });
+    }
+
+    /**
+     * Creates a {@link PendingAnimation} for dismissing the specified {@link TaskView}.
+     * @param dismissedTaskView the {@link TaskView} to be dismissed
+     * @param animateTaskView whether the {@link TaskView} to be dismissed should be animated
+     * @param shouldRemoveTask whether the associated {@link Task} should be removed from
+     *                         ActivityManager after dismissal
+     * @param duration duration of the animation
+     * @param dismissingForSplitSelection task dismiss animation is used for entering split
+     *                                    selection state from app icon
+     */
+    public PendingAnimation createTaskDismissAnimation(TaskView dismissedTaskView,
+            boolean animateTaskView, boolean shouldRemoveTask, long duration,
+            boolean dismissingForSplitSelection) {
         if (mPendingAnimation != null) {
             mPendingAnimation.createPlaybackController().dispatchOnCancel().dispatchOnEnd();
         }
@@ -2310,30 +2702,157 @@
             return anim;
         }
 
+        boolean showAsGrid = showAsGrid();
+        int taskCount = getTaskViewCount();
+        int dismissedIndex = indexOfChild(dismissedTaskView);
+        int dismissedTaskViewId = dismissedTaskView.getTaskViewId();
+
+        // Grid specific properties.
+        boolean isFocusedTaskDismissed = false;
+        TaskView nextFocusedTaskView = null;
+        boolean nextFocusedTaskFromTop = false;
+        float dismissedTaskWidth = 0;
+        float nextFocusedTaskWidth = 0;
+
+        // Non-grid specific properties.
         int[] oldScroll = new int[count];
         int[] newScroll = new int[count];
-        getPageScrolls(oldScroll, false, SIMPLE_SCROLL_LOGIC);
-        getPageScrolls(newScroll, false, (v) -> v.getVisibility() != GONE && v != taskView);
-        int taskCount = getTaskViewCount();
         int scrollDiffPerPage = 0;
-        if (count > 1) {
-            scrollDiffPerPage = Math.abs(oldScroll[1] - oldScroll[0]);
-        }
-        int draggedIndex = indexOfChild(taskView);
-
-        boolean isFocusedTaskDismissed = taskView.getTask().key.id == mFocusedTaskId;
-        if (isFocusedTaskDismissed && showAsGrid()) {
-            anim.setFloat(mActionsView, VIEW_ALPHA, 0, clampToProgress(ACCEL_0_5, 0, 0.5f));
-        }
-        float dismissedTaskWidth = taskView.getLayoutParams().width + mPageSpacing;
         boolean needsCurveUpdates = false;
+
+        if (showAsGrid) {
+            dismissedTaskWidth = dismissedTaskView.getLayoutParams().width + mPageSpacing;
+            isFocusedTaskDismissed = dismissedTaskViewId == mFocusedTaskViewId;
+            if (isFocusedTaskDismissed && !isSplitSelectionActive()) {
+                nextFocusedTaskFromTop =
+                        mTopRowIdSet.size() > 0 && mTopRowIdSet.size() >= (taskCount - 1) / 2f;
+                // Pick the next focused task from the preferred row.
+                for (int i = 0; i < taskCount; i++) {
+                    TaskView taskView = getTaskViewAt(i);
+                    if (taskView == dismissedTaskView) {
+                        continue;
+                    }
+                    boolean isTopRow = mTopRowIdSet.contains(taskView.getTaskViewId());
+                    if ((nextFocusedTaskFromTop && isTopRow
+                            || (!nextFocusedTaskFromTop && !isTopRow))) {
+                        nextFocusedTaskView = taskView;
+                        break;
+                    }
+                }
+                if (nextFocusedTaskView != null) {
+                    nextFocusedTaskWidth =
+                            nextFocusedTaskView.getLayoutParams().width + mPageSpacing;
+                }
+            }
+        } else {
+            getPageScrolls(oldScroll, false, SIMPLE_SCROLL_LOGIC);
+            getPageScrolls(newScroll, false,
+                    v -> v.getVisibility() != GONE && v != dismissedTaskView);
+            if (count > 1) {
+                scrollDiffPerPage = Math.abs(oldScroll[1] - oldScroll[0]);
+            }
+        }
+
+        float dismissTranslationInterpolationEnd = 1;
+        boolean closeGapBetweenClearAll = false;
+        boolean isClearAllHidden = isClearAllHidden();
+        if (showAsGrid && isLastGridTaskVisible()) {
+            // 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.
+            float longGridRowWidthDiff = 0;
+
+            int topGridRowSize = mTopRowIdSet.size();
+            int bottomGridRowSize = taskCount - mTopRowIdSet.size() - 1;
+            boolean topRowLonger = topGridRowSize > bottomGridRowSize;
+            boolean bottomRowLonger = bottomGridRowSize > topGridRowSize;
+            boolean dismissedTaskFromTop = mTopRowIdSet.contains(dismissedTaskViewId);
+            boolean dismissedTaskFromBottom = !dismissedTaskFromTop && !isFocusedTaskDismissed;
+            float gapWidth = 0;
+            if ((topRowLonger && dismissedTaskFromTop)
+                    || (bottomRowLonger && dismissedTaskFromBottom)) {
+                gapWidth = dismissedTaskWidth;
+            } else if ((topRowLonger && nextFocusedTaskFromTop)
+                    || (bottomRowLonger && !nextFocusedTaskFromTop)) {
+                gapWidth = nextFocusedTaskWidth;
+            }
+            if (gapWidth > 0) {
+                if (taskCount > 2) {
+                    // Compensate the removed gap.
+                    longGridRowWidthDiff += mIsRtl ? -gapWidth : gapWidth;
+                    if (isClearAllHidden) {
+                        // If ClearAllButton isn't fully shown, snap to the last task.
+                        longGridRowWidthDiff += getSnapToLastTaskScrollDiff();
+                    }
+                } else {
+                    // If only focused task will be left, snap to focused task instead.
+                    longGridRowWidthDiff += getSnapToFocusedTaskScrollDiff(isClearAllHidden);
+                }
+            }
+
+            // If we need to animate the grid to compensate the clear all gap, we split the second
+            // half of the dismiss pending animation (in which the non-dismissed tasks slide into
+            // place) in half again, making the first quarter the existing non-dismissal sliding
+            // and the second quarter this new animation of gap filling. This is due to the fact
+            // that PendingAnimation is a single animation, not a sequence of animations, so we
+            // fake it using interpolation.
+            if (longGridRowWidthDiff != 0) {
+                closeGapBetweenClearAll = true;
+                // Stagger the offsets of each additional task for a delayed animation. We use
+                // half here as this animation is half of half of an animation (1/4th).
+                float halfAdditionalDismissTranslationOffset =
+                        (0.5f * ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET);
+                dismissTranslationInterpolationEnd = Utilities.boundToRange(
+                        END_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
+                                + (taskCount - 1) * halfAdditionalDismissTranslationOffset,
+                        END_DISMISS_TRANSLATION_INTERPOLATION_OFFSET, 1);
+                for (int i = 0; i < taskCount; i++) {
+                    TaskView taskView = getTaskViewAt(i);
+                    anim.setFloat(taskView, TaskView.GRID_END_TRANSLATION_X, longGridRowWidthDiff,
+                            clampToProgress(LINEAR, dismissTranslationInterpolationEnd, 1));
+                    dismissTranslationInterpolationEnd = Utilities.boundToRange(
+                            dismissTranslationInterpolationEnd
+                                    - halfAdditionalDismissTranslationOffset,
+                            END_DISMISS_TRANSLATION_INTERPOLATION_OFFSET, 1);
+                    if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
+                            && taskView.isRunningTask()) {
+                        anim.addOnFrameCallback(() -> {
+                            runActionOnRemoteHandles(
+                                    remoteTargetHandle ->
+                                            remoteTargetHandle.getTaskViewSimulator()
+                                                    .taskPrimaryTranslation.value =
+                                                    TaskView.GRID_END_TRANSLATION_X.get(taskView));
+                            redrawLiveTile();
+                        });
+                    }
+                }
+
+                // Change alpha of clear all if translating grid to hide it
+                if (isClearAllHidden) {
+                    anim.setFloat(mClearAllButton, DISMISS_ALPHA, 0, LINEAR);
+                    anim.addListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            super.onAnimationEnd(animation);
+                            mClearAllButton.setDismissAlpha(1);
+                        }
+                    });
+                }
+            }
+        }
+
+        int distanceFromDismissedTask = 0;
         for (int i = 0; i < count; i++) {
             View child = getChildAt(i);
-            if (child == taskView) {
+            if (child == dismissedTaskView) {
                 if (animateTaskView) {
-                    addDismissedTaskAnimations(taskView, duration, anim);
+                    if (dismissingForSplitSelection) {
+                        createInitialSplitSelectAnimation(anim);
+                    } else {
+                        addDismissedTaskAnimations(dismissedTaskView, duration, anim);
+                    }
                 }
-            } else if (!showAsGrid()) {
+            } else if (!showAsGrid) {
                 // Compute scroll offsets from task dismissal for animation.
                 // If we just take newScroll - oldScroll, everything to the right of dragged task
                 // translates to the left. We need to offset this in some cases:
@@ -2342,15 +2861,15 @@
                 // - Current page is rightmost page (leftmost for RTL)
                 // - Dragging an adjacent page on the left side (right side for RTL)
                 int offset = mIsRtl ? scrollDiffPerPage : 0;
-                if (mCurrentPage == draggedIndex) {
+                if (mCurrentPage == dismissedIndex) {
                     int lastPage = taskCount - 1;
                     if (mCurrentPage == lastPage) {
                         offset += mIsRtl ? -scrollDiffPerPage : scrollDiffPerPage;
                     }
                 } else {
-                    // Dragging an adjacent page.
+                    // Dismissing an adjacent page.
                     int negativeAdjacent = mCurrentPage - 1; // (Right in RTL, left in LTR)
-                    if (draggedIndex == negativeAdjacent) {
+                    if (dismissedIndex == negativeAdjacent) {
                         offset += mIsRtl ? -scrollDiffPerPage : scrollDiffPerPage;
                     }
                 }
@@ -2363,7 +2882,7 @@
 
                     float additionalDismissDuration =
                             ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET * Math.abs(
-                                    i - draggedIndex);
+                                    i - dismissedIndex);
                     anim.setFloat(child, translationProperty, scrollDiff, clampToProgress(LINEAR,
                             Utilities.boundToRange(INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
                                     + additionalDismissDuration, 0f, 1f), 1));
@@ -2371,30 +2890,65 @@
                             && child instanceof TaskView
                             && ((TaskView) child).isRunningTask()) {
                         anim.addOnFrameCallback(() -> {
-                            mLiveTileTaskViewSimulator.taskPrimaryTranslation.value =
-                                    mOrientationHandler.getPrimaryValue(child.getTranslationX(),
-                                            child.getTranslationY());
+                            runActionOnRemoteHandles(
+                                    remoteTargetHandle ->
+                                            remoteTargetHandle.getTaskViewSimulator()
+                                                    .taskPrimaryTranslation.value =
+                                                    mOrientationHandler.getPrimaryValue(
+                                                            child.getTranslationX(),
+                                                            child.getTranslationY()
+                                                    ));
                             redrawLiveTile();
                         });
                     }
                     needsCurveUpdates = true;
                 }
             } else if (child instanceof TaskView) {
+                TaskView taskView = (TaskView) child;
+                if (isFocusedTaskDismissed) {
+                    if (nextFocusedTaskView != null &&
+                            !isSameGridRow(taskView, nextFocusedTaskView)) {
+                        continue;
+                    }
+                } else {
+                    if (i < dismissedIndex || !isSameGridRow(taskView, dismissedTaskView)) {
+                        continue;
+                    }
+                }
                 // Animate task with index >= dismissed index and in the same row as the
-                // dismissed index, or if the dismissed task was the focused task. Offset
-                // successive task dismissal durations for a staggered effect.
-                if (isFocusedTaskDismissed || (i >= draggedIndex && isSameGridRow((TaskView) child,
-                        taskView))) {
-                    FloatProperty translationProperty =
-                            ((TaskView) child).getPrimaryDismissTranslationProperty();
-                    float additionalDismissDuration =
-                            ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET * Math.abs(
-                                    i - draggedIndex);
-                    anim.setFloat(child, translationProperty,
-                            !mIsRtl ? -dismissedTaskWidth : dismissedTaskWidth,
-                            clampToProgress(LINEAR, Utilities.boundToRange(
-                                    INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
-                                            + additionalDismissDuration, 0f, 1f), 1));
+                // dismissed index or next focused index. Offset successive task dismissal
+                // durations for a staggered effect.
+                float animationStartProgress = Utilities.boundToRange(
+                        INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
+                                + ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
+                                * ++distanceFromDismissedTask, 0f,
+                        dismissTranslationInterpolationEnd);
+                if (taskView == nextFocusedTaskView) {
+                    // Enlarge the task to be focused next, and translate into focus position.
+                    float scale = mTaskWidth / (float) mLastComputedGridTaskSize.width();
+                    anim.setFloat(taskView, TaskView.SNAPSHOT_SCALE, scale,
+                            clampToProgress(LINEAR, animationStartProgress,
+                                    dismissTranslationInterpolationEnd));
+                    anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(),
+                            mIsRtl ? dismissedTaskWidth : -dismissedTaskWidth,
+                            clampToProgress(LINEAR, animationStartProgress,
+                                    dismissTranslationInterpolationEnd));
+                    float secondaryTranslation = -mTaskGridVerticalDiff;
+                    if (!nextFocusedTaskFromTop) {
+                        secondaryTranslation -= mTopBottomRowHeightDiff;
+                    }
+                    anim.setFloat(taskView, taskView.getSecondaryDissmissTranslationProperty(),
+                            secondaryTranslation, clampToProgress(LINEAR, animationStartProgress,
+                                    dismissTranslationInterpolationEnd));
+                    anim.setFloat(taskView, TaskView.FOCUS_TRANSITION, 0f,
+                            clampToProgress(LINEAR, 0f, ANIMATION_DISMISS_PROGRESS_MIDPOINT));
+                } else {
+                    float primaryTranslation =
+                            nextFocusedTaskView != null ? nextFocusedTaskWidth : dismissedTaskWidth;
+                    anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(),
+                            mIsRtl ? primaryTranslation : -primaryTranslation,
+                            clampToProgress(LINEAR, animationStartProgress,
+                                    dismissTranslationInterpolationEnd));
                 }
             }
         }
@@ -2405,15 +2959,17 @@
 
         // Add a tiny bit of translation Z, so that it draws on top of other views
         if (animateTaskView) {
-            taskView.setTranslationZ(0.1f);
+            dismissedTaskView.setTranslationZ(0.1f);
         }
 
         mPendingAnimation = anim;
+        final TaskView finalNextFocusedTaskView = nextFocusedTaskView;
+        final boolean finalCloseGapBetweenClearAll = closeGapBetweenClearAll;
         mPendingAnimation.addEndListener(new Consumer<Boolean>() {
             @Override
             public void accept(Boolean success) {
                 if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
-                        && taskView.isRunningTask() && success) {
+                        && dismissedTaskView.isRunningTask() && success) {
                     finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
                             () -> onEnd(success));
                 } else {
@@ -2425,15 +2981,16 @@
             private void onEnd(boolean success) {
                 if (success) {
                     if (shouldRemoveTask) {
-                        if (taskView.getTask() != null) {
-                            if (ENABLE_QUICKSTEP_LIVE_TILE.get() && taskView.isRunningTask()) {
+                        if (dismissedTaskView.getTask() != null) {
+                            if (ENABLE_QUICKSTEP_LIVE_TILE.get()
+                                    && dismissedTaskView.isRunningTask()) {
                                 finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
-                                        () -> removeTaskInternal(taskView));
+                                        () -> removeTaskInternal(dismissedTaskViewId));
                             } else {
-                                removeTaskInternal(taskView);
+                                removeTaskInternal(dismissedTaskViewId);
                             }
                             mActivity.getStatsLogManager().logger()
-                                    .withItemInfo(taskView.getItemInfo())
+                                    .withItemInfo(dismissedTaskView.getItemInfo())
                                     .log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
                         }
                     }
@@ -2443,33 +3000,146 @@
                     resetTaskVisuals();
 
                     int pageToSnapTo = mCurrentPage;
-                    // Snap to start if focused task was dismissed, as after quick switch it could
-                    // be at any page but the focused task always displays at the start.
-                    if (taskView.getTask().key.id == mFocusedTaskId) {
-                        pageToSnapTo = mTaskViewStartIndex;
-                    } else if (draggedIndex < pageToSnapTo || pageToSnapTo == (getTaskViewCount()
-                            - 1)) {
-                        pageToSnapTo -= 1;
-                    }
-                    removeViewInLayout(taskView);
+                    mCurrentPageScrollDiff = 0;
+                    int taskViewIdToSnapTo = -1;
+                    if (showAsGrid) {
+                        if (finalCloseGapBetweenClearAll) {
+                            if (taskCount > 2) {
+                                pageToSnapTo = indexOfChild(mClearAllButton);
+                                if (isClearAllHidden) {
+                                    int clearAllWidth = mOrientationHandler.getPrimarySize(
+                                            mClearAllButton);
+                                    mCurrentPageScrollDiff =
+                                            isRtl() ? clearAllWidth : -clearAllWidth;
+                                }
+                            } else if (isClearAllHidden) {
+                                // Snap to focused task if clear all is hidden.
+                                pageToSnapTo = 0;
+                            }
+                        } else {
+                            // Get the id of the task view we will snap to based on the current
+                            // page's relative position as the order of indices change over time due
+                            // to dismissals.
+                            TaskView snappedTaskView = getTaskViewAt(mCurrentPage);
+                            if (snappedTaskView != null) {
+                                if (snappedTaskView.getTaskViewId() == mFocusedTaskViewId) {
+                                    if (finalNextFocusedTaskView != null) {
+                                        taskViewIdToSnapTo =
+                                                finalNextFocusedTaskView.getTaskViewId();
+                                    } else {
+                                        taskViewIdToSnapTo = mFocusedTaskViewId;
+                                    }
+                                } else {
+                                    int snappedTaskViewId = snappedTaskView.getTaskViewId();
+                                    boolean isSnappedTaskInTopRow = mTopRowIdSet.contains(
+                                            snappedTaskViewId);
+                                    IntArray taskViewIdArray =
+                                            isSnappedTaskInTopRow ? getTopRowIdArray()
+                                                    : getBottomRowIdArray();
+                                    int snappedIndex = taskViewIdArray.indexOf(snappedTaskViewId);
+                                    taskViewIdArray.removeValue(dismissedTaskViewId);
+                                    if (finalNextFocusedTaskView != null) {
+                                        taskViewIdArray.removeValue(
+                                                finalNextFocusedTaskView.getTaskViewId());
+                                    }
+                                    if (snappedIndex < taskViewIdArray.size()) {
+                                        taskViewIdToSnapTo = taskViewIdArray.get(snappedIndex);
+                                    } else if (snappedIndex == taskViewIdArray.size()) {
+                                        // If the snapped task is the last item from the
+                                        // dismissed row,
+                                        // snap to the same column in the other grid row
+                                        IntArray inverseRowTaskViewIdArray =
+                                                isSnappedTaskInTopRow ? getBottomRowIdArray()
+                                                        : getTopRowIdArray();
+                                        if (snappedIndex < inverseRowTaskViewIdArray.size()) {
+                                            taskViewIdToSnapTo = inverseRowTaskViewIdArray.get(
+                                                    snappedIndex);
+                                        }
+                                    }
+                                }
+                            }
 
-                    if (getTaskViewCount() == 0) {
+                            int primaryScroll = mOrientationHandler.getPrimaryScroll(
+                                    RecentsView.this);
+                            int currentPageScroll = getScrollForPage(pageToSnapTo);
+                            mCurrentPageScrollDiff = primaryScroll - currentPageScroll;
+                        }
+                    } else if (dismissedIndex < pageToSnapTo || pageToSnapTo == taskCount - 1) {
+                        pageToSnapTo--;
+                    }
+                    removeViewInLayout(dismissedTaskView);
+                    mTopRowIdSet.remove(dismissedTaskViewId);
+
+                    if (taskCount == 1) {
                         removeViewInLayout(mClearAllButton);
                         startHome();
                     } else {
-                        snapToPageImmediately(pageToSnapTo);
+                        // Update focus task and its size.
+                        if (finalNextFocusedTaskView != null) {
+                            mFocusedTaskViewId = finalNextFocusedTaskView.getTaskViewId();
+                            mTopRowIdSet.remove(mFocusedTaskViewId);
+                            finalNextFocusedTaskView.animateIconScaleAndDimIntoView();
+                        }
+                        updateTaskSize(/*isTaskDismissal=*/ true);
+                        updateChildTaskOrientations();
+                        // Update scroll and snap to page.
+                        updateScrollSynchronously();
+
+                        if (showAsGrid) {
+                            // Rebalance tasks in the grid
+                            int highestVisibleTaskIndex = getHighestVisibleTaskIndex();
+                            if (highestVisibleTaskIndex < Integer.MAX_VALUE) {
+                                TaskView taskView = getTaskViewAt(highestVisibleTaskIndex);
+
+                                boolean shouldRebalance = false;
+                                int screenStart = mOrientationHandler.getPrimaryScroll(
+                                        RecentsView.this);
+                                int taskStart = mOrientationHandler.getChildStart(taskView)
+                                        + (int) taskView.getOffsetAdjustment(/*fullscreenEnabled=*/
+                                        false, /*gridEnabled=*/ true);
+
+                                // Rebalance only if there is a maximum gap between the task and the
+                                // screen's edge; this ensures that rebalanced tasks are outside the
+                                // visible screen.
+                                if (mIsRtl) {
+                                    shouldRebalance = taskStart <= screenStart + mPageSpacing;
+                                } else {
+                                    int screenEnd =
+                                            screenStart + mOrientationHandler.getMeasuredSize(
+                                                    RecentsView.this);
+                                    int taskSize = (int) (mOrientationHandler.getMeasuredSize(
+                                            taskView) * taskView
+                                            .getSizeAdjustment(/*fullscreenEnabled=*/false));
+                                    int taskEnd = taskStart + taskSize;
+
+                                    shouldRebalance = taskEnd >= screenEnd - mPageSpacing;
+                                }
+
+                                if (shouldRebalance) {
+                                    updateGridProperties(/*isTaskDismissal=*/ true,
+                                            highestVisibleTaskIndex);
+                                    updateScrollSynchronously();
+                                }
+                            }
+
+                            // If snapping to another page due to indices rearranging, find the new
+                            // index after dismissal & rearrange using the task view id.
+                            if (taskViewIdToSnapTo != -1) {
+                                pageToSnapTo = indexOfChild(
+                                        getTaskViewFromTaskViewId(taskViewIdToSnapTo));
+                            }
+                        }
+                        setCurrentPage(pageToSnapTo);
+                        // Update various scroll-dependent UI.
                         dispatchScrollChanged();
-                        // Grid got messed up, reapply.
-                        updateGridProperties(true);
-                        if (showAsGrid() && getFocusedTaskView() == null
-                                && mActionsView.getVisibilityAlpha().getValue() == 1) {
-                            animateActionsViewOut();
+                        updateActionsViewFocusedScroll();
+                        if (isClearAllHidden()) {
+                            mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING,
+                                    false);
                         }
                     }
-                    // Update the layout synchronously so that the position of next view is
-                    // immediately available.
-                    onLayout(false /*  changed */, getLeft(), getTop(), getRight(), getBottom());
                 }
+                updateCurrentTaskActionsVisibility();
                 onDismissAnimationEnds();
                 mPendingAnimation = null;
             }
@@ -2477,41 +3147,128 @@
         return anim;
     }
 
-    private void removeTaskInternal(TaskView taskView) {
-        UI_HELPER_EXECUTOR.getHandler().postDelayed(() ->
-                        ActivityManagerWrapper.getInstance().removeTask(
-                                taskView.getTask().key.id),
+    /**
+     * Hides all overview actions if current page is for split apps, shows otherwise
+     * If actions are showing, we only show split option if
+     * * Device is large screen
+     * * There are at least 2 tasks to invoke split
+     */
+    private void updateCurrentTaskActionsVisibility() {
+        boolean isCurrentSplit = getCurrentPageTaskView() instanceof GroupedTaskView;
+        mActionsView.updateHiddenFlags(HIDDEN_SPLIT_SCREEN, isCurrentSplit);
+        if (isCurrentSplit) {
+            return;
+        }
+        mActionsView.setSplitButtonVisible(
+                mActivity.getDeviceProfile().overviewShowAsGrid && getTaskViewCount() > 1);
+    }
+
+    /**
+     * Returns all the tasks in the top row, without the focused task
+     */
+    private IntArray getTopRowIdArray() {
+        if (mTopRowIdSet.isEmpty()) {
+            return new IntArray(0);
+        }
+        IntArray topArray = new IntArray(mTopRowIdSet.size());
+        int taskViewCount = getTaskViewCount();
+        for (int i = 0; i < taskViewCount; i++) {
+            int taskViewId = getTaskViewAt(i).getTaskViewId();
+            if (mTopRowIdSet.contains(taskViewId)) {
+                topArray.add(taskViewId);
+            }
+        }
+        return topArray;
+    }
+
+    /**
+     * Returns all the tasks in the bottom row, without the focused task
+     */
+    private IntArray getBottomRowIdArray() {
+        int bottomRowIdArraySize = getBottomRowTaskCountForTablet();
+        if (bottomRowIdArraySize <= 0) {
+            return new IntArray(0);
+        }
+        IntArray bottomArray = new IntArray(bottomRowIdArraySize);
+        int taskViewCount = getTaskViewCount();
+        for (int i = 0; i < taskViewCount; i++) {
+            int taskViewId = getTaskViewAt(i).getTaskViewId();
+            if (!mTopRowIdSet.contains(taskViewId) && taskViewId != mFocusedTaskViewId) {
+                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.
+     *
+     * @return the highest visible TaskView index between both rows
+     */
+    private int getHighestVisibleTaskIndex() {
+        if (mTopRowIdSet.isEmpty()) return Integer.MAX_VALUE; // return earlier
+
+        int lastVisibleIndex = Integer.MAX_VALUE;
+        IntArray topRowIdArray = getTopRowIdArray();
+        IntArray bottomRowIdArray = getBottomRowIdArray();
+        int balancedColumns = Math.min(bottomRowIdArray.size(), topRowIdArray.size());
+
+        for (int i = 0; i < balancedColumns; i++) {
+            TaskView topTask = getTaskViewFromTaskViewId(topRowIdArray.get(i));
+
+            if (isTaskViewVisible(topTask)) {
+                TaskView bottomTask = getTaskViewFromTaskViewId(bottomRowIdArray.get(i));
+                lastVisibleIndex = Math.max(indexOfChild(topTask), indexOfChild(bottomTask));
+            } else if (lastVisibleIndex < Integer.MAX_VALUE) {
+                break;
+            }
+        }
+
+        return lastVisibleIndex;
+    }
+
+    private void removeTaskInternal(int dismissedTaskViewId) {
+        int[] taskIds = getTaskIdsForTaskViewId(dismissedTaskViewId);
+        int primaryTaskId = taskIds[0];
+        int secondaryTaskId = taskIds[1];
+        UI_HELPER_EXECUTOR.getHandler().postDelayed(
+                () -> {
+                    ActivityManagerWrapper.getInstance().removeTask(primaryTaskId);
+                    if (secondaryTaskId != -1) {
+                        ActivityManagerWrapper.getInstance().removeTask(secondaryTaskId);
+                    }
+                },
                 REMOVE_TASK_WAIT_FOR_APP_STOP_MS);
     }
 
     /**
      * @return {@code true} if one of the task thumbnails would intersect/overlap with the
-     *         {@link #mSplitPlaceholderView}
+     *         {@link #mFirstFloatingTaskView}
      */
-    public boolean shouldShiftThumbnailsForSplitSelect(@SplitConfigurationOptions.StagePosition
-            int stagePosition) {
+    public boolean shouldShiftThumbnailsForSplitSelect(@StagePosition int stagePosition) {
         if (!mActivity.getDeviceProfile().isTablet) {
             // Never enough space on phones
             return true;
         } else if (!mActivity.getDeviceProfile().isLandscape) {
-            return false;
+            return true;
         }
 
         Rect splitBounds = new Rect();
-        float placeholderSize = getResources().getDimension(R.dimen.split_placeholder_size);
         // This acts as a best approximation on where the splitplaceholder view would be,
         // doesn't need to be exact necessarily. This also doesn't need to take translations
         // into account since placeholder view is not translated
         if (stagePosition == SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT) {
-            splitBounds.set((int) (getWidth() - placeholderSize), 0, getWidth(), getHeight());
+            splitBounds.set(getWidth() - mSplitPlaceholderSize, 0, getWidth(), getHeight());
         } else {
-            splitBounds.set(0, 0, (int) (placeholderSize), getHeight());
+            splitBounds.set(0, 0, mSplitPlaceholderSize, getHeight());
         }
         Rect taskBounds = new Rect();
         int taskCount = getTaskViewCount();
         for (int i = 0; i < taskCount; i++) {
             TaskView taskView = getTaskViewAt(i);
-            if (taskView == mSplitHiddenTaskView && taskView != getFocusedTaskView()) {
+            if (taskView == mSplitHiddenTaskView
+                    && !(showAsGrid() && taskView == getFocusedTaskView())) {
                 // Case where the hidden task view would have overlapped w/ placeholder,
                 // but because it's going to hide we don't care
                 // TODO (b/187312247) edge case for thumbnails that are off screen but scroll on
@@ -2526,6 +3283,7 @@
     }
 
     protected void onDismissAnimationEnds() {
+        AccessibilityManagerCompat.sendDismissAnimationEndsEventToTest(getContext());
     }
 
     public PendingAnimation createAllTasksDismissAnimation(long duration) {
@@ -2578,7 +3336,7 @@
 
     @UiThread
     private void dismissTask(int taskId) {
-        TaskView taskView = getTaskView(taskId);
+        TaskView taskView = getTaskViewByTaskId(taskId);
         if (taskView == null) {
             return;
         }
@@ -2587,7 +3345,7 @@
 
     public void dismissTask(TaskView taskView, boolean animateTaskView, boolean removeTask) {
         runDismissAnimation(createTaskDismissAnimation(taskView, animateTaskView, removeTask,
-                DISMISS_TASK_DURATION));
+                DISMISS_TASK_DURATION, false /* dismissingForSplitSelection*/));
     }
 
     @SuppressWarnings("unused")
@@ -2657,9 +3415,12 @@
         }
         alpha = Utilities.boundToRange(alpha, 0, 1);
         mContentAlpha = alpha;
+        int runningTaskId = getTaskIdsForRunningTaskView()[0];
         for (int i = getTaskViewCount() - 1; i >= 0; i--) {
             TaskView child = getTaskViewAt(i);
-            if (!mRunningTaskTileHidden || child.getTask().key.id != mRunningTaskId) {
+            int[] childTaskIds = child.getTaskIds();
+            if (!mRunningTaskTileHidden ||
+                    (childTaskIds[0] != runningTaskId && childTaskIds[1] != runningTaskId)) {
                 child.setStableAlpha(alpha);
             }
         }
@@ -2730,22 +3491,22 @@
 
     @Nullable
     public TaskView getNextTaskView() {
-        return getTaskViewAtByAbsoluteIndex(getRunningTaskIndex() + 1);
+        return getTaskViewAt(getRunningTaskIndex() + 1);
     }
 
     @Nullable
     public TaskView getCurrentPageTaskView() {
-        return getTaskViewAtByAbsoluteIndex(getCurrentPage());
+        return getTaskViewAt(getCurrentPage());
     }
 
     @Nullable
     public TaskView getNextPageTaskView() {
-        return getTaskViewAtByAbsoluteIndex(getNextPage());
+        return getTaskViewAt(getNextPage());
     }
 
     @Nullable
     public TaskView getTaskViewNearestToCenterOfScreen() {
-        return getTaskViewAtByAbsoluteIndex(getPageNearestToCenterOfScreen());
+        return getTaskViewAt(getPageNearestToCenterOfScreen());
     }
 
     /**
@@ -2753,16 +3514,8 @@
      */
     @Nullable
     public TaskView getTaskViewAt(int index) {
-        return getTaskViewAtByAbsoluteIndex(index + mTaskViewStartIndex);
-    }
-
-    @Nullable
-    private TaskView getTaskViewAtByAbsoluteIndex(int index) {
-        if (index < getChildCount() && index >= 0) {
-            View child = getChildAt(index);
-            return child instanceof TaskView ? (TaskView) child : null;
-        }
-        return null;
+        View child = getChildAt(index);
+        return child instanceof TaskView ? (TaskView) child : null;
     }
 
     public void setOnEmptyMessageUpdatedListener(OnEmptyMessageUpdatedListener listener) {
@@ -2788,6 +3541,14 @@
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        // If we're going to a state without overview panel, avoid unnecessary onLayout that
+        // cause TaskViews to re-arrange during animation to that state.
+        if (!mOverviewStateEnabled && !mFirstLayout) {
+            return;
+        }
+
+        mShowAsGridLastOnLayout = showAsGrid();
+
         super.onLayout(changed, left, top, right, bottom);
 
         updateEmptyStateUi(changed);
@@ -2802,6 +3563,9 @@
         mLastComputedTaskStartPushOutDistance = null;
         mLastComputedTaskEndPushOutDistance = null;
         updatePageOffsets();
+        runActionOnRemoteHandles(
+                remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
+                        .setScroll(getScrollOffset()));
         setImportantForAccessibility(isModal() ? IMPORTANT_FOR_ACCESSIBILITY_NO
                 : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
     }
@@ -2811,8 +3575,8 @@
         float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness);
         int count = getChildCount();
 
-        TaskView runningTask = mRunningTaskId == -1 || !mRunningTaskTileHidden
-                ? null : getTaskView(mRunningTaskId);
+        TaskView runningTask = mRunningTaskViewId == -1 || !mRunningTaskTileHidden
+                ? null : getRunningTaskView();
         int midpoint = runningTask == null ? -1 : indexOfChild(runningTask);
         int modalMidpoint = getCurrentPage();
 
@@ -2865,7 +3629,9 @@
             translationProperty.set(child, totalTranslation);
             if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
                     && i == getRunningTaskIndex()) {
-                mLiveTileTaskViewSimulator.taskPrimaryTranslation.value = totalTranslation;
+                runActionOnRemoteHandles(
+                        remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
+                                .taskPrimaryTranslation.value = totalTranslation);
                 redrawLiveTile();
             }
         }
@@ -2884,6 +3650,12 @@
             outRect.offset(taskView.getPersistentTranslationX(),
                     taskView.getPersistentTranslationY());
             outRect.top += mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
+
+            mTempMatrix.reset();
+            float persistentScale = taskView.getPersistentScale();
+            mTempMatrix.postScale(persistentScale, persistentScale,
+                    mIsRtl ? outRect.right : outRect.left, outRect.top);
+            mTempMatrix.mapRect(outRect);
         }
         outRect.offset(mOrientationHandler.getPrimaryValue(-midPointScroll, 0),
                 mOrientationHandler.getSecondaryValue(-midPointScroll, 0));
@@ -2965,7 +3737,15 @@
             TaskView task = getTaskViewAt(i);
             task.getTaskResistanceTranslationProperty().set(task, translation / getScaleY());
         }
-        mLiveTileTaskViewSimulator.recentsViewSecondaryTranslation.value = translation;
+        runActionOnRemoteHandles(
+                remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
+                        .recentsViewSecondaryTranslation.value = translation);
+    }
+
+    private void updateTaskViewsSnapshotRadius() {
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            getTaskViewAt(i).updateSnapshotRadius();
+        }
     }
 
     protected void setTaskViewsPrimarySplitTranslation(float translation) {
@@ -2979,12 +3759,59 @@
     protected void setTaskViewsSecondarySplitTranslation(float translation) {
         mTaskViewsSecondarySplitTranslation = translation;
         for (int i = 0; i < getTaskViewCount(); i++) {
-            TaskView task = getTaskViewAt(i);
-            task.getSecondarySplitTranslationProperty().set(task, translation);
+            TaskView taskView = getTaskViewAt(i);
+            if (taskView == mSplitHiddenTaskView) {
+                continue;
+            }
+            taskView.getSecondarySplitTranslationProperty().set(taskView, translation);
         }
     }
 
     /**
+     * Apply scroll offset to children of RecentsView when entering split select.
+     */
+    public void applySplitPrimaryScrollOffset() {
+        if (!mActivity.getDeviceProfile().isLandscape || !showAsGrid()) {
+            return;
+        }
+
+        @StagePosition int position = mSplitSelectStateController.getActiveSplitStagePosition();
+        boolean shouldShiftThumbnailsForSplitSelect = shouldShiftThumbnailsForSplitSelect(
+                position);
+        boolean expandLeft = false;
+        boolean expandRight = false;
+        if (mIsRtl) {
+            if (position == SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
+                    && shouldShiftThumbnailsForSplitSelect) {
+                expandLeft = true;
+            } else if (position == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
+                if (shouldShiftThumbnailsForSplitSelect) {
+                    expandRight = true;
+                } else {
+                    expandLeft = true;
+                }
+            }
+        } // TODO(b/200537659): Handle system RTL.
+        if (expandRight) {
+            for (int i = 0; i < getTaskViewCount(); i++) {
+                getTaskViewAt(i).setSplitScrollOffsetPrimary(mSplitPlaceholderSize);
+            }
+        } else if (expandLeft) {
+            mClearAllButton.setSplitSelectScrollOffsetPrimary(-mSplitPlaceholderSize);
+        }
+    }
+
+    /**
+     * Reset scroll offset on children of RecentsView when exiting split select.
+     */
+    public void resetSplitPrimaryScrollOffset() {
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            getTaskViewAt(i).setSplitScrollOffsetPrimary(0);
+        }
+        mClearAllButton.setSplitSelectScrollOffsetPrimary(0);
+    }
+
+    /**
      * Resets the visuals when exit modal state.
      */
     public void resetModalVisuals() {
@@ -2994,37 +3821,74 @@
         }
     }
 
-    public void initiateSplitSelect(TaskView taskView, SplitPositionOption splitPositionOption) {
+    public void initiateSplitSelect(TaskView taskView) {
+        int defaultSplitPosition = mOrientationHandler
+                .getDefaultSplitPosition(mActivity.getDeviceProfile());
+        initiateSplitSelect(taskView, defaultSplitPosition);
+    }
+
+    public void initiateSplitSelect(TaskView taskView, @StagePosition int stagePosition) {
         mSplitHiddenTaskView = taskView;
-        SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
         Rect initialBounds = new Rect(taskView.getLeft(), taskView.getTop(), taskView.getRight(),
                 taskView.getBottom());
-        splitController.setInitialTaskSelect(taskView, splitPositionOption, initialBounds);
+        mSplitSelectStateController.setInitialTaskSelect(taskView.getTask(),
+                stagePosition, initialBounds);
         mSplitHiddenTaskViewIndex = indexOfChild(taskView);
-        mSplitPlaceholderView.setLayoutParams(
-                splitController.getLayoutParamsForActivePosition(getResources(),
-                        mActivity.getDeviceProfile()));
-        mSplitPlaceholderView.setIcon(taskView.getIconView());
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            finishRecentsAnimation(true, null);
+        }
     }
 
     public PendingAnimation createSplitSelectInitAnimation() {
         int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext());
-        return createTaskDismissAnimation(mSplitHiddenTaskView, true, false, duration);
+        return createTaskDismissAnimation(mSplitHiddenTaskView, true, false, duration,
+                true /* dismissingForSplitSelection*/);
     }
 
     public void confirmSplitSelect(TaskView taskView) {
-        mSplitPlaceholderView.getSplitController().setSecondTaskId(taskView);
-        resetTaskVisuals();
-        setTranslationY(0);
+        mSplitToast.cancel();
+        RectF secondTaskStartingBounds = new RectF();
+        Rect secondTaskEndingBounds = new Rect();
+        // TODO(194414938) starting bounds seem slightly off, investigate
+        Rect firstTaskStartingBounds = new Rect();
+        Rect firstTaskEndingBounds = mTempRect;
+        int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext());
+        PendingAnimation pendingAnimation = new PendingAnimation(duration);
+
+        int halfDividerSize = getResources()
+                .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
+        mOrientationHandler.getFinalSplitPlaceholderBounds(halfDividerSize,
+                mActivity.getDeviceProfile(),
+                mSplitSelectStateController.getActiveSplitStagePosition(), firstTaskEndingBounds,
+                secondTaskEndingBounds);
+
+        mFirstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds);
+        mFirstFloatingTaskView.addAnimation(pendingAnimation,
+                new RectF(firstTaskStartingBounds), firstTaskEndingBounds, mFirstFloatingTaskView,
+                false /*fadeWithThumbnail*/);
+
+        mSecondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
+                taskView, secondTaskStartingBounds);
+        mSecondFloatingTaskView.setAlpha(1);
+        mSecondFloatingTaskView.addAnimation(pendingAnimation, secondTaskStartingBounds,
+                secondTaskEndingBounds, taskView.getThumbnail(),
+                true /*fadeWithThumbnail*/);
+        pendingAnimation.addEndListener(aBoolean ->
+                mSplitSelectStateController.setSecondTaskId(taskView.getTask(),
+                aBoolean1 -> RecentsView.this.resetFromSplitSelectionState()));
+        mSecondSplitHiddenTaskView = taskView;
+        taskView.setVisibility(INVISIBLE);
+        pendingAnimation.buildAnim().start();
     }
 
     public PendingAnimation cancelSplitSelect(boolean animate) {
-        SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
-        SplitPositionOption splitOption = splitController.getActiveSplitPositionOption();
+        SplitSelectStateController splitController = mSplitSelectStateController;
+        @StagePosition int stagePosition = splitController.getActiveSplitStagePosition();
         Rect initialBounds = splitController.getInitialBounds();
         splitController.resetState();
         int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext());
         PendingAnimation pendingAnim = new PendingAnimation(duration);
+        mSplitToast.cancel();
         if (!animate) {
             resetFromSplitSelectionState();
             return pendingAnim;
@@ -3046,7 +3910,7 @@
             if (child == mSplitHiddenTaskView) {
                 TaskView taskView = (TaskView) child;
 
-                int dir = mOrientationHandler.getSplitTaskViewDismissDirection(splitOption,
+                int dir = mOrientationHandler.getSplitTaskViewDismissDirection(stagePosition,
                         mActivity.getDeviceProfile());
                 FloatProperty<TaskView> dismissingTaskViewTranslate;
                 Rect hiddenBounds = new Rect(taskView.getLeft(), taskView.getTop(),
@@ -3102,9 +3966,9 @@
             pendingAnim.addOnFrameCallback(this::updateCurveProperties);
         }
 
-        pendingAnim.addListener(new AnimationSuccessListener() {
+        pendingAnim.addListener(new AnimatorListenerAdapter() {
             @Override
-            public void onAnimationSuccess(Animator animator) {
+            public void onAnimationEnd(Animator animation) {
                 // TODO(b/186800707) Figure out how to undo for grid view
                 //  Need to handle cases where dismissed task is
                 //  * Top Row
@@ -3118,10 +3982,9 @@
         return pendingAnim;
     }
 
+    /** TODO(b/181707736) More gracefully handle exiting split selection state */
     private void resetFromSplitSelectionState() {
-        mSplitHiddenTaskView.setTranslationY(0);
         if (!showAsGrid()) {
-            // TODO(b/186800707)
             int pageToSnapTo = mCurrentPage;
             if (mSplitHiddenTaskViewIndex <= pageToSnapTo) {
                 pageToSnapTo += 1;
@@ -3132,8 +3995,22 @@
         }
         onLayout(false /*  changed */, getLeft(), getTop(), getRight(), getBottom());
         resetTaskVisuals();
-        mSplitHiddenTaskView = null;
         mSplitHiddenTaskViewIndex = -1;
+        if (mSplitHiddenTaskView != null) {
+            mSplitHiddenTaskView.setTranslationY(0);
+            mSplitHiddenTaskView.setVisibility(VISIBLE);
+            mSplitHiddenTaskView = null;
+        }
+        if (mFirstFloatingTaskView != null) {
+            mActivity.getRootView().removeView(mFirstFloatingTaskView);
+            mFirstFloatingTaskView = null;
+        }
+        if (mSecondFloatingTaskView != null) {
+            mActivity.getRootView().removeView(mSecondFloatingTaskView);
+            mSecondFloatingTaskView = null;
+            mSecondSplitHiddenTaskView.setVisibility(VISIBLE);
+            mSecondSplitHiddenTaskView = null;
+        }
     }
 
     private void updateDeadZoneRects() {
@@ -3228,10 +4105,12 @@
             int runningTaskIndex = recentsView.getRunningTaskIndex();
             if (ENABLE_QUICKSTEP_LIVE_TILE.get() && runningTaskIndex != -1
                     && runningTaskIndex != taskIndex) {
-                anim.play(ObjectAnimator.ofFloat(
-                        recentsView.getLiveTileTaskViewSimulator().taskPrimaryTranslation,
-                        AnimatedFloat.VALUE,
-                        primaryTranslation));
+                for (RemoteTargetHandle remoteHandle : recentsView.getRemoteTargetHandles()) {
+                    anim.play(ObjectAnimator.ofFloat(
+                            remoteHandle.getTaskViewSimulator().taskPrimaryTranslation,
+                            AnimatedFloat.VALUE,
+                            primaryTranslation));
+                }
             }
 
             int otherAdjacentTaskIndex = centerTaskIndex + (centerTaskIndex - taskIndex);
@@ -3311,7 +4190,9 @@
         mPendingAnimation = new PendingAnimation(duration);
         mPendingAnimation.add(anim);
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            mLiveTileTaskViewSimulator.addOverviewToAppAnim(mPendingAnimation, interpolator);
+            runActionOnRemoteHandles(
+                    remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
+                            .addOverviewToAppAnim(mPendingAnimation, interpolator));
             mPendingAnimation.addOnFrameCallback(this::redrawLiveTile);
         }
         mPendingAnimation.addEndListener(isSuccess -> {
@@ -3344,6 +4225,7 @@
     @Override
     protected void notifyPageSwitchListener(int prevPage) {
         super.notifyPageSwitchListener(prevPage);
+        updateCurrentTaskActionsVisibility();
         loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
         updateEnabledOverlays();
     }
@@ -3402,30 +4284,63 @@
     }
 
     public void redrawLiveTile() {
-        if (mLiveTileParams.getTargetSet() != null) {
-            mLiveTileTaskViewSimulator.apply(mLiveTileParams);
-        }
+        runActionOnRemoteHandles(remoteTargetHandle -> {
+            TransformParams params = remoteTargetHandle.getTransformParams();
+            if (params.getTargetSet() != null) {
+                remoteTargetHandle.getTaskViewSimulator().apply(params);
+            }
+        });
     }
 
-    public TaskViewSimulator getLiveTileTaskViewSimulator() {
-        return mLiveTileTaskViewSimulator;
-    }
-
-    public TransformParams getLiveTileParams() {
-        return mLiveTileParams;
+    public RemoteTargetHandle[] getRemoteTargetHandles() {
+        return mRemoteTargetHandles;
     }
 
     // TODO: To be removed in a follow up CL
     public void setRecentsAnimationTargets(RecentsAnimationController recentsAnimationController,
             RecentsAnimationTargets recentsAnimationTargets) {
         mRecentsAnimationController = recentsAnimationController;
-        if (recentsAnimationTargets != null && recentsAnimationTargets.apps.length > 0) {
-            if (mSyncTransactionApplier != null) {
-                recentsAnimationTargets.addReleaseCheck(mSyncTransactionApplier);
-            }
-            mLiveTileTaskViewSimulator.setPreview(
-                    recentsAnimationTargets.apps[recentsAnimationTargets.apps.length - 1]);
-            mLiveTileParams.setTargetSet(recentsAnimationTargets);
+        if (recentsAnimationTargets == null || recentsAnimationTargets.apps.length == 0) {
+            return;
+        }
+
+        RemoteTargetGluer gluer = new RemoteTargetGluer(getContext(), getSizeStrategy());
+        mRemoteTargetHandles = gluer.assignTargetsForSplitScreen(recentsAnimationTargets);
+        mSplitBoundsConfig = gluer.getStagedSplitBounds();
+        if (mSyncTransactionApplier != null) {
+            // Add release check to the targets from the RemoteTargetGluer and not the targets
+            // passed in because in the event we're in split screen, we use the passed in targets
+            // to create new RemoteAnimationTargets in assignTargetsForSplitScreen(), and the
+            // mSyncTransactionApplier doesn't get transferred over
+            runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle
+                    .getTransformParams().getTargetSet()
+                    .addReleaseCheck(mSyncTransactionApplier));
+        }
+
+        TaskView runningTaskView = getRunningTaskView();
+        if (runningTaskView instanceof GroupedTaskView) {
+            // We initially create a GroupedTaskView in showCurrentTask() before launcher even
+            // receives the leashes for the remote apps, so the mSplitBoundsConfig that gets passed
+            // in there is either null or outdated, so we need to update here as soon as we're
+            // notified.
+            ((GroupedTaskView) runningTaskView).updateSplitBoundsConfig(mSplitBoundsConfig);
+        }
+        for (RemoteTargetHandle remoteTargetHandle : mRemoteTargetHandles) {
+            TaskViewSimulator tvs = remoteTargetHandle.getTaskViewSimulator();
+            tvs.setOrientationState(mOrientationState);
+            tvs.setDp(mActivity.getDeviceProfile());
+            tvs.recentsViewScale.value = 1;
+        }
+    }
+
+    /** Helper to avoid writing some for-loops to iterate over {@link #mRemoteTargetHandles} */
+    private void runActionOnRemoteHandles(Consumer<RemoteTargetHandle> consumer) {
+        if (mRemoteTargetHandles == null) {
+            return;
+        }
+
+        for (RemoteTargetHandle handle : mRemoteTargetHandles) {
+            consumer.accept(handle);
         }
     }
 
@@ -3435,6 +4350,8 @@
 
     public void finishRecentsAnimation(boolean toRecents, boolean shouldPip,
             Runnable onFinishComplete) {
+        // TODO(b/197232424#comment#10) Move this back into onRecentsAnimationComplete(). Maybe?
+        mRemoteTargetHandles = null;
         if (!toRecents && ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             // Reset the minimized state since we force-toggled the minimized state when entering
             // overview, but never actually finished the recents animation.  This is a catch all for
@@ -3492,14 +4409,27 @@
     }
 
     /**
-     * Updates page scroll synchronously and layout child views.
+     * Updates page scroll synchronously after measure and layout child views.
      */
     public void updateScrollSynchronously() {
+        // onMeasure is needed to update child's measured width which is used in scroll calculation,
+        // in case TaskView sizes has changed when being focused/unfocused.
+        onMeasure(makeMeasureSpec(getMeasuredWidth(), EXACTLY),
+                makeMeasureSpec(getMeasuredHeight(), EXACTLY));
         onLayout(false /*  changed */, getLeft(), getTop(), getRight(), getBottom());
         updateMinAndMaxScrollX();
     }
 
     @Override
+    protected void updateMinAndMaxScrollX() {
+        super.updateMinAndMaxScrollX();
+        if (DEBUG) {
+            Log.d(TAG, "updateMinAndMaxScrollX - mMinScroll: " + mMinScroll);
+            Log.d(TAG, "updateMinAndMaxScrollX - mMaxScroll: " + mMaxScroll);
+        }
+    }
+
+    @Override
     protected int computeMinScroll() {
         if (getTaskViewCount() > 0) {
             if (mIsRtl) {
@@ -3508,9 +4438,9 @@
                 return getScrollForPage(mDisallowScrollToClearAll ? indexOfChild(
                         getTaskViewAt(getTaskViewCount() - 1)) : indexOfChild(mClearAllButton));
             } else {
-                TaskView focusedTaskView = showAsGrid() ? getFocusedTaskView() : null;
+                TaskView focusedTaskView = mShowAsGridLastOnLayout ? getFocusedTaskView() : null;
                 return getScrollForPage(focusedTaskView != null ? indexOfChild(focusedTaskView)
-                        : mTaskViewStartIndex);
+                        : 0);
             }
         }
         return super.computeMinScroll();
@@ -3520,9 +4450,9 @@
     protected int computeMaxScroll() {
         if (getTaskViewCount() > 0) {
             if (mIsRtl) {
-                TaskView focusedTaskView = showAsGrid() ? getFocusedTaskView() : null;
+                TaskView focusedTaskView = mShowAsGridLastOnLayout ? getFocusedTaskView() : null;
                 return getScrollForPage(focusedTaskView != null ? indexOfChild(focusedTaskView)
-                        : mTaskViewStartIndex);
+                        : 0);
             } else {
                 // If we aren't showing the clear all button, use the leftmost task as the min
                 // scroll.
@@ -3557,22 +4487,38 @@
         }
 
         boolean pageScrollChanged = false;
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            float scrollDiff = 0;
-            if (child instanceof TaskView) {
-                scrollDiff = ((TaskView) child).getScrollAdjustment(showAsFullscreen, showAsGrid);
-            } else if (child instanceof ClearAllButton) {
-                scrollDiff = ((ClearAllButton) child).getScrollAdjustment(showAsFullscreen,
-                        showAsGrid);
-            }
 
-            final int pageScroll = newPageScrolls[i] + (int) scrollDiff;
+        int clearAllIndex = indexOfChild(mClearAllButton);
+        int clearAllScroll = 0;
+        int clearAllWidth = mOrientationHandler.getPrimarySize(mClearAllButton);
+        if (clearAllIndex != -1 && clearAllIndex < outPageScrolls.length) {
+            float scrollDiff = mClearAllButton.getScrollAdjustment(showAsFullscreen, showAsGrid);
+            clearAllScroll = newPageScrolls[clearAllIndex] + (int) scrollDiff;
+            if (outPageScrolls[clearAllIndex] != clearAllScroll) {
+                pageScrollChanged = true;
+                outPageScrolls[clearAllIndex] = clearAllScroll;
+            }
+        }
+
+        final int taskCount = getTaskViewCount();
+        for (int i = 0; i < taskCount; i++) {
+            TaskView taskView = getTaskViewAt(i);
+            float scrollDiff = taskView.getScrollAdjustment(showAsFullscreen, showAsGrid);
+            int pageScroll = newPageScrolls[i] + (int) scrollDiff;
+            if ((mIsRtl && pageScroll < clearAllScroll + clearAllWidth)
+                    || (!mIsRtl && pageScroll > clearAllScroll - clearAllWidth)) {
+                pageScroll = clearAllScroll + (mIsRtl ? clearAllWidth : -clearAllWidth);
+            }
             if (outPageScrolls[i] != pageScroll) {
                 pageScrollChanged = true;
                 outPageScrolls[i] = pageScroll;
             }
+            if (DEBUG) {
+                Log.d(TAG, "getPageScrolls - outPageScrolls[" + i + "]: " + outPageScrolls[i]);
+            }
+        }
+        if (DEBUG) {
+            Log.d(TAG, "getPageScrolls - clearAllScroll: " + clearAllScroll);
         }
         return pageScrollChanged;
     }
@@ -3593,7 +4539,7 @@
 
     @Override
     protected int getChildVisibleSize(int index) {
-        final TaskView taskView = getTaskViewAtByAbsoluteIndex(index);
+        final TaskView taskView = getTaskViewAt(index);
         if (taskView == null) {
             return super.getChildVisibleSize(index);
         }
@@ -3636,7 +4582,7 @@
      * according to {@link #mGridProgress}.
      */
     public float getGridTranslationSecondary(int pageIndex) {
-        TaskView taskView = getTaskViewAtByAbsoluteIndex(pageIndex);
+        TaskView taskView = getTaskViewAt(pageIndex);
         if (taskView == null) {
             return 0;
         }
@@ -3677,8 +4623,8 @@
     private void updateEnabledOverlays() {
         int overlayEnabledPage = mOverlayEnabled ? getNextPage() : -1;
         int taskCount = getTaskViewCount();
-        for (int i = mTaskViewStartIndex; i < mTaskViewStartIndex + taskCount; i++) {
-            getTaskViewAtByAbsoluteIndex(i).setOverlayEnabled(i == overlayEnabledPage);
+        for (int i = 0; i < taskCount; i++) {
+            getTaskViewAt(i).setOverlayEnabled(i == overlayEnabledPage);
         }
     }
 
@@ -3692,6 +4638,7 @@
     public void setOverviewGridEnabled(boolean overviewGridEnabled) {
         if (mOverviewGridEnabled != overviewGridEnabled) {
             mOverviewGridEnabled = overviewGridEnabled;
+            updateActionsViewFocusedScroll();
             // Request layout to ensure scroll position is recalculated with updated mGridProgress.
             requestLayout();
         }
@@ -3717,13 +4664,42 @@
             }
             return;
         }
-        switchToScreenshot(mRunningTaskId == -1 ? null
-                : mRecentsAnimationController.screenshotTask(mRunningTaskId), onFinishRunnable);
+
+        switchToScreenshotInternal(onFinishRunnable);
+    }
+
+    private void switchToScreenshotInternal(Runnable onFinishRunnable) {
+        TaskView taskView = getRunningTaskView();
+        if (taskView == null) {
+            onFinishRunnable.run();
+            return;
+        }
+
+        taskView.setShowScreenshot(true);
+        for (TaskView.TaskIdAttributeContainer container :
+                taskView.getTaskIdAttributeContainers()) {
+            if (container == null) {
+                continue;
+            }
+
+            ThumbnailData td =
+                    mRecentsAnimationController.screenshotTask(container.getTask().key.id);
+            TaskThumbnailView thumbnailView = container.getThumbnailView();
+            if (td != null) {
+                thumbnailView.setThumbnail(container.getTask(), td);
+            } else {
+                thumbnailView.refresh();
+            }
+        }
+        ViewUtils.postFrameDrawn(taskView, onFinishRunnable);
     }
 
     /**
      * Switch the current running task view to static snapshot mode, using the
      * provided thumbnail data as the snapshot.
+     * TODO(b/195609063) Consolidate this method w/ the one above, except this thumbnail data comes
+     *  from gesture state, which is a larger change of it having to keep track of multiple tasks.
+     *  OR. Maybe it doesn't need to pass in a thumbnail and we can use the exact same flow as above
      */
     public void switchToScreenshot(ThumbnailData thumbnailData, Runnable onFinishRunnable) {
         TaskView taskView = getRunningTaskView();
@@ -3751,10 +4727,9 @@
             getCurrentPageTaskView().setModalness(modalness);
         }
         // Only show actions view when it's modal for in-place landscape mode.
-        boolean inPlaceLandscape = !mOrientationState.canRecentsActivityRotate()
+        boolean inPlaceLandscape = !mOrientationState.isRecentsActivityRotationAllowed()
                 && mOrientationState.getTouchRotation() != ROTATION_0;
         mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION, modalness < 1 && inPlaceLandscape);
-        mActionsView.setTaskModalness(modalness);
     }
 
     @Nullable
@@ -3837,11 +4812,6 @@
                 && mCurrentGestureEndTarget != GestureState.GestureEndTarget.RECENTS;
     }
 
-    public boolean shouldShowOverviewActionsForState(STATE_TYPE state) {
-        return !state.displayOverviewTasksAsGrid(mActivity.getDeviceProfile())
-                || getFocusedTaskView() != null;
-    }
-
     /**
      * Used to register callbacks for when our empty message state changes.
      *
@@ -3882,7 +4852,8 @@
     }
 
     private void dispatchScrollChanged() {
-        mLiveTileTaskViewSimulator.setScroll(getScrollOffset());
+        runActionOnRemoteHandles(remoteTargetHandle ->
+                remoteTargetHandle.getTaskViewSimulator().setScroll(getScrollOffset()));
         for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
             mScrollListeners.get(i).onScrollChanged();
         }
@@ -3944,4 +4915,8 @@
         // Set locus context is a binder call, don't want it to happen during a transition
         UI_HELPER_EXECUTOR.post(() -> mActivity.setLocusContext(id, Bundle.EMPTY));
     }
+
+    public interface TaskLaunchListener {
+        void onTaskLaunched();
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
index bb8bc11..845e13e 100644
--- a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
@@ -22,7 +22,7 @@
 import android.view.Gravity;
 import android.widget.FrameLayout;
 
-import com.android.quickstep.util.SplitSelectStateController;
+import androidx.annotation.Nullable;
 
 public class SplitPlaceholderView extends FrameLayout {
 
@@ -40,29 +40,26 @@
                 }
             };
 
-    private SplitSelectStateController mSplitController;
-    private IconView mIcon;
+    private IconView mIconView;
 
     public SplitPlaceholderView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
-    public void init(SplitSelectStateController controller) {
-        this.mSplitController = controller;
+    @Nullable
+    public IconView getIconView() {
+        return mIconView;
     }
 
-    public SplitSelectStateController getSplitController() {
-        return mSplitController;
-    }
-
-    public void setIcon(IconView icon) {
-        if (mIcon == null) {
-            mIcon = new IconView(getContext());
-            addView(mIcon);
+    public void setIconView(IconView iconView, int iconSize) {
+        if (mIconView == null) {
+            mIconView = new IconView(getContext());
+            addView(mIconView);
         }
-        mIcon.setDrawable(icon.getDrawable());
-        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(icon.getLayoutParams());
+        mIconView.setDrawable(iconView.getDrawable());
+        mIconView.setDrawableSize(iconSize, iconSize);
+        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(iconView.getLayoutParams());
         params.gravity = Gravity.CENTER;
-        mIcon.setLayoutParams(params);
+        mIconView.setLayoutParams(params);
     }
 }
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index 906e854..03ab737 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -16,7 +16,8 @@
 
 package com.android.quickstep.views;
 
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
 import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
 
 import android.animation.Animator;
@@ -40,17 +41,18 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.TaskOverlayFactory;
 import com.android.quickstep.TaskUtils;
 import com.android.quickstep.util.TaskCornerRadius;
+import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
 
 /**
  * Contains options for a recent task when long-pressing its icon.
@@ -67,6 +69,7 @@
     private TextView mTaskName;
     private AnimatorSet mOpenCloseAnimator;
     private TaskView mTaskView;
+    private TaskIdAttributeContainer mTaskContainer;
     private LinearLayout mOptionLayout;
 
     public TaskMenuView(Context context, AttributeSet attrs) {
@@ -131,7 +134,8 @@
         // Inset due to margin
         PointF additionalInset = pagedOrientationHandler
                 .getAdditionalInsetForTaskMenu(mTaskInsetMargin);
-        int taskTopMargin = mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
+        DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+        int taskTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
 
         float adjustedY = y + taskTopMargin - additionalInset.y;
         float adjustedX = x - additionalInset.x;
@@ -139,7 +143,7 @@
         // NOTE: Changing the pivots means the rotated view gets rotated about the new pivots set,
         // which would render the X and Y position set here incorrect
         setPivotX(0);
-        if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
+        if (deviceProfile.overviewShowAsGrid) {
             // In tablet, set pivotY to original position without mThumbnailTopMargin adjustment.
             setPivotY(-taskTopMargin);
         } else {
@@ -147,9 +151,26 @@
         }
         setRotation(pagedOrientationHandler.getDegreesRotated());
         setX(pagedOrientationHandler.getTaskMenuX(adjustedX,
-                mTaskView.getThumbnail(), overscrollShift));
+                mTaskContainer.getThumbnailView(), overscrollShift, deviceProfile));
         setY(pagedOrientationHandler.getTaskMenuY(
-                adjustedY, mTaskView.getThumbnail(), overscrollShift));
+                adjustedY, mTaskContainer.getThumbnailView(), overscrollShift));
+
+        // TODO(b/193432925) temporary menu placement for split screen task menus
+        TaskIdAttributeContainer[] taskIdAttributeContainers =
+                mTaskView.getTaskIdAttributeContainers();
+        if (taskIdAttributeContainers[0].getStagePosition() != STAGE_POSITION_UNDEFINED) {
+            if (mTaskContainer.getStagePosition() != STAGE_POSITION_BOTTOM_OR_RIGHT) {
+                return;
+            }
+            Rect r = new Rect();
+            mTaskContainer.getThumbnailView().getBoundsOnScreen(r);
+            if (deviceProfile.isLandscape) {
+                setX(r.left);
+            } else {
+                setY(r.top);
+
+            }
+        }
     }
 
     public void onRotationChanged() {
@@ -164,19 +185,21 @@
         }
     }
 
-    public static boolean showForTask(TaskView taskView) {
-        BaseDraggingActivity activity = BaseDraggingActivity.fromContext(taskView.getContext());
+    public static boolean showForTask(TaskIdAttributeContainer taskContainer) {
+        BaseDraggingActivity activity = BaseDraggingActivity.fromContext(
+                taskContainer.getTaskView().getContext());
         final TaskMenuView taskMenuView = (TaskMenuView) activity.getLayoutInflater().inflate(
                         R.layout.task_menu, activity.getDragLayer(), false);
-        return taskMenuView.populateAndShowForTask(taskView);
+        return taskMenuView.populateAndShowForTask(taskContainer);
     }
 
-    private boolean populateAndShowForTask(TaskView taskView) {
+    private boolean populateAndShowForTask(TaskIdAttributeContainer taskContainer) {
         if (isAttachedToWindow()) {
             return false;
         }
         mActivity.getDragLayer().addView(this);
-        mTaskView = taskView;
+        mTaskView = taskContainer.getTaskView();
+        mTaskContainer = taskContainer;
         if (!populateAndLayoutMenu()) {
             return false;
         }
@@ -194,20 +217,20 @@
 
     /** @return true if successfully able to populate task view menu, false otherwise */
     private boolean populateAndLayoutMenu() {
-        if (mTaskView.getTask().icon == null) {
+        if (mTaskContainer.getTask().icon == null) {
             // Icon may not be loaded
             return false;
         }
-        addMenuOptions(mTaskView);
-        orientAroundTaskView(mTaskView);
+        addMenuOptions(mTaskContainer);
+        orientAroundTaskView(mTaskContainer);
         return true;
     }
 
-    private void addMenuOptions(TaskView taskView) {
-        mTaskName.setText(TaskUtils.getTitle(getContext(), taskView.getTask()));
+    private void addMenuOptions(TaskIdAttributeContainer taskContainer) {
+        mTaskName.setText(TaskUtils.getTitle(getContext(), taskContainer.getTask()));
         mTaskName.setOnClickListener(v -> close(true));
-        
-        TaskOverlayFactory.getEnabledShortcuts(taskView, mActivity.getDeviceProfile())
+        TaskOverlayFactory.getEnabledShortcuts(mTaskView, mActivity.getDeviceProfile(),
+                taskContainer)
                 .forEach(this::addMenuOption);
     }
 
@@ -219,39 +242,37 @@
         LayoutParams lp = (LayoutParams) menuOptionView.getLayoutParams();
         mTaskView.getPagedOrientationHandler().setLayoutParamsForTaskMenuOptionItem(lp,
                 menuOptionView, mActivity.getDeviceProfile());
-        menuOptionView.setEnabled(menuOption.isEnabled());
-        menuOptionView.setAlpha(menuOption.isEnabled() ? 1 : 0.5f);
-        menuOptionView.setOnClickListener(view -> {
-            if (ENABLE_QUICKSTEP_LIVE_TILE.get() && !menuOption.hasFinishRecentsInAction()) {
-                RecentsView recentsView = mTaskView.getRecentsView();
-                recentsView.switchToScreenshot(null,
-                        () -> recentsView.finishRecentsAnimation(true /* toRecents */,
-                                false /* shouldPip */,
-                                () -> menuOption.onClick(view)));
-            } else {
-                menuOption.onClick(view);
-            }
-        });
+        menuOptionView.setOnClickListener(menuOption::onClick);
         mOptionLayout.addView(menuOptionView);
     }
 
-    private void orientAroundTaskView(TaskView taskView) {
-        PagedOrientationHandler orientationHandler = taskView.getPagedOrientationHandler();
+    private void orientAroundTaskView(TaskIdAttributeContainer taskContainer) {
+        RecentsView recentsView = mActivity.getOverviewPanel();
+        PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
         measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
         orientationHandler.setTaskMenuAroundTaskView(this, mTaskInsetMargin);
 
         // Get Position
-        mActivity.getDragLayer().getDescendantRectRelativeToSelf(taskView, sTempRect);
+        DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+        mActivity.getDragLayer().getDescendantRectRelativeToSelf(mTaskView, sTempRect);
         Rect insets = mActivity.getDragLayer().getInsets();
         BaseDragLayer.LayoutParams params = (BaseDragLayer.LayoutParams) getLayoutParams();
         int padding = getResources()
                 .getDimensionPixelSize(R.dimen.task_menu_vertical_padding);
-        params.width = orientationHandler.getTaskMenuWidth(taskView.getThumbnail()) - (2 * padding);
+        if (deviceProfile.overviewShowAsGrid) {
+            // TODO(b/193432925) temporary so it doesn't look terrible on large screen
+            params.width =
+                    getContext().getResources().getDimensionPixelSize(R.dimen.task_menu_width_grid);
+        } else {
+            params.width = orientationHandler
+                    .getTaskMenuWidth(taskContainer.getThumbnailView(),
+                            deviceProfile) - (2 * padding);
+        }
         // Gravity set to Left instead of Start as sTempRect.left measures Left distance not Start
         params.gravity = Gravity.LEFT;
         setLayoutParams(params);
-        setScaleX(taskView.getScaleX());
-        setScaleY(taskView.getScaleY());
+        setScaleX(mTaskView.getScaleX());
+        setScaleY(mTaskView.getScaleY());
 
         // Set divider spacing
         ShapeDrawable divider = new ShapeDrawable(new RectShape());
@@ -260,7 +281,7 @@
         mOptionLayout.setShowDividers(SHOW_DIVIDER_MIDDLE);
 
         orientationHandler.setTaskOptionsMenuLayoutOrientation(
-                mActivity.getDeviceProfile(), mOptionLayout, dividerSpacing, divider);
+                deviceProfile, mOptionLayout, dividerSpacing, divider);
         setPosition(sTempRect.left - insets.left, sTempRect.top - insets.top, 0);
     }
 
@@ -284,7 +305,7 @@
         revealAnimator.setInterpolator(Interpolators.DEACCEL);
         mOpenCloseAnimator.playTogether(revealAnimator,
                 ObjectAnimator.ofFloat(
-                        mTaskView.getThumbnail(), DIM_ALPHA,
+                        mTaskContainer.getThumbnailView(), DIM_ALPHA,
                         closing ? 0 : TaskView.MAX_PAGE_SCRIM_ALPHA),
                 ObjectAnimator.ofFloat(this, ALPHA, closing ? 0 : 1));
         mOpenCloseAnimator.addListener(new AnimationSuccessListener() {
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index c70596d..a9db400 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -19,7 +19,6 @@
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
 
-import static com.android.launcher3.Utilities.comp;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
 
@@ -32,8 +31,6 @@
 import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Shader;
@@ -50,21 +47,17 @@
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.SystemUiController;
 import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
 import com.android.quickstep.views.TaskView.FullscreenDrawParams;
-import com.android.systemui.plugins.OverviewScreenshotActions;
-import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
 /**
  * A task in the Recents view.
  */
-public class TaskThumbnailView extends View implements PluginListener<OverviewScreenshotActions> {
+public class TaskThumbnailView extends View {
     private static final MainThreadInitializedObject<FullscreenDrawParams> TEMP_PARAMS =
             new MainThreadInitializedObject<>(FullscreenDrawParams::new);
 
@@ -85,7 +78,6 @@
     private TaskOverlay mOverlay;
     private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-    private final Paint mClearPaint = new Paint();
     private final Paint mDimmingPaintAfterClearing = new Paint();
     private final int mDimColor;
 
@@ -102,7 +94,6 @@
     private float mDimAlpha = 0f;
 
     private boolean mOverlayEnabled;
-    private OverviewScreenshotActions mOverviewScreenshotActionsPlugin;
 
     public TaskThumbnailView(Context context) {
         this(context, null);
@@ -116,7 +107,6 @@
         super(context, attrs, defStyleAttr);
         mPaint.setFilterBitmap(true);
         mBackgroundPaint.setColor(Color.WHITE);
-        mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
         mActivity = BaseActivity.fromContext(context);
         // Initialize with placeholder value. It is overridden later by TaskView
         mFullscreenParams = TEMP_PARAMS.get(context);
@@ -168,15 +158,15 @@
             mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
             mPaint.setShader(mBitmapShader);
             updateThumbnailMatrix();
+            if (mOverlayEnabled) {
+                getTaskOverlay().refreshActionVisibility(mThumbnailData);
+            }
         } else {
             mBitmapShader = null;
             mThumbnailData = null;
             mPaint.setShader(null);
             getTaskOverlay().reset();
         }
-        if (mOverviewScreenshotActionsPlugin != null) {
-            mOverviewScreenshotActionsPlugin.setupActions(getTaskView(), getThumbnail(), mActivity);
-        }
         updateThumbnailPaintFilter();
     }
 
@@ -214,10 +204,6 @@
             return Insets.NONE;
         }
 
-        if (!TaskView.CLIP_STATUS_AND_NAV_BARS) {
-            return Insets.NONE;
-        }
-
         RectF bitmapRect = new RectF(
                 0, 0,
                 mThumbnailData.thumbnail.getWidth(), mThumbnailData.thumbnail.getHeight());
@@ -231,11 +217,14 @@
         RectF boundsInBitmapSpace = new RectF();
         boundsToBitmapSpace.mapRect(boundsInBitmapSpace, viewRect);
 
-        return Insets.of(
-            Math.round(boundsInBitmapSpace.left),
-            Math.round(boundsInBitmapSpace.top),
-            Math.round(bitmapRect.right - boundsInBitmapSpace.right),
-            Math.round(bitmapRect.bottom - boundsInBitmapSpace.bottom));
+        DeviceProfile dp = mActivity.getDeviceProfile();
+        int leftInset = TaskView.clipLeft(dp) ? Math.round(boundsInBitmapSpace.left) : 0;
+        int topInset = TaskView.clipTop(dp) ? Math.round(boundsInBitmapSpace.top) : 0;
+        int rightInset = TaskView.clipRight(dp) ? Math.round(
+                bitmapRect.right - boundsInBitmapSpace.right) : 0;
+        int bottomInset = TaskView.clipBottom(dp)
+                ? Math.round(bitmapRect.bottom - boundsInBitmapSpace.bottom) : 0;
+        return Insets.of(leftInset, topInset, rightInset, bottomInset);
     }
 
 
@@ -269,33 +258,6 @@
         canvas.restore();
     }
 
-    @Override
-    public void onPluginConnected(OverviewScreenshotActions overviewScreenshotActions,
-            Context context) {
-        mOverviewScreenshotActionsPlugin = overviewScreenshotActions;
-        mOverviewScreenshotActionsPlugin.setupActions(getTaskView(), getThumbnail(), mActivity);
-    }
-
-    @Override
-    public void onPluginDisconnected(OverviewScreenshotActions plugin) {
-        if (mOverviewScreenshotActionsPlugin != null) {
-            mOverviewScreenshotActionsPlugin = null;
-        }
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        PluginManagerWrapper.INSTANCE.get(getContext())
-            .addPluginListener(this, OverviewScreenshotActions.class);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        PluginManagerWrapper.INSTANCE.get(getContext()).removePluginListener(this);
-    }
-
     public PreviewPositionHelper getPreviewPositionHelper() {
         return mPreviewPositionHelper;
     }
@@ -309,17 +271,7 @@
             float cornerRadius) {
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             if (mTask != null && getTaskView().isRunningTask() && !getTaskView().showScreenshot()) {
-                // TODO(b/189265196): Temporary fix to align the surface with the cutout perfectly.
-                // Round up only when the live tile task is displayed in Overview.
-                float rounding = comp(mFullscreenParams.mFullscreenProgress);
-                float left = x + rounding / 2;
-                float top = y + rounding / 2;
-                float right = width - rounding;
-                float bottom = height - rounding;
-
-                canvas.drawRoundRect(left, top, right, bottom, cornerRadius, cornerRadius,
-                        mClearPaint);
-                canvas.drawRoundRect(left, top, right, bottom, cornerRadius, cornerRadius,
+                canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius,
                         mDimmingPaintAfterClearing);
                 return;
             }
@@ -431,7 +383,9 @@
      */
     public static class PreviewPositionHelper {
 
-        // Contains the portion of the thumbnail that is clipped when fullscreen progress = 0.
+        private static final RectF EMPTY_RECT_F = new RectF();
+
+        // Contains the portion of the thumbnail that is unclipped when fullscreen progress = 1.
         private final RectF mClippedInsets = new RectF();
         private final Matrix mMatrix = new Matrix();
         private boolean mIsOrientationChanged;
@@ -451,8 +405,19 @@
 
             int thumbnailRotation = thumbnailData.rotation;
             int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
-            RectF thumbnailClipHint = TaskView.CLIP_STATUS_AND_NAV_BARS
-                    ? new RectF(thumbnailData.insets) : new RectF();
+            RectF thumbnailClipHint = new RectF();
+            if (TaskView.clipLeft(dp)) {
+                thumbnailClipHint.left = thumbnailData.insets.left;
+            }
+            if (TaskView.clipRight(dp)) {
+                thumbnailClipHint.right = thumbnailData.insets.right;
+            }
+            if (TaskView.clipTop(dp)) {
+                thumbnailClipHint.top = thumbnailData.insets.top;
+            }
+            if (TaskView.clipBottom(dp)) {
+                thumbnailClipHint.bottom = thumbnailData.insets.bottom;
+            }
 
             float scale = thumbnailData.scale;
             final float thumbnailScale;
@@ -461,7 +426,7 @@
             // Note: Disable rotation in grid layout.
             boolean windowingModeSupportsRotation = !dp.isMultiWindowMode
                     && thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN
-                    && !(dp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get());
+                    && !dp.overviewShowAsGrid;
             isOrientationDifferent = isOrientationChange(deltaRotate)
                     && windowingModeSupportsRotation;
             if (canvasWidth == 0 || canvasHeight == 0 || scale == 0) {
@@ -565,7 +530,7 @@
                         -thumbnailClipHint.left * scale,
                         -thumbnailClipHint.top * scale);
             } else {
-                setThumbnailRotation(deltaRotate, thumbnailClipHint, scale, thumbnailBounds);
+                setThumbnailRotation(deltaRotate, thumbnailClipHint, scale, thumbnailBounds, dp);
             }
 
             final float widthWithInsets;
@@ -610,7 +575,7 @@
         }
 
         private void setThumbnailRotation(int deltaRotate, RectF thumbnailInsets, float scale,
-                Rect thumbnailPosition) {
+                Rect thumbnailPosition, DeviceProfile dp) {
             float newLeftInset = 0;
             float newTopInset = 0;
             float translateX = 0;
@@ -636,15 +601,17 @@
                     break;
             }
             mClippedInsets.offsetTo(newLeftInset * scale, newTopInset * scale);
-            mMatrix.postTranslate(translateX - mClippedInsets.left,
-                    translateY - mClippedInsets.top);
+            mMatrix.postTranslate(translateX, translateY);
+            if (TaskView.useFullThumbnail(dp)) {
+                mMatrix.postTranslate(-mClippedInsets.left, -mClippedInsets.top);
+            }
         }
 
         /**
          * Insets to used for clipping the thumbnail (in case it is drawing outside its own space)
          */
-        public RectF getInsetsToDrawInFullscreen() {
-            return mClippedInsets;
+        public RectF getInsetsToDrawInFullscreen(DeviceProfile dp) {
+            return TaskView.useFullThumbnail(dp) ? mClippedInsets : EMPTY_RECT_F;
         }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 2d322e9..8d77e44 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -16,15 +16,6 @@
 
 package com.android.quickstep.views;
 
-import static android.view.Gravity.BOTTOM;
-import static android.view.Gravity.CENTER_HORIZONTAL;
-import static android.view.Gravity.CENTER_VERTICAL;
-import static android.view.Gravity.END;
-import static android.view.Gravity.START;
-import static android.view.Gravity.TOP;
-import static android.view.Surface.ROTATION_180;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
 import static android.widget.Toast.LENGTH_SHORT;
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
@@ -39,6 +30,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
@@ -58,7 +50,6 @@
 import android.util.FloatProperty;
 import android.util.Log;
 import android.view.MotionEvent;
-import android.view.Surface;
 import android.view.TouchDelegate;
 import android.view.View;
 import android.view.ViewGroup;
@@ -77,7 +68,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.statemanager.StatefulActivity;
@@ -87,11 +77,13 @@
 import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.util.TransformingTouchDelegate;
 import com.android.launcher3.util.ViewPool.Reusable;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.RemoteAnimationTargets;
+import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskIconCache;
 import com.android.quickstep.TaskOverlayFactory;
@@ -99,18 +91,23 @@
 import com.android.quickstep.TaskUtils;
 import com.android.quickstep.TaskViewUtils;
 import com.android.quickstep.util.CancellableTask;
+import com.android.quickstep.util.LauncherSplitScreenListener;
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.util.TaskCornerRadius;
+import com.android.quickstep.util.TransformParams;
 import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
 import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.lang.annotation.Retention;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.function.Consumer;
+import java.util.stream.Stream;
 
 /**
  * A task in the Recents view.
@@ -132,20 +129,43 @@
     @IntDef({FLAG_UPDATE_ALL, FLAG_UPDATE_ICON, FLAG_UPDATE_THUMBNAIL})
     public @interface TaskDataChanges {}
 
-    /**
-     * Should the layout account for space for a proactive action (or chip) to be added under
-     * the task.
-     */
-    public static final boolean SHOW_PROACTIVE_ACTIONS = false;
-
     /** The maximum amount that a task view can be scrimmed, dimmed or tinted. */
     public static final float MAX_PAGE_SCRIM_ALPHA = 0.4f;
 
     /**
-     * Should the TaskView display clip off the status and navigation bars in recents. When this
-     * is false the overview shows the whole screen scaled down instead.
+     * Should the TaskView display clip off the left inset in RecentsView.
      */
-    public static final boolean CLIP_STATUS_AND_NAV_BARS = false;
+    public static boolean clipLeft(DeviceProfile deviceProfile) {
+        return false;
+    }
+
+    /**
+     * Should the TaskView display clip off the top inset in RecentsView.
+     */
+    public static boolean clipTop(DeviceProfile deviceProfile) {
+        return false;
+    }
+
+    /**
+     * Should the TaskView display clip off the right inset in RecentsView.
+     */
+    public static boolean clipRight(DeviceProfile deviceProfile) {
+        return false;
+    }
+
+    /**
+     * Should the TaskView display clip off the bottom inset in RecentsView.
+     */
+    public static boolean clipBottom(DeviceProfile deviceProfile) {
+        return deviceProfile.isTablet;
+    }
+
+    /**
+     * Should the TaskView scale down to fit whole thumbnail in fullscreen.
+     */
+    public static boolean useFullThumbnail(DeviceProfile deviceProfile) {
+        return deviceProfile.isTablet && !deviceProfile.isTaskbarPresentInApps;
+    }
 
     private static final float EDGE_SCALE_DOWN_FACTOR_CAROUSEL = 0.03f;
     private static final float EDGE_SCALE_DOWN_FACTOR_GRID = 0.00f;
@@ -153,7 +173,7 @@
     public static final long SCALE_ICON_DURATION = 120;
     private static final long DIM_ANIM_DURATION = 700;
 
-    private static final Interpolator FULLSCREEN_INTERPOLATOR = ACCEL_DEACCEL;
+    private static final Interpolator GRID_INTERPOLATOR = ACCEL_DEACCEL;
 
     /**
      * This technically can be a vanilla {@link TouchDelegate} class, however that class requires
@@ -162,12 +182,11 @@
      * delegated bounds only to be updated.
      */
     private TransformingTouchDelegate mIconTouchDelegate;
-    private TransformingTouchDelegate mChipTouchDelegate;
 
     private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
             Collections.singletonList(new Rect());
 
-    private static final FloatProperty<TaskView> FOCUS_TRANSITION =
+    public static final FloatProperty<TaskView> FOCUS_TRANSITION =
             new FloatProperty<TaskView>("focusTransition") {
                 @Override
                 public void setValue(TaskView taskView, float v) {
@@ -284,69 +303,70 @@
                 }
             };
 
-    private static final FloatProperty<TaskView> FULLSCREEN_TRANSLATION_X =
-            new FloatProperty<TaskView>("fullscreenTranslationX") {
+    private static final FloatProperty<TaskView> NON_GRID_TRANSLATION_X =
+            new FloatProperty<TaskView>("nonGridTranslationX") {
                 @Override
                 public void setValue(TaskView taskView, float v) {
-                    taskView.setFullscreenTranslationX(v);
+                    taskView.setNonGridTranslationX(v);
                 }
 
                 @Override
                 public Float get(TaskView taskView) {
-                    return taskView.mFullscreenTranslationX;
+                    return taskView.mNonGridTranslationX;
                 }
             };
 
-    private static final FloatProperty<TaskView> FULLSCREEN_TRANSLATION_Y =
-            new FloatProperty<TaskView>("fullscreenTranslationY") {
+    private static final FloatProperty<TaskView> NON_GRID_TRANSLATION_Y =
+            new FloatProperty<TaskView>("nonGridTranslationY") {
                 @Override
                 public void setValue(TaskView taskView, float v) {
-                    taskView.setFullscreenTranslationY(v);
+                    taskView.setNonGridTranslationY(v);
                 }
 
                 @Override
                 public Float get(TaskView taskView) {
-                    return taskView.mFullscreenTranslationY;
+                    return taskView.mNonGridTranslationY;
                 }
             };
 
-    private static final FloatProperty<TaskView> NON_FULLSCREEN_TRANSLATION_X =
-            new FloatProperty<TaskView>("nonFullscreenTranslationX") {
+    public static final FloatProperty<TaskView> GRID_END_TRANSLATION_X =
+            new FloatProperty<TaskView>("gridEndTranslationX") {
                 @Override
                 public void setValue(TaskView taskView, float v) {
-                    taskView.setNonFullscreenTranslationX(v);
+                    taskView.setGridEndTranslationX(v);
                 }
 
                 @Override
                 public Float get(TaskView taskView) {
-                    return taskView.mNonFullscreenTranslationX;
+                    return taskView.mGridEndTranslationX;
                 }
             };
 
-    private static final FloatProperty<TaskView> NON_FULLSCREEN_TRANSLATION_Y =
-            new FloatProperty<TaskView>("nonFullscreenTranslationY") {
+    public static final FloatProperty<TaskView> SNAPSHOT_SCALE =
+            new FloatProperty<TaskView>("snapshotScale") {
                 @Override
                 public void setValue(TaskView taskView, float v) {
-                    taskView.setNonFullscreenTranslationY(v);
+                    taskView.setSnapshotScale(v);
                 }
 
                 @Override
                 public Float get(TaskView taskView) {
-                    return taskView.mNonFullscreenTranslationY;
+                    return taskView.mSnapshotView.getScaleX();
                 }
             };
 
     private final TaskOutlineProvider mOutlineProvider;
 
-    private Task mTask;
-    private TaskThumbnailView mSnapshotView;
-    private IconView mIconView;
+    protected Task mTask;
+    protected TaskThumbnailView mSnapshotView;
+    protected IconView mIconView;
     private final DigitalWellBeingToast mDigitalWellBeingToast;
     private float mFullscreenProgress;
     private float mGridProgress;
-    private float mFullscreenScale = 1;
+    private float mNonGridScale = 1;
+    private float mDismissScale = 1;
     private final FullscreenDrawParams mCurrentFullscreenParams;
-    private final StatefulActivity mActivity;
+    protected final StatefulActivity mActivity;
 
     // Various causes of changing primary translation, which we aggregate to setTranslationX/Y().
     private float mDismissTranslationX;
@@ -356,19 +376,20 @@
     private float mTaskResistanceTranslationX;
     private float mTaskResistanceTranslationY;
     // The following translation variables should only be used in the same orientation as Launcher.
-    private float mFullscreenTranslationX;
-    private float mFullscreenTranslationY;
-    // Applied as a complement to fullscreenTranslation, for adjusting the carousel overview, or the
-    // in transition carousel before forming the grid on tablets.
-    private float mNonFullscreenTranslationX;
-    private float mNonFullscreenTranslationY;
     private float mBoxTranslationY;
     // The following grid translations scales with mGridProgress.
     private float mGridTranslationX;
     private float mGridTranslationY;
+    // The following grid translation is used to animate closing the gap between grid and clear all.
+    private float mGridEndTranslationX;
+    // Applied as a complement to gridTranslation, for adjusting the carousel overview and quick
+    // switch.
+    private float mNonGridTranslationX;
+    private float mNonGridTranslationY;
     // Used when in SplitScreenSelectState
     private float mSplitSelectTranslationY;
     private float mSplitSelectTranslationX;
+    private float mSplitSelectScrollOffsetPrimary;
 
     private ObjectAnimator mIconAndDimAnimator;
     private float mIconScaleAnimStartProgress = 0;
@@ -376,6 +397,14 @@
     private float mModalness = 0;
     private float mStableAlpha = 1;
 
+    private int mTaskViewId = -1;
+    /**
+     * Index 0 will contain taskID of left/top task, index 1 will contain taskId of bottom/right
+     */
+    protected final int[] mTaskIdContainer = new int[]{-1, -1};
+    protected final TaskIdAttributeContainer[] mTaskIdAttributeContainer =
+            new TaskIdAttributeContainer[2];
+
     private boolean mShowScreenshot;
 
     // The current background requests to load the task thumbnail and icon
@@ -384,9 +413,7 @@
 
     private boolean mEndQuickswitchCuj;
 
-    private View mContextualChipWrapper;
     private final float[] mIconCenterCoords = new float[2];
-    private final float[] mChipCenterCoords = new float[2];
 
     private boolean mIsClickableAsLiveTile = true;
 
@@ -411,11 +438,22 @@
         setOutlineProvider(mOutlineProvider);
     }
 
+    public void setTaskViewId(int id) {
+        this.mTaskViewId = id;
+    }
+
+    public int getTaskViewId() {
+        return mTaskViewId;
+    }
+
     /**
      * Builds proto for logging
      */
     public WorkspaceItemInfo getItemInfo() {
-        final Task task = getTask();
+        return getItemInfo(mTask);
+    }
+
+    protected WorkspaceItemInfo getItemInfo(Task task) {
         ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(task.key);
         WorkspaceItemInfo stubInfo = new WorkspaceItemInfo();
         stubInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_TASK;
@@ -441,45 +479,25 @@
      */
     public boolean offerTouchToChildren(MotionEvent event) {
         if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            computeAndSetIconTouchDelegate();
-            computeAndSetChipTouchDelegate();
+            computeAndSetIconTouchDelegate(mIconView, mIconCenterCoords, mIconTouchDelegate);
         }
         if (mIconTouchDelegate != null && mIconTouchDelegate.onTouchEvent(event)) {
             return true;
         }
-        if (mChipTouchDelegate != null && mChipTouchDelegate.onTouchEvent(event)) {
-            return true;
-        }
         return false;
     }
 
-    private void computeAndSetIconTouchDelegate() {
-        float iconHalfSize = mIconView.getWidth() / 2f;
-        mIconCenterCoords[0] = mIconCenterCoords[1] = iconHalfSize;
-        getDescendantCoordRelativeToAncestor(mIconView, mActivity.getDragLayer(), mIconCenterCoords,
+    protected void computeAndSetIconTouchDelegate(IconView iconView, float[] tempCenterCoords,
+            TransformingTouchDelegate transformingTouchDelegate) {
+        float iconHalfSize = iconView.getWidth() / 2f;
+        tempCenterCoords[0] = tempCenterCoords[1] = iconHalfSize;
+        getDescendantCoordRelativeToAncestor(iconView, mActivity.getDragLayer(), tempCenterCoords,
                 false);
-        mIconTouchDelegate.setBounds(
-                (int) (mIconCenterCoords[0] - iconHalfSize),
-                (int) (mIconCenterCoords[1] - iconHalfSize),
-                (int) (mIconCenterCoords[0] + iconHalfSize),
-                (int) (mIconCenterCoords[1] + iconHalfSize));
-    }
-
-    private void computeAndSetChipTouchDelegate() {
-        if (mContextualChipWrapper != null) {
-            float chipHalfWidth = mContextualChipWrapper.getWidth() / 2f;
-            float chipHalfHeight = mContextualChipWrapper.getHeight() / 2f;
-            mChipCenterCoords[0] = chipHalfWidth;
-            mChipCenterCoords[1] = chipHalfHeight;
-            getDescendantCoordRelativeToAncestor(mContextualChipWrapper, mActivity.getDragLayer(),
-                    mChipCenterCoords,
-                    false);
-            mChipTouchDelegate.setBounds(
-                    (int) (mChipCenterCoords[0] - chipHalfWidth),
-                    (int) (mChipCenterCoords[1] - chipHalfHeight),
-                    (int) (mChipCenterCoords[0] + chipHalfWidth),
-                    (int) (mChipCenterCoords[1] + chipHalfHeight));
-        }
+        transformingTouchDelegate.setBounds(
+                (int) (tempCenterCoords[0] - iconHalfSize),
+                (int) (tempCenterCoords[1] - iconHalfSize),
+                (int) (tempCenterCoords[0] + iconHalfSize),
+                (int) (tempCenterCoords[1] + iconHalfSize));
     }
 
     /**
@@ -494,10 +512,6 @@
         }
         mModalness = modalness;
         mIconView.setAlpha(comp(modalness));
-        if (mContextualChipWrapper != null) {
-            mContextualChipWrapper.setScaleX(comp(modalness));
-            mContextualChipWrapper.setScaleY(comp(modalness));
-        }
         mDigitalWellBeingToast.updateBannerOffset(modalness,
                 mCurrentFullscreenParams.mCurrentDrawnInsets.top
                         + mCurrentFullscreenParams.mCurrentDrawnInsets.bottom);
@@ -516,22 +530,38 @@
     public void bind(Task task, RecentsOrientedState orientedState) {
         cancelPendingLoadTasks();
         mTask = task;
+        mTaskIdContainer[0] = mTask.key.id;
+        mTaskIdAttributeContainer[0] = new TaskIdAttributeContainer(task, mSnapshotView,
+                STAGE_POSITION_UNDEFINED);
         mSnapshotView.bind(task);
         setOrientationState(orientedState);
     }
 
+    public TaskIdAttributeContainer[] getTaskIdAttributeContainers() {
+        return mTaskIdAttributeContainer;
+    }
+
     public Task getTask() {
         return mTask;
     }
 
-    public boolean hasTaskId(int taskId) {
-        return mTask != null && mTask.key != null && mTask.key.id == taskId;
+    /**
+     * @return integer array of two elements to be size consistent with max number of tasks possible
+     *         index 0 will contain the taskId, index 1 will be -1 indicating a null taskID value
+     */
+    public int[] getTaskIds() {
+        return mTaskIdContainer;
     }
 
     public TaskThumbnailView getThumbnail() {
         return mSnapshotView;
     }
 
+    /** TODO(b/197033698) Remove all usages of above method and migrate to this one */
+    public TaskThumbnailView[] getThumbnails() {
+        return new TaskThumbnailView[]{mSnapshotView};
+    }
+
     public IconView getIconView() {
         return mIconView;
     }
@@ -540,6 +570,9 @@
         if (getTask() == null) {
             return;
         }
+        if (confirmSecondSplitSelectApp()) {
+            return;
+        }
         if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask()) {
             if (!mIsClickableAsLiveTile) {
                 return;
@@ -554,11 +587,32 @@
 
             mIsClickableAsLiveTile = false;
             RecentsView recentsView = getRecentsView();
-            final RemoteAnimationTargets targets = recentsView.getLiveTileParams().getTargetSet();
+            RemoteAnimationTargets targets;
+            RemoteTargetHandle[] remoteTargetHandles = recentsView.mRemoteTargetHandles;
+            if (remoteTargetHandles.length == 1) {
+                targets = remoteTargetHandles[0].getTransformParams().getTargetSet();
+            } else {
+                TransformParams topLeftParams = remoteTargetHandles[0].getTransformParams();
+                TransformParams rightBottomParams = remoteTargetHandles[1].getTransformParams();
+                RemoteAnimationTargetCompat[] apps = Stream.concat(
+                        Arrays.stream(topLeftParams.getTargetSet().apps),
+                        Arrays.stream(rightBottomParams.getTargetSet().apps))
+                        .toArray(RemoteAnimationTargetCompat[]::new);
+                RemoteAnimationTargetCompat[] wallpapers = Stream.concat(
+                        Arrays.stream(topLeftParams.getTargetSet().wallpapers),
+                        Arrays.stream(rightBottomParams.getTargetSet().wallpapers))
+                        .toArray(RemoteAnimationTargetCompat[]::new);
+                RemoteAnimationTargetCompat[] nonApps = Stream.concat(
+                        Arrays.stream(topLeftParams.getTargetSet().nonApps),
+                        Arrays.stream(rightBottomParams.getTargetSet().nonApps))
+                        .toArray(RemoteAnimationTargetCompat[]::new);
+                targets = new RemoteAnimationTargets(apps, wallpapers, nonApps,
+                        topLeftParams.getTargetSet().targetMode);
+            }
             if (targets == null) {
                 // If the recents animation is cancelled somehow between the parent if block and
                 // here, try to launch the task as a non live tile task.
-                launcherNonLiveTileTask();
+                launchTaskAnimated();
                 return;
             }
 
@@ -570,31 +624,29 @@
                     recentsView.getDepthController());
             anim.addListener(new AnimatorListenerAdapter() {
                 @Override
-                public void onAnimationStart(Animator animator) {
-                    recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(false);
-                }
-
-                @Override
                 public void onAnimationEnd(Animator animator) {
-                    recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(true);
                     mIsClickableAsLiveTile = true;
                 }
             });
             anim.start();
+            recentsView.onTaskLaunchedInLiveTileMode();
         } else {
-            launcherNonLiveTileTask();
+            launchTaskAnimated();
         }
         mActivity.getStatsLogManager().logger().withItemInfo(getItemInfo())
                 .log(LAUNCHER_TASK_LAUNCH_TAP);
     }
 
-    private void launcherNonLiveTileTask() {
-        if (mActivity.isInState(OVERVIEW_SPLIT_SELECT)) {
-            // User tapped to select second split screen app
+    /**
+     * @return {@code true} if user is already in split select mode and this tap was to choose the
+     *         second app. {@code false} otherwise
+     */
+    private boolean confirmSecondSplitSelectApp() {
+        boolean isSelectingSecondSplitApp = mActivity.isInState(OVERVIEW_SPLIT_SELECT);
+        if (isSelectingSecondSplitApp) {
             getRecentsView().confirmSplitSelect(this);
-        } else {
-            launchTaskAnimated();
         }
+        return isSelectingSecondSplitApp;
     }
 
     /**
@@ -606,13 +658,22 @@
             TestLogging.recordEvent(
                     TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
             ActivityOptionsWrapper opts =  mActivity.getActivityLaunchOptions(this, null);
+            opts.options.setLaunchDisplayId(getRootViewDisplayId());
+            boolean isOldTaskSplit = LauncherSplitScreenListener.INSTANCE.getNoCreate()
+                    .getPersistentSplitIds().length > 0;
             if (ActivityManagerWrapper.getInstance()
                     .startActivityFromRecents(mTask.key, opts.options)) {
-                if (ENABLE_QUICKSTEP_LIVE_TILE.get() && getRecentsView().getRunningTaskId() != -1) {
+                if (isOldTaskSplit) {
+                    SystemUiProxy.INSTANCE.getNoCreate().exitSplitScreen(mTask.key.id);
+                }
+                RecentsView recentsView = getRecentsView();
+                if (ENABLE_QUICKSTEP_LIVE_TILE.get() && recentsView.getRunningTaskViewId() != -1) {
+                    recentsView.onTaskLaunchedInLiveTileMode();
+
                     // Return a fresh callback in the live tile case, so that it's not accidentally
                     // triggered by QuickstepTransitionManager.AppLaunchAnimationRunner.
                     RunnableList callbackList = new RunnableList();
-                    getRecentsView().addSideTaskLaunchCallback(callbackList);
+                    recentsView.addSideTaskLaunchCallback(callbackList);
                     return callbackList;
                 }
                 return opts.onEndCallback;
@@ -643,6 +704,7 @@
             // Indicate success once the system has indicated that the transition has started
             ActivityOptions opts = ActivityOptionsCompat.makeCustomAnimation(
                     getContext(), 0, 0, () -> callback.accept(true), MAIN_EXECUTOR.getHandler());
+            opts.setLaunchDisplayId(getRootViewDisplayId());
             if (freezeTaskList) {
                 ActivityOptionsCompat.setFreezeRecentTasksList(opts);
             }
@@ -696,7 +758,7 @@
             if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
                 mIconLoadRequest = iconCache.updateIconInBackground(mTask,
                         (task) -> {
-                            setIcon(task.icon);
+                            setIcon(mIconView, task.icon);
                             mDigitalWellBeingToast.initialize(mTask);
                         });
             }
@@ -708,16 +770,16 @@
                 mTask.thumbnail = null;
             }
             if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
-                setIcon(null);
+                setIcon(mIconView, null);
             }
         }
     }
 
-    private boolean needsUpdate(@TaskDataChanges int dataChange, @TaskDataChanges int flag) {
+    protected boolean needsUpdate(@TaskDataChanges int dataChange, @TaskDataChanges int flag) {
         return (dataChange & flag) == flag;
     }
 
-    private void cancelPendingLoadTasks() {
+    protected void cancelPendingLoadTasks() {
         if (mThumbnailLoadRequest != null) {
             mThumbnailLoadRequest.cancel();
             mThumbnailLoadRequest = null;
@@ -728,34 +790,52 @@
         }
     }
 
-    private boolean showTaskMenu() {
+    private boolean showTaskMenu(IconView iconView) {
         if (getRecentsView().mActivity.isInState(OVERVIEW_SPLIT_SELECT)) {
             // Don't show menu when selecting second split screen app
             return true;
         }
 
-        if (!getRecentsView().isClearAllHidden()) {
+        if (!mActivity.getDeviceProfile().overviewShowAsGrid
+                && !getRecentsView().isClearAllHidden()) {
             getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
             return false;
         } else {
             mActivity.getStatsLogManager().logger().withItemInfo(getItemInfo())
                     .log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS);
-            return TaskMenuView.showForTask(this);
+            return showTaskMenuWithContainer(iconView);
         }
     }
 
-    private void setIcon(Drawable icon) {
+    protected boolean showTaskMenuWithContainer(IconView iconView) {
+        return TaskMenuView.showForTask(mTaskIdAttributeContainer[0]);
+    }
+
+    protected void setIcon(IconView iconView, Drawable icon) {
         if (icon != null) {
-            mIconView.setDrawable(icon);
-            mIconView.setOnClickListener(v -> showTaskMenu());
-            mIconView.setOnLongClickListener(v -> {
+            iconView.setDrawable(icon);
+            iconView.setOnClickListener(v -> {
+                if (confirmSecondSplitSelectApp()) {
+                    return;
+                }
+                if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask()) {
+                    RecentsView recentsView = getRecentsView();
+                    recentsView.switchToScreenshot(
+                            () -> recentsView.finishRecentsAnimation(true /* toRecents */,
+                                    false /* shouldPip */,
+                                    () -> showTaskMenu(iconView)));
+                } else {
+                    showTaskMenu(iconView);
+                }
+            });
+            iconView.setOnLongClickListener(v -> {
                 requestDisallowInterceptTouchEvent(true);
-                return showTaskMenu();
+                return showTaskMenu(iconView);
             });
         } else {
-            mIconView.setDrawable(null);
-            mIconView.setOnClickListener(null);
-            mIconView.setOnLongClickListener(null);
+            iconView.setDrawable(null);
+            iconView.setOnClickListener(null);
+            iconView.setOnLongClickListener(null);
         }
     }
 
@@ -765,42 +845,24 @@
         LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
         DeviceProfile deviceProfile = mActivity.getDeviceProfile();
         snapshotParams.topMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
-        int taskIconMargin = deviceProfile.overviewTaskMarginPx;
+        boolean isGridTask = deviceProfile.overviewShowAsGrid && !isFocusedTask();
         int taskIconHeight = deviceProfile.overviewTaskIconSizePx;
+        int taskMargin = isGridTask ? deviceProfile.overviewTaskMarginGridPx
+                : deviceProfile.overviewTaskMarginPx;
+        int taskIconMargin = snapshotParams.topMargin - taskIconHeight - taskMargin;
         LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
-        switch (orientationHandler.getRotation()) {
-            case ROTATION_90:
-                iconParams.gravity = (isRtl ? START : END) | CENTER_VERTICAL;
-                iconParams.rightMargin = -taskIconHeight - taskIconMargin / 2;
-                iconParams.leftMargin = 0;
-                iconParams.topMargin = snapshotParams.topMargin / 2;
-                break;
-            case ROTATION_180:
-                iconParams.gravity = BOTTOM | CENTER_HORIZONTAL;
-                iconParams.bottomMargin = -snapshotParams.topMargin;
-                iconParams.leftMargin = iconParams.rightMargin = 0;
-                iconParams.topMargin = taskIconMargin;
-                break;
-            case ROTATION_270:
-                iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL;
-                iconParams.leftMargin = -taskIconHeight - taskIconMargin / 2;
-                iconParams.rightMargin = 0;
-                iconParams.topMargin = snapshotParams.topMargin / 2;
-                break;
-            case Surface.ROTATION_0:
-            default:
-                iconParams.gravity = TOP | CENTER_HORIZONTAL;
-                iconParams.leftMargin = iconParams.rightMargin = 0;
-                iconParams.topMargin = taskIconMargin;
-                break;
-        }
+        orientationHandler.setIconAndSnapshotParams(mIconView, taskIconMargin, taskIconHeight,
+                snapshotParams, isRtl);
         mSnapshotView.setLayoutParams(snapshotParams);
         iconParams.width = iconParams.height = taskIconHeight;
         mIconView.setLayoutParams(iconParams);
         mIconView.setRotation(orientationHandler.getDegreesRotated());
+        int iconDrawableSize = isGridTask ? deviceProfile.overviewTaskIconDrawableSizeGridPx
+                : deviceProfile.overviewTaskIconDrawableSizePx;
+        mIconView.setDrawableSize(iconDrawableSize, iconDrawableSize);
         snapshotParams.topMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
         mSnapshotView.setLayoutParams(snapshotParams);
-        getThumbnail().getTaskOverlay().updateOrientationState(orientationState);
+        mSnapshotView.getTaskOverlay().updateOrientationState(orientationState);
     }
 
     private void setIconAndDimTransitionProgress(float progress, boolean invert) {
@@ -814,11 +876,6 @@
         float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, lowerClamp, upperClamp)
                 .getInterpolation(progress);
         mIconView.setAlpha(scale);
-        if (mContextualChipWrapper != null && mContextualChipWrapper != null) {
-            mContextualChipWrapper.setAlpha(scale);
-            mContextualChipWrapper.setScaleX(Math.min(scale, comp(mModalness)));
-            mContextualChipWrapper.setScaleY(Math.min(scale, comp(mModalness)));
-        }
         mDigitalWellBeingToast.updateBannerOffset(1f - scale,
                 mCurrentFullscreenParams.mCurrentDrawnInsets.top
                         + mCurrentFullscreenParams.mCurrentDrawnInsets.bottom);
@@ -855,13 +912,20 @@
         setIconAndDimTransitionProgress(iconScale, invert);
     }
 
+    protected void resetPersistentViewTransforms() {
+        mNonGridTranslationX = mNonGridTranslationY =
+                mGridTranslationX = mGridTranslationY = mBoxTranslationY = 0f;
+        resetViewTransforms();
+    }
+
     protected void resetViewTransforms() {
         // fullscreenTranslation and accumulatedTranslation should not be reset, as
         // resetViewTransforms is called during Quickswitch scrolling.
-        mDismissTranslationX = mTaskOffsetTranslationX = mTaskResistanceTranslationX =
-                mSplitSelectTranslationX = 0f;
+        mDismissTranslationX = mTaskOffsetTranslationX =
+                mTaskResistanceTranslationX = mSplitSelectTranslationX = mGridEndTranslationX = 0f;
         mDismissTranslationY = mTaskOffsetTranslationY = mTaskResistanceTranslationY =
                 mSplitSelectTranslationY = 0f;
+        setSnapshotScale(1f);
         applyTranslationX();
         applyTranslationY();
         setTranslationZ(0);
@@ -877,10 +941,7 @@
 
     @Override
     public void onRecycle() {
-        mFullscreenTranslationX = mFullscreenTranslationY = mNonFullscreenTranslationX =
-                mNonFullscreenTranslationY = mGridTranslationX = mGridTranslationY =
-                        mBoxTranslationY = 0f;
-        resetViewTransforms();
+        resetPersistentViewTransforms();
         // Clear any references to the thumbnail (it will be re-read either from the cache or the
         // system on next bind)
         mSnapshotView.setThumbnail(mTask, null);
@@ -888,57 +949,14 @@
         onTaskListVisibilityChanged(false);
     }
 
-    /**
-     * Sets the contextual chip.
-     *
-     * @param view Wrapper view containing contextual chip.
-     */
-    public void setContextualChip(View view) {
-        if (mContextualChipWrapper != null) {
-            removeView(mContextualChipWrapper);
-        }
-        if (view != null) {
-            mContextualChipWrapper = view;
-            LayoutParams layoutParams = new LayoutParams(((View) getParent()).getMeasuredWidth(),
-                    LayoutParams.WRAP_CONTENT);
-            layoutParams.gravity = BOTTOM | CENTER_HORIZONTAL;
-            int expectedChipHeight = getExpectedViewHeight(view);
-            float chipOffset = getResources().getDimension(R.dimen.chip_hint_vertical_offset);
-            layoutParams.bottomMargin = -expectedChipHeight - (int) chipOffset;
-            mContextualChipWrapper.setScaleX(0f);
-            mContextualChipWrapper.setScaleY(0f);
-            addView(view, getChildCount(), layoutParams);
-            if (mContextualChipWrapper != null) {
-                float scale = comp(mModalness);
-                mContextualChipWrapper.animate().scaleX(scale).scaleY(scale).setDuration(50);
-                mChipTouchDelegate = new TransformingTouchDelegate(mContextualChipWrapper);
-            }
-        }
-    }
-
     public float getTaskCornerRadius() {
         return TaskCornerRadius.get(mActivity);
     }
 
-    /**
-     * Clears the contextual chip from TaskView.
-     *
-     * @return The contextual chip wrapper view to be recycled.
-     */
-    public View clearContextualChip() {
-        if (mContextualChipWrapper != null) {
-            removeView(mContextualChipWrapper);
-        }
-        View oldContextualChipWrapper = mContextualChipWrapper;
-        mContextualChipWrapper = null;
-        mChipTouchDelegate = null;
-        return oldContextualChipWrapper;
-    }
-
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
-        if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
+        if (mActivity.getDeviceProfile().overviewShowAsGrid) {
             setPivotX(getLayoutDirection() == LAYOUT_DIRECTION_RTL ? 0 : right - left);
             setPivotY(mSnapshotView.getTop());
         } else {
@@ -955,20 +973,22 @@
      * How much to scale down pages near the edge of the screen.
      */
     public static float getEdgeScaleDownFactor(DeviceProfile deviceProfile) {
-        if (deviceProfile.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
-            return EDGE_SCALE_DOWN_FACTOR_GRID;
-        } else {
-            return EDGE_SCALE_DOWN_FACTOR_CAROUSEL;
-        }
+        return deviceProfile.overviewShowAsGrid ? EDGE_SCALE_DOWN_FACTOR_GRID
+                : EDGE_SCALE_DOWN_FACTOR_CAROUSEL;
     }
 
-    private void setFullscreenScale(float fullscreenScale) {
-        mFullscreenScale = fullscreenScale;
+    private void setNonGridScale(float nonGridScale) {
+        mNonGridScale = nonGridScale;
         applyScale();
     }
 
-    public float getFullscreenScale() {
-        return mFullscreenScale;
+    public float getNonGridScale() {
+        return mNonGridScale;
+    }
+
+    private void setSnapshotScale(float dismissScale) {
+        mDismissScale = dismissScale;
+        applyScale();
     }
 
     /**
@@ -985,10 +1005,22 @@
 
     private void applyScale() {
         float scale = 1;
-        float fullScreenProgress = FULLSCREEN_INTERPOLATOR.getInterpolation(mFullscreenProgress);
-        scale *= Utilities.mapRange(fullScreenProgress, 1f, mFullscreenScale);
+        scale *= getPersistentScale();
+        scale *= mDismissScale;
         setScaleX(scale);
         setScaleY(scale);
+        updateSnapshotRadius();
+    }
+
+    /**
+     * Returns multiplication of scale that is persistent (e.g. fullscreen and grid), and does not
+     * change according to a temporary state.
+     */
+    public float getPersistentScale() {
+        float scale = 1;
+        float gridProgress = GRID_INTERPOLATOR.getInterpolation(mGridProgress);
+        scale *= Utilities.mapRange(gridProgress, mNonGridScale, 1f);
+        return scale;
     }
 
     private void setSplitSelectTranslationX(float x) {
@@ -1000,6 +1032,11 @@
         mSplitSelectTranslationY = y;
         applyTranslationY();
     }
+
+    public void setSplitScrollOffsetPrimary(float splitSelectScrollOffsetPrimary) {
+        mSplitSelectScrollOffsetPrimary = splitSelectScrollOffsetPrimary;
+    }
+
     private void setDismissTranslationX(float x) {
         mDismissTranslationX = x;
         applyTranslationX();
@@ -1030,23 +1067,13 @@
         applyTranslationY();
     }
 
-    private void setFullscreenTranslationX(float fullscreenTranslationX) {
-        mFullscreenTranslationX = fullscreenTranslationX;
+    private void setNonGridTranslationX(float nonGridTranslationX) {
+        mNonGridTranslationX = nonGridTranslationX;
         applyTranslationX();
     }
 
-    private void setFullscreenTranslationY(float fullscreenTranslationY) {
-        mFullscreenTranslationY = fullscreenTranslationY;
-        applyTranslationY();
-    }
-
-    private void setNonFullscreenTranslationX(float nonFullscreenTranslationX) {
-        mNonFullscreenTranslationX = nonFullscreenTranslationX;
-        applyTranslationX();
-    }
-
-    private void setNonFullscreenTranslationY(float nonFullscreenTranslationY) {
-        mNonFullscreenTranslationY = nonFullscreenTranslationY;
+    private void setNonGridTranslationY(float nonGridTranslationY) {
+        mNonGridTranslationY = nonGridTranslationY;
         applyTranslationY();
     }
 
@@ -1068,16 +1095,19 @@
         return mGridTranslationY;
     }
 
+    private void setGridEndTranslationX(float gridEndTranslationX) {
+        mGridEndTranslationX = gridEndTranslationX;
+        applyTranslationX();
+    }
+
     public float getScrollAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
         float scrollAdjustment = 0;
-        if (fullscreenEnabled) {
-            scrollAdjustment += getPrimaryFullscreenTranslationProperty().get(this);
-        } else {
-            scrollAdjustment += getPrimaryNonFullscreenTranslationProperty().get(this);
-        }
         if (gridEnabled) {
             scrollAdjustment += mGridTranslationX;
+        } else {
+            scrollAdjustment += getPrimaryNonGridTranslationProperty().get(this);
         }
+        scrollAdjustment += mSplitSelectScrollOffsetPrimary;
         return scrollAdjustment;
     }
 
@@ -1088,7 +1118,7 @@
     public float getSizeAdjustment(boolean fullscreenEnabled) {
         float sizeAdjustment = 1;
         if (fullscreenEnabled) {
-            sizeAdjustment *= mFullscreenScale;
+            sizeAdjustment *= mNonGridScale;
         }
         return sizeAdjustment;
     }
@@ -1100,7 +1130,7 @@
 
     private void applyTranslationX() {
         setTranslationX(mDismissTranslationX + mTaskOffsetTranslationX + mTaskResistanceTranslationX
-                + mSplitSelectTranslationX + getPersistentTranslationX());
+                + mSplitSelectTranslationX + mGridEndTranslationX + getPersistentTranslationX());
     }
 
     private void applyTranslationY() {
@@ -1113,9 +1143,7 @@
      * change according to a temporary state (e.g. task offset).
      */
     public float getPersistentTranslationX() {
-        return getFullscreenTrans(mFullscreenTranslationX)
-                + getNonFullscreenTrans(mNonFullscreenTranslationX)
-                + getGridTrans(mGridTranslationX);
+        return getNonGridTrans(mNonGridTranslationX) + getGridTrans(mGridTranslationX);
     }
 
     /**
@@ -1124,8 +1152,7 @@
      */
     public float getPersistentTranslationY() {
         return mBoxTranslationY
-                + getFullscreenTrans(mFullscreenTranslationY)
-                + getNonFullscreenTrans(mNonFullscreenTranslationY)
+                + getNonGridTrans(mNonGridTranslationY)
                 + getGridTrans(mGridTranslationY);
     }
 
@@ -1159,24 +1186,14 @@
                 TASK_RESISTANCE_TRANSLATION_X, TASK_RESISTANCE_TRANSLATION_Y);
     }
 
-    public FloatProperty<TaskView> getPrimaryFullscreenTranslationProperty() {
+    public FloatProperty<TaskView> getPrimaryNonGridTranslationProperty() {
         return getPagedOrientationHandler().getPrimaryValue(
-                FULLSCREEN_TRANSLATION_X, FULLSCREEN_TRANSLATION_Y);
+                NON_GRID_TRANSLATION_X, NON_GRID_TRANSLATION_Y);
     }
 
-    public FloatProperty<TaskView> getSecondaryFullscreenTranslationProperty() {
+    public FloatProperty<TaskView> getSecondaryNonGridTranslationProperty() {
         return getPagedOrientationHandler().getSecondaryValue(
-                FULLSCREEN_TRANSLATION_X, FULLSCREEN_TRANSLATION_Y);
-    }
-
-    public FloatProperty<TaskView> getPrimaryNonFullscreenTranslationProperty() {
-        return getPagedOrientationHandler().getPrimaryValue(
-                NON_FULLSCREEN_TRANSLATION_X, NON_FULLSCREEN_TRANSLATION_Y);
-    }
-
-    public FloatProperty<TaskView> getSecondaryNonFullscreenTranslationProperty() {
-        return getPagedOrientationHandler().getSecondaryValue(
-                NON_FULLSCREEN_TRANSLATION_X, NON_FULLSCREEN_TRANSLATION_Y);
+                NON_GRID_TRANSLATION_X, NON_GRID_TRANSLATION_Y);
     }
 
     @Override
@@ -1242,8 +1259,9 @@
                         getContext().getText(R.string.accessibility_close)));
 
         final Context context = getContext();
+        // TODO(b/200609838) Determine which task to run A11y action on when in split screen
         for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this,
-                mActivity.getDeviceProfile())) {
+                mActivity.getDeviceProfile(), mTaskIdAttributeContainer[0])) {
             info.addAction(s.createAccessibilityAction(context));
         }
 
@@ -1275,8 +1293,9 @@
             return true;
         }
 
+        // TODO(b/200609838) Determine which task to run A11y action on when in split screen
         for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this,
-                mActivity.getDeviceProfile())) {
+                mActivity.getDeviceProfile(), mTaskIdAttributeContainer[0])) {
             if (s.hasHandlerForAction(action)) {
                 s.onClick(this);
                 return true;
@@ -1312,37 +1331,27 @@
         progress = Utilities.boundToRange(progress, 0, 1);
         mFullscreenProgress = progress;
         mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
-        getThumbnail().getTaskOverlay().setFullscreenProgress(progress);
+        mSnapshotView.getTaskOverlay().setFullscreenProgress(progress);
 
-        applyTranslationX();
-        applyTranslationY();
-        applyScale();
+        updateSnapshotRadius();
 
-        TaskThumbnailView thumbnail = getThumbnail();
-        updateCurrentFullscreenParams(thumbnail.getPreviewPositionHelper());
-
-        if (!getRecentsView().isTaskIconScaledDown(this)) {
-            // Some of the items in here are dependent on the current fullscreen params, but don't
-            // update them if the icon is supposed to be scaled down.
-            setIconScaleAndDim(progress, true /* invert */);
-        }
-
-        thumbnail.setFullscreenParams(mCurrentFullscreenParams);
         mOutlineProvider.updateParams(
                 mCurrentFullscreenParams,
                 mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx);
         invalidateOutline();
     }
 
+    void updateSnapshotRadius() {
+        updateCurrentFullscreenParams(mSnapshotView.getPreviewPositionHelper());
+        mSnapshotView.setFullscreenParams(mCurrentFullscreenParams);
+    }
+
     void updateCurrentFullscreenParams(PreviewPositionHelper previewPositionHelper) {
         if (getRecentsView() == null) {
             return;
         }
-        mCurrentFullscreenParams.setProgress(
-                mFullscreenProgress,
-                getRecentsView().getScaleX(),
-                getWidth(), mActivity.getDeviceProfile(),
-                previewPositionHelper);
+        mCurrentFullscreenParams.setProgress(mFullscreenProgress, getRecentsView().getScaleX(),
+                getScaleX(), getWidth(), mActivity.getDeviceProfile(), previewPositionHelper);
     }
 
     /**
@@ -1351,69 +1360,49 @@
      */
     void updateTaskSize() {
         ViewGroup.LayoutParams params = getLayoutParams();
-        float fullscreenScale;
+        float nonGridScale;
         float boxTranslationY;
         int expectedWidth;
         int expectedHeight;
-        if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
-            final int thumbnailPadding =
-                    mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
+        DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+        if (deviceProfile.overviewShowAsGrid) {
+            final int thumbnailPadding = deviceProfile.overviewTaskThumbnailTopMarginPx;
             final Rect lastComputedTaskSize = getRecentsView().getLastComputedTaskSize();
             final int taskWidth = lastComputedTaskSize.width();
             final int taskHeight = lastComputedTaskSize.height();
 
             int boxWidth;
             int boxHeight;
-            float thumbnailRatio;
             boolean isFocusedTask = isFocusedTask();
             if (isFocusedTask) {
                 // Task will be focused and should use focused task size. Use focusTaskRatio
                 // that is associated with the original orientation of the focused task.
                 boxWidth = taskWidth;
                 boxHeight = taskHeight;
-                thumbnailRatio = getRecentsView().getFocusedTaskRatio();
             } else {
                 // Otherwise task is in grid, and should use lastComputedGridTaskSize.
                 Rect lastComputedGridTaskSize = getRecentsView().getLastComputedGridTaskSize();
                 boxWidth = lastComputedGridTaskSize.width();
                 boxHeight = lastComputedGridTaskSize.height();
-                thumbnailRatio = mTask != null ? mTask.getVisibleThumbnailRatio(
-                        TaskView.CLIP_STATUS_AND_NAV_BARS) : 0f;
             }
-            int boxLength = Math.max(boxWidth, boxHeight);
 
             // Bound width/height to the box size.
-            if (thumbnailRatio == 0f) {
-                expectedWidth = boxWidth;
-                expectedHeight = boxHeight + thumbnailPadding;
-            } else if (thumbnailRatio > 1) {
-                expectedWidth = boxLength;
-                expectedHeight = (int) (boxLength / thumbnailRatio) + thumbnailPadding;
-            } else {
-                expectedWidth = (int) (boxLength * thumbnailRatio);
-                expectedHeight = boxLength + thumbnailPadding;
-            }
+            expectedWidth = boxWidth;
+            expectedHeight = boxHeight + thumbnailPadding;
 
             // Scale to to fit task Rect.
-            fullscreenScale = taskWidth / (float) boxWidth;
-
-            // In full screen, scale back TaskView to original size.
-            if (expectedWidth > boxWidth) {
-                fullscreenScale *= boxWidth / (float) expectedWidth;
-            } else if (expectedHeight - thumbnailPadding > boxHeight) {
-                fullscreenScale *= boxHeight / (float) (expectedHeight - thumbnailPadding);
-            }
+            nonGridScale = taskWidth / (float) boxWidth;
 
             // Align to top of task Rect.
             boxTranslationY = (expectedHeight - thumbnailPadding - taskHeight) / 2.0f;
         } else {
-            fullscreenScale = 1f;
+            nonGridScale = 1f;
             boxTranslationY = 0f;
             expectedWidth = ViewGroup.LayoutParams.MATCH_PARENT;
             expectedHeight = ViewGroup.LayoutParams.MATCH_PARENT;
         }
 
-        setFullscreenScale(fullscreenScale);
+        setNonGridScale(nonGridScale);
         setBoxTranslationY(boxTranslationY);
         if (params.width != expectedWidth || params.height != expectedHeight) {
             params.width = expectedWidth;
@@ -1422,20 +1411,15 @@
         }
     }
 
-    private float getFullscreenTrans(float endTranslation) {
-        float progress = FULLSCREEN_INTERPOLATOR.getInterpolation(mFullscreenProgress);
-        return Utilities.mapRange(progress, 0, endTranslation);
-    }
-
-    private float getNonFullscreenTrans(float endTranslation) {
-        return endTranslation - getFullscreenTrans(endTranslation);
-    }
-
     private float getGridTrans(float endTranslation) {
-        float progress = ACCEL_DEACCEL.getInterpolation(mGridProgress);
+        float progress = GRID_INTERPOLATOR.getInterpolation(mGridProgress);
         return Utilities.mapRange(progress, 0, endTranslation);
     }
 
+    private float getNonGridTrans(float endTranslation) {
+        return endTranslation - getGridTrans(endTranslation);
+    }
+
     public boolean isRunningTask() {
         if (getRecentsView() == null) {
             return false;
@@ -1467,7 +1451,7 @@
 
     public void initiateSplitSelect(SplitPositionOption splitPositionOption) {
         AbstractFloatingView.closeOpenViews(mActivity, false, TYPE_TASK_MENU);
-        getRecentsView().initiateSplitSelect(this, splitPositionOption);
+        getRecentsView().initiateSplitSelect(this, splitPositionOption.stagePosition);
     }
 
     /**
@@ -1479,6 +1463,11 @@
         mDigitalWellBeingToast.setBannerColorTint(tintColor, amount);
     }
 
+
+    private int getRootViewDisplayId() {
+        return getRootView().getDisplay().getDisplayId();
+    }
+
     /**
      * We update and subsequently draw these in {@link #setFullscreenProgress(float)}.
      */
@@ -1487,7 +1476,6 @@
         private final float mCornerRadius;
         private final float mWindowCornerRadius;
 
-        public float mFullscreenProgress;
         public RectF mCurrentDrawnInsets = new RectF();
         public float mCurrentDrawnCornerRadius;
         /** The current scale we apply to the thumbnail to adjust for new left/right insets. */
@@ -1495,7 +1483,7 @@
 
         public FullscreenDrawParams(Context context) {
             mCornerRadius = TaskCornerRadius.get(context);
-            mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context.getResources());
+            mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context);
 
             mCurrentDrawnCornerRadius = mCornerRadius;
         }
@@ -1503,20 +1491,23 @@
         /**
          * Sets the progress in range [0, 1]
          */
-        public void setProgress(float fullscreenProgress, float parentScale, int previewWidth,
-                DeviceProfile dp, PreviewPositionHelper pph) {
-            mFullscreenProgress = fullscreenProgress;
-            RectF insets = pph.getInsetsToDrawInFullscreen();
+        public void setProgress(float fullscreenProgress, float parentScale, float taskViewScale,
+                int previewWidth, DeviceProfile dp, PreviewPositionHelper pph) {
+            RectF insets = pph.getInsetsToDrawInFullscreen(dp);
 
             float currentInsetsLeft = insets.left * fullscreenProgress;
             float currentInsetsRight = insets.right * fullscreenProgress;
+            float insetsBottom = insets.bottom;
+            if (dp.isTaskbarPresentInApps) {
+                insetsBottom = Math.max(0, insetsBottom - dp.taskbarSize);
+            }
             mCurrentDrawnInsets.set(currentInsetsLeft, insets.top * fullscreenProgress,
-                    currentInsetsRight, insets.bottom * fullscreenProgress);
+                    currentInsetsRight, insetsBottom * fullscreenProgress);
             float fullscreenCornerRadius = dp.isMultiWindowMode ? 0 : mWindowCornerRadius;
 
             mCurrentDrawnCornerRadius =
                     Utilities.mapRange(fullscreenProgress, mCornerRadius, fullscreenCornerRadius)
-                            / parentScale;
+                            / parentScale / taskViewScale;
 
             // We scaled the thumbnail to fit the content (excluding insets) within task view width.
             // Now that we are drawing left/right insets again, we need to scale down to fit them.
@@ -1526,4 +1517,42 @@
         }
 
     }
+
+    public class TaskIdAttributeContainer {
+        private final TaskThumbnailView mThumbnailView;
+        private final Task mTask;
+        /** Defaults to STAGE_POSITION_UNDEFINED if in not a split screen task view */
+        private @SplitConfigurationOptions.StagePosition int mStagePosition;
+
+        public TaskIdAttributeContainer(Task task, TaskThumbnailView thumbnailView,
+                int stagePosition) {
+            this.mTask = task;
+            this.mThumbnailView = thumbnailView;
+            this.mStagePosition = stagePosition;
+        }
+
+        public TaskThumbnailView getThumbnailView() {
+            return mThumbnailView;
+        }
+
+        public Task getTask() {
+            return mTask;
+        }
+
+        public WorkspaceItemInfo getItemInfo() {
+            return TaskView.this.getItemInfo(mTask);
+        }
+
+        public TaskView getTaskView() {
+            return TaskView.this;
+        }
+
+        public int getStagePosition() {
+            return mStagePosition;
+        }
+
+        void setStagePosition(@SplitConfigurationOptions.StagePosition int stagePosition) {
+            this.mStagePosition = stagePosition;
+        }
+    }
 }
diff --git a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
new file mode 100644
index 0000000..c1b3beb
--- /dev/null
+++ b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
@@ -0,0 +1,226 @@
+/*
+ * 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.model;
+
+import static android.os.Process.myUserHandle;
+
+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;
+import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetId;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.BgDataModel.FixedContainerItems;
+import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
+import com.android.launcher3.util.LauncherModelHelper;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.PendingAddWidgetInfo;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class WidgetsPredicationUpdateTaskTest {
+
+    private AppWidgetProviderInfo mApp1Provider1;
+    private AppWidgetProviderInfo mApp1Provider2;
+    private AppWidgetProviderInfo mApp2Provider1;
+    private AppWidgetProviderInfo mApp4Provider1;
+    private AppWidgetProviderInfo mApp4Provider2;
+    private AppWidgetProviderInfo mApp5Provider1;
+    private List<AppWidgetProviderInfo> allWidgets;
+
+    private FakeBgDataModelCallback mCallback = new FakeBgDataModelCallback();
+    private LauncherModelHelper mModelHelper;
+    private UserHandle mUserHandle;
+
+    @Mock
+    private IconCache mIconCache;
+
+    @Before
+    public void setup() throws Exception {
+        mModelHelper = new LauncherModelHelper();
+        MockitoAnnotations.initMocks(this);
+        doAnswer(invocation -> {
+            ComponentWithLabel componentWithLabel = invocation.getArgument(0);
+            return componentWithLabel.getComponent().getShortClassName();
+        }).when(mIconCache).getTitleNoCache(any());
+
+        mUserHandle = myUserHandle();
+        mApp1Provider1 = createAppWidgetProviderInfo(
+                ComponentName.createRelative("app1", "provider1"));
+        mApp1Provider2 = createAppWidgetProviderInfo(
+                ComponentName.createRelative("app1", "provider2"));
+        mApp2Provider1 = createAppWidgetProviderInfo(
+                ComponentName.createRelative("app2", "provider1"));
+        mApp4Provider1 = createAppWidgetProviderInfo(
+                ComponentName.createRelative("app4", "provider1"));
+        mApp4Provider2 = createAppWidgetProviderInfo(
+                ComponentName.createRelative("app4", ".provider2"));
+        mApp5Provider1 = createAppWidgetProviderInfo(
+                ComponentName.createRelative("app5", "provider1"));
+        allWidgets = Arrays.asList(mApp1Provider1, mApp1Provider2, mApp2Provider1,
+                mApp4Provider1, mApp4Provider2, mApp5Provider1);
+
+        AppWidgetManager manager = mModelHelper.sandboxContext.spyService(AppWidgetManager.class);
+        doReturn(allWidgets).when(manager).getInstalledProviders();
+        doReturn(allWidgets).when(manager).getInstalledProvidersForProfile(eq(myUserHandle()));
+        doAnswer(i -> {
+            String pkg = i.getArgument(0);
+            Log.e("Hello", "Getting v " + pkg);
+            return TextUtils.isEmpty(pkg) ? allWidgets : allWidgets.stream()
+                    .filter(a -> pkg.equals(a.provider.getPackageName()))
+                    .collect(Collectors.toList());
+        }).when(manager).getInstalledProvidersForPackage(any(), eq(myUserHandle()));
+
+        // 2 widgets, app4/provider1 & app5/provider1, have already been added to the workspace.
+        mModelHelper.initializeData("widgets_predication_update_task_data");
+
+        MAIN_EXECUTOR.submit(() -> mModelHelper.getModel().addCallbacks(mCallback)).get();
+        MODEL_EXECUTOR.post(() -> mModelHelper.getBgDataModel().widgetsModel.update(
+                LauncherAppState.getInstance(mModelHelper.sandboxContext),
+                /* packageUser= */ null));
+
+        MODEL_EXECUTOR.submit(() -> { }).get();
+        MAIN_EXECUTOR.submit(() -> { }).get();
+    }
+
+    @After
+    public void tearDown() {
+        mModelHelper.destroy();
+    }
+
+    @Test
+    public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder()
+            throws Exception {
+        // WHEN newPredicationTask is executed with app predication of 5 apps.
+        AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "className",
+                mUserHandle);
+        AppTarget app2 = new AppTarget(new AppTargetId("app2"), "app2", "className",
+                mUserHandle);
+        AppTarget app3 = new AppTarget(new AppTargetId("app3"), "app3", "className",
+                mUserHandle);
+        AppTarget app4 = new AppTarget(new AppTargetId("app4"), "app4", "className",
+                mUserHandle);
+        AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "className",
+                mUserHandle);
+        mModelHelper.executeTaskForTest(
+                newWidgetsPredicationTask(List.of(app5, app3, app2, app4, app1)))
+                .forEach(Runnable::run);
+
+        // THEN only 3 widgets are returned because
+        // 1. app5/provider1 & app4/provider1 have already been added to workspace. They are
+        //    excluded from the result.
+        // 2. app3 doesn't have a widget.
+        // 3. only 1 widget is picked from app1 because we only want to promote one widget per app.
+        List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
+                .stream()
+                .map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
+                .collect(Collectors.toList());
+        assertThat(recommendedWidgets).hasSize(3);
+        assertWidgetInfo(recommendedWidgets.get(0).info, mApp2Provider1);
+        assertWidgetInfo(recommendedWidgets.get(1).info, mApp4Provider2);
+        assertWidgetInfo(recommendedWidgets.get(2).info, mApp1Provider1);
+    }
+
+    @Test
+    public void widgetsRecommendationRan_localFilterDisabled_shouldReturnWidgetsInPredicationOrder()
+            throws Exception {
+        if (FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER.get()) {
+            return;
+        }
+
+        // WHEN newPredicationTask is executed with 5 predicated widgets.
+        AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1",
+                mUserHandle);
+        AppTarget widget2 = new AppTarget(new AppTargetId("app1"), "app1", "provider2",
+                mUserHandle);
+        // Not installed app
+        AppTarget widget3 = new AppTarget(new AppTargetId("app2"), "app3", "provider1",
+                mUserHandle);
+        // Not installed widget
+        AppTarget widget4 = new AppTarget(new AppTargetId("app4"), "app4", "provider3",
+                mUserHandle);
+        AppTarget widget5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
+                mUserHandle);
+        mModelHelper.executeTaskForTest(
+                newWidgetsPredicationTask(List.of(widget5, widget3, widget2, widget4, widget1)))
+                .forEach(Runnable::run);
+
+        // THEN only 3 widgets are returned because the launcher only filters out non-exist widgets.
+        List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
+                .stream()
+                .map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
+                .collect(Collectors.toList());
+        assertThat(recommendedWidgets).hasSize(3);
+        assertWidgetInfo(recommendedWidgets.get(0).info, mApp5Provider1);
+        assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider2);
+        assertWidgetInfo(recommendedWidgets.get(2).info, mApp1Provider1);
+    }
+
+    private void assertWidgetInfo(
+            LauncherAppWidgetProviderInfo actual, AppWidgetProviderInfo expected) {
+        assertThat(actual.provider).isEqualTo(expected.provider);
+        assertThat(actual.getUser()).isEqualTo(expected.getProfile());
+    }
+
+    private WidgetsPredictionUpdateTask newWidgetsPredicationTask(List<AppTarget> appTargets) {
+       return new WidgetsPredictionUpdateTask(
+                new PredictorState(CONTAINER_WIDGETS_PREDICTION, "test_widgets_prediction"),
+                appTargets);
+    }
+
+    private final class FakeBgDataModelCallback implements BgDataModel.Callbacks {
+
+        private FixedContainerItems mRecommendedWidgets = null;
+
+        @Override
+        public void bindExtraContainerItems(FixedContainerItems item) {
+            mRecommendedWidgets = item;
+        }
+    }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
index dc73a9a..ca47de3 100644
--- a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
+++ b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
@@ -82,6 +82,6 @@
 
         RecentsView recentsView = launcher.getOverviewPanel();
         return recentsView.getSizeStrategy().isInLiveTileMode()
-                && recentsView.getRunningTaskId() != -1;
+                && recentsView.getRunningTaskViewId() != -1;
     }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index a683d01..cba4833 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -42,7 +42,6 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.os.RemoteException;
-import android.util.Log;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -56,7 +55,6 @@
 import com.android.launcher3.tapl.OverviewTask;
 import com.android.launcher3.tapl.TestHelpers;
 import com.android.launcher3.testcomponent.TestCommandReceiver;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.Wait;
 import com.android.launcher3.util.rule.FailureWatcher;
 import com.android.quickstep.views.RecentsView;
@@ -98,6 +96,7 @@
         mDevice = UiDevice.getInstance(instrumentation);
         mDevice.setOrientationNatural();
         mLauncher = new LauncherInstrumentation();
+        mLauncher.enableDebugTracing();
         // b/143488140
         //mLauncher.enableCheckEventsForSuccessfulGestures();
 
@@ -187,15 +186,9 @@
 
     protected <T> T getFromRecents(Function<RecentsActivity, T> f) {
         if (!TestHelpers.isInLauncherProcess()) return null;
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.FALLBACK_ACTIVITY_NO_SET, "getFromRecents");
-        }
         Object[] result = new Object[1];
         Wait.atMost("Failed to get from recents", () -> MAIN_EXECUTOR.submit(() -> {
             RecentsActivity activity = RecentsActivity.ACTIVITY_TRACKER.getCreatedActivity();
-            if (TestProtocol.sDebugTracing) {
-                Log.d(TestProtocol.FALLBACK_ACTIVITY_NO_SET, "activity=" + activity);
-            }
             if (activity == null) {
                 return false;
             }
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationBarRotationContextTest.java b/quickstep/tests/src/com/android/quickstep/NavigationBarRotationContextTest.java
new file mode 100644
index 0000000..af5819a
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/NavigationBarRotationContextTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 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;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.view.View;
+import android.view.WindowInsetsController;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.taskbar.contextual.RotationButton;
+import com.android.launcher3.taskbar.contextual.RotationButtonController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+/** SysUI equivalent */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NavigationBarRotationContextTest {
+    private static final int DEFAULT_ROTATE = 0;
+    private static final int DEFAULT_DISPLAY = 0;
+
+
+    private RotationButtonController mRotationButtonController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        Context mTargetContext = InstrumentationRegistry.getTargetContext();
+        final View view = new View(mTargetContext);
+        RotationButton rotationButton = mock(RotationButton.class);
+        mRotationButtonController = new RotationButtonController(mTargetContext, 0, 0);
+        mRotationButtonController.setRotationButton(rotationButton);
+        // Due to a mockito issue, only spy the object after setting the initial state
+        mRotationButtonController = spy(mRotationButtonController);
+        final AnimatedVectorDrawable kbd = mock(AnimatedVectorDrawable.class);
+        doReturn(view).when(rotationButton).getCurrentView();
+        doReturn(true).when(rotationButton).acceptRotationProposal();
+    }
+
+    @Test
+    public void testOnInvalidRotationProposal() {
+        mRotationButtonController.onRotationProposal(DEFAULT_ROTATE + 1,
+                false /* isValid */);
+        verify(mRotationButtonController, times(1))
+                .setRotateSuggestionButtonState(false /* visible */);
+    }
+
+    @Test
+    public void testOnSameRotationProposal() {
+        mRotationButtonController.onRotationProposal(DEFAULT_ROTATE,
+                true /* isValid */);
+        verify(mRotationButtonController, times(1))
+                .setRotateSuggestionButtonState(false /* visible */);
+    }
+
+    @Test
+    public void testOnRotationProposalShowButtonShowNav() {
+        // No navigation bar should not call to set visibility state
+        mRotationButtonController.onBehaviorChanged(DEFAULT_DISPLAY,
+                WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
+        mRotationButtonController.onTaskBarVisibilityChange(false /* showing */);
+        verify(mRotationButtonController, times(0)).setRotateSuggestionButtonState(
+                false /* visible */);
+        verify(mRotationButtonController, times(0)).setRotateSuggestionButtonState(
+                true /* visible */);
+
+        // No navigation bar with rotation change should not call to set visibility state
+        mRotationButtonController.onRotationProposal(DEFAULT_ROTATE + 1,
+                true /* isValid */);
+        verify(mRotationButtonController, times(0)).setRotateSuggestionButtonState(
+                false /* visible */);
+        verify(mRotationButtonController, times(0)).setRotateSuggestionButtonState(
+                true /* visible */);
+
+        // Since rotation has changed rotation should be pending, show mButton when showing nav bar
+        mRotationButtonController.onTaskBarVisibilityChange(true /* showing */);
+        verify(mRotationButtonController, times(1)).setRotateSuggestionButtonState(
+                true /* visible */);
+    }
+
+    @Test
+    public void testOnRotationProposalShowButton() {
+        // Navigation bar being visible should not call to set visibility state
+        mRotationButtonController.onTaskBarVisibilityChange(true /* showing */);
+        verify(mRotationButtonController, times(0))
+                .setRotateSuggestionButtonState(false /* visible */);
+        verify(mRotationButtonController, times(0))
+                .setRotateSuggestionButtonState(true /* visible */);
+
+        // Navigation bar is visible and rotation requested
+        mRotationButtonController.onRotationProposal(DEFAULT_ROTATE + 1,
+                true /* isValid */);
+        verify(mRotationButtonController, times(1))
+                .setRotateSuggestionButtonState(true /* visible */);
+    }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index 67840d1..8d489e3 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -30,7 +30,6 @@
 import android.content.pm.PackageManager;
 import android.util.Log;
 
-import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.UiDevice;
 
 import com.android.launcher3.tapl.LauncherInstrumentation;
@@ -49,6 +48,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Test rule that allows executing a test with Quickstep on and then Quickstep off.
@@ -182,17 +182,12 @@
                     };
             targetContext.getMainExecutor().execute(() ->
                     SYS_UI_NAVIGATION_MODE.addModeChangeListener(listener));
-            // b/139137636
-//            latch.await(60, TimeUnit.SECONDS);
+            latch.await(60, TimeUnit.SECONDS);
             targetContext.getMainExecutor().execute(() ->
                     SYS_UI_NAVIGATION_MODE.removeModeChangeListener(listener));
 
-            Wait.atMost(() -> "Navigation mode didn't change to " + expectedMode,
-                    () -> currentSysUiNavigationMode() == expectedMode, WAIT_TIME_MS,
-                    launcher);
-            // b/139137636
-//            assertTrue(launcher, "Navigation mode didn't change to " + expectedMode,
-//                    currentSysUiNavigationMode() == expectedMode, description);
+            assertTrue(launcher, "Navigation mode didn't change to " + expectedMode,
+                    currentSysUiNavigationMode() == expectedMode, description);
 
         }
 
@@ -221,12 +216,7 @@
 
     private static void assertTrue(LauncherInstrumentation launcher, String message,
             boolean condition, Description description) {
-        if (launcher.getDevice().hasObject(By.textStartsWith(""))) {
-            // The condition above is "screen is not empty". We are not treating
-            // "Screen is empty" as an anomaly here. It's an acceptable state when
-            // Launcher just starts under instrumentation.
-            launcher.checkForAnomaly();
-        }
+        launcher.checkForAnomaly(true, true);
         if (!condition) {
             final AssertionError assertionError = new AssertionError(message);
             if (description != null) {
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
similarity index 98%
rename from quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
rename to quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
index 2b1b57c..159a51f 100644
--- a/quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
+++ b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
@@ -19,6 +19,8 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 
 import static org.junit.Assert.assertFalse;
@@ -40,6 +42,9 @@
 import android.view.MotionEvent;
 import android.view.Surface;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
 import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.util.DisplayController;
 
@@ -47,10 +52,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
 
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class OrientationTouchTransformerTest {
     static class ScreenSize {
         int mHeight;
@@ -293,7 +297,7 @@
     }
 
     private DisplayController.Info createDisplayInfo(ScreenSize screenSize, int rotation) {
-        Context context = RuntimeEnvironment.application;
+        Context context = getApplicationContext();
         Display display = spy(context.getSystemService(DisplayManager.class)
                 .getDisplay(DEFAULT_DISPLAY));
 
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index 6e19436..f44a812 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -25,6 +25,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.launcher3.Launcher;
+import com.android.launcher3.ui.TaplTestsLauncher3;
 import com.android.launcher3.util.RaceConditionReproducer;
 import com.android.quickstep.NavigationModeSwitchRule.Mode;
 import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
@@ -45,6 +46,7 @@
     @Before
     public void setUp() throws Exception {
         super.setUp();
+        TaplTestsLauncher3.initialize(this);
         // b/143488140
         mLauncher.pressHome();
         // Start an activity where the gestures start.
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index a5038a1..710afe0 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -23,7 +23,6 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Intent;
-import android.util.Log;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -37,13 +36,14 @@
 import com.android.launcher3.tapl.Overview;
 import com.android.launcher3.tapl.OverviewActions;
 import com.android.launcher3.tapl.OverviewTask;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.ui.TaplTestsLauncher3;
+import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
 import com.android.quickstep.views.RecentsView;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -145,13 +145,10 @@
                 launcher -> assertEquals("Dismissing a task didn't remove 1 task from Overview",
                         numTasks - 1, getTaskCount(launcher)));
 
-        // Test UIDevice.pressHome, once we are in AllApps.
-        mDevice.pressHome();
-        waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
-
         // Test dismissing all tasks.
-        mLauncher.getWorkspace().switchToOverview().dismissAllTasks();
-        waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
+        mLauncher.pressHome().switchToOverview().dismissAllTasks();
+        assertTrue("Launcher internal state is not Home",
+                isInState(() -> LauncherState.NORMAL));
         executeOnLauncher(
                 launcher -> assertEquals("Still have tasks after dismissing all",
                         0, getTaskCount(launcher)));
@@ -163,6 +160,7 @@
     @Test
     @NavigationModeSwitch
     @PortraitLandscape
+    @ScreenRecord // b/195673272
     public void testOverviewActions() throws Exception {
         // Experimenting for b/165029151:
         final Overview overview = mLauncher.pressHome().switchToOverview();
@@ -174,7 +172,6 @@
         OverviewActions actionsView =
                 mLauncher.pressHome().switchToOverview().getOverviewActions();
         actionsView.clickAndDismissScreenshot();
-        actionsView.clickAndDismissShare();
     }
 
     private int getCurrentOverviewPage(Launcher launcher) {
@@ -185,6 +182,14 @@
         return launcher.<RecentsView>getOverviewPanel().getTaskViewCount();
     }
 
+    private int getTopRowTaskCountForTablet(Launcher launcher) {
+        return launcher.<RecentsView>getOverviewPanel().getTopRowTaskCountForTablet();
+    }
+
+    private int getBottomRowTaskCountForTablet(Launcher launcher) {
+        return launcher.<RecentsView>getOverviewPanel().getBottomRowTaskCountForTablet();
+    }
+
     @Test
     @NavigationModeSwitch
     @PortraitLandscape
@@ -281,4 +286,71 @@
                 isTestActivityRunning(2));
         getAndAssertBackground();
     }
+
+    @Test
+    @PortraitLandscape
+    @Ignore("b/203781041")
+    public void testOverviewForTablet() throws Exception {
+        if (!mLauncher.isTablet()) {
+            return;
+        }
+        for (int i = 2; i <= 12; i++) {
+            startTestActivity(i);
+        }
+
+        Overview overview = mLauncher.pressHome().switchToOverview();
+        executeOnLauncher(
+                launcher -> assertTrue("Don't have at least 11 tasks",
+                        getTaskCount(launcher) >= 11));
+
+        // Test scroll the first task off screen
+        overview.scrollCurrentTaskOffScreen();
+        assertTrue("Launcher internal state is not Overview",
+                isInState(() -> LauncherState.OVERVIEW));
+        executeOnLauncher(launcher -> assertTrue("Current task in Overview is still 0",
+                getCurrentOverviewPage(launcher) > 0));
+
+        // Test opening the task.
+        overview.getCurrentTask().open();
+        assertTrue("Test activity didn't open from Overview",
+                mDevice.wait(Until.hasObject(By.pkg(getAppPackageName()).text("TestActivity8")),
+                        DEFAULT_UI_TIMEOUT));
+
+        // Scroll the task offscreen as it is now first
+        overview = mLauncher.pressHome().switchToOverview();
+        overview.scrollCurrentTaskOffScreen();
+        assertTrue("Launcher internal state is not Overview",
+                isInState(() -> LauncherState.OVERVIEW));
+        executeOnLauncher(launcher -> assertTrue("Current task in Overview is still 0",
+                getCurrentOverviewPage(launcher) > 0));
+
+        // Test dismissing the later task.
+        final Integer numTasks = getFromLauncher(this::getTaskCount);
+        overview.getCurrentTask().dismiss();
+        executeOnLauncher(
+                launcher -> assertEquals("Dismissing a task didn't remove 1 task from Overview",
+                        numTasks - 1, getTaskCount(launcher)));
+        executeOnLauncher(launcher -> assertTrue("Grid did not rebalance after dismissal",
+                (Math.abs(getTopRowTaskCountForTablet(launcher) - getBottomRowTaskCountForTablet(
+                        launcher)) <= 1)));
+
+        // Test dismissing more tasks.
+        assertTrue("Launcher internal state didn't remain in Overview",
+                isInState(() -> LauncherState.OVERVIEW));
+        overview.getCurrentTask().dismiss();
+        assertTrue("Launcher internal state didn't remain in Overview",
+                isInState(() -> LauncherState.OVERVIEW));
+        overview.getCurrentTask().dismiss();
+        executeOnLauncher(launcher -> assertTrue("Grid did not rebalance after multiple dismissals",
+                (Math.abs(getTopRowTaskCountForTablet(launcher) - getBottomRowTaskCountForTablet(
+                        launcher)) <= 1)));
+
+        // Test dismissing all tasks.
+        mLauncher.pressHome().switchToOverview().dismissAllTasks();
+        assertTrue("Launcher internal state is not Home",
+                isInState(() -> LauncherState.NORMAL));
+        executeOnLauncher(
+                launcher -> assertEquals("Still have tasks after dismissing all",
+                        0, getTaskCount(launcher)));
+    }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
index f33abb0..0f5a1c8 100644
--- a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
+++ b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
@@ -19,9 +19,9 @@
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
 import static androidx.test.InstrumentationRegistry.getTargetContext;
 
-import static com.android.launcher3.common.WidgetUtils.createWidgetInfo;
 import static com.android.launcher3.testcomponent.TestCommandReceiver.EXTRA_VALUE;
 import static com.android.launcher3.testcomponent.TestCommandReceiver.SET_LIST_VIEW_SERVICE_BINDER;
+import static com.android.launcher3.util.WidgetUtils.createWidgetInfo;
 import static com.android.quickstep.NavigationModeSwitchRule.Mode.ZERO_BUTTON;
 
 import static org.junit.Assert.assertEquals;
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/util/RecentsOrientedStateTest.java b/quickstep/tests/src/com/android/quickstep/util/RecentsOrientedStateTest.java
similarity index 89%
rename from quickstep/robolectric_tests/src/com/android/quickstep/util/RecentsOrientedStateTest.java
rename to quickstep/tests/src/com/android/quickstep/util/RecentsOrientedStateTest.java
index 656379f..47ef13b 100644
--- a/quickstep/robolectric_tests/src/com/android/quickstep/util/RecentsOrientedStateTest.java
+++ b/quickstep/tests/src/com/android/quickstep/util/RecentsOrientedStateTest.java
@@ -19,33 +19,34 @@
 import static android.view.Surface.ROTATION_180;
 import static android.view.Surface.ROTATION_90;
 
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 
 import android.content.Context;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
 import com.android.quickstep.FallbackActivityInterface;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.LooperMode;
-import org.robolectric.annotation.LooperMode.Mode;
 
 /**
  * Tests for {@link RecentsOrientedState}
  */
-@RunWith(RobolectricTestRunner.class)
-@LooperMode(Mode.PAUSED)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class RecentsOrientedStateTest {
 
     private RecentsOrientedState mR1, mR2;
 
     @Before
     public void setup() {
-        Context context = RuntimeEnvironment.application;
+        Context context = getApplicationContext();
         mR1 = new RecentsOrientedState(context, FallbackActivityInterface.INSTANCE, i -> { });
         mR2 = new RecentsOrientedState(context, FallbackActivityInterface.INSTANCE, i -> { });
         assertEquals(mR1.getStateId(), mR2.getStateId());
diff --git a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
new file mode 100644
index 0000000..9c5cfcd
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2020 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 static android.view.Display.DEFAULT_DISPLAY;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.Display;
+import android.view.Surface;
+import android.view.SurfaceControl;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.Info;
+import com.android.launcher3.util.LauncherModelHelper;
+import com.android.launcher3.util.ReflectionHelpers;
+import com.android.quickstep.FallbackActivityInterface;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SystemUiProxy;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
+
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TaskViewSimulatorTest {
+
+    @Test
+    public void taskProperlyScaled_portrait_noRotation_sameInsets1() {
+        new TaskMatrixVerifier()
+                .withLauncherSize(1200, 2450)
+                .withInsets(new Rect(0, 80, 0, 120))
+                .verifyNoTransforms();
+    }
+
+    @Test
+    public void taskProperlyScaled_portrait_noRotation_sameInsets2() {
+        new TaskMatrixVerifier()
+                .withLauncherSize(1200, 2450)
+                .withInsets(new Rect(55, 80, 55, 120))
+                .verifyNoTransforms();
+    }
+
+    @Test
+    public void taskProperlyScaled_landscape_noRotation_sameInsets1() {
+        new TaskMatrixVerifier()
+                .withLauncherSize(2450, 1250)
+                .withInsets(new Rect(0, 80, 0, 40))
+                .verifyNoTransforms();
+    }
+
+    @Test
+    public void taskProperlyScaled_landscape_noRotation_sameInsets2() {
+        new TaskMatrixVerifier()
+                .withLauncherSize(2450, 1250)
+                .withInsets(new Rect(0, 80, 120, 0))
+                .verifyNoTransforms();
+    }
+
+    @Test
+    public void taskProperlyScaled_landscape_noRotation_sameInsets3() {
+        new TaskMatrixVerifier()
+                .withLauncherSize(2450, 1250)
+                .withInsets(new Rect(55, 80, 55, 120))
+                .verifyNoTransforms();
+    }
+
+    @Test
+    public void taskProperlyScaled_landscape_rotated() {
+        new TaskMatrixVerifier()
+                .withLauncherSize(1200, 2450)
+                .withInsets(new Rect(0, 80, 0, 120))
+                .withAppBounds(
+                        new Rect(0, 0, 2450, 1200),
+                        new Rect(0, 80, 0, 120),
+                        Surface.ROTATION_90)
+                .verifyNoTransforms();
+    }
+
+    private static class TaskMatrixVerifier extends TransformParams {
+
+        private Point mDisplaySize = new Point();
+        private Rect mDisplayInsets = new Rect();
+        private Rect mAppBounds = new Rect();
+        private Rect mLauncherInsets = new Rect();
+
+        private Rect mAppInsets;
+
+        private int mAppRotation = -1;
+        private DeviceProfile mDeviceProfile;
+
+        TaskMatrixVerifier withLauncherSize(int width, int height) {
+            mDisplaySize.set(width, height);
+            if (mAppBounds.isEmpty()) {
+                mAppBounds.set(0, 0, width, height);
+            }
+            return this;
+        }
+
+        TaskMatrixVerifier withInsets(Rect insets) {
+            mDisplayInsets.set(insets);
+            mLauncherInsets.set(insets);
+            return this;
+        }
+
+        TaskMatrixVerifier withAppBounds(Rect bounds, Rect insets, int appRotation) {
+            mAppBounds.set(bounds);
+            mAppInsets = insets;
+            mAppRotation = appRotation;
+            return this;
+        }
+
+        void verifyNoTransforms() {
+            LauncherModelHelper helper = new LauncherModelHelper();
+            try {
+                helper.sandboxContext.allow(SystemUiProxy.INSTANCE);
+                helper.sandboxContext.allow(SysUINavigationMode.INSTANCE);
+
+                Display display = mock(Display.class);
+                doReturn(DEFAULT_DISPLAY).when(display).getDisplayId();
+                doReturn(mDisplaySize.x > mDisplaySize.y ? Surface.ROTATION_90 : Surface.ROTATION_0)
+                        .when(display).getRotation();
+                doAnswer(i -> {
+                    ((Point) i.getArgument(0)).set(mDisplaySize.x, mDisplaySize.y);
+                    return null;
+                }).when(display).getRealSize(any());
+                doAnswer(i -> {
+                    Point smallestSize = i.getArgument(0);
+                    Point largestSize = i.getArgument(1);
+                    smallestSize.x = smallestSize.y = Math.min(mDisplaySize.x, mDisplaySize.y);
+                    largestSize.x = largestSize.y = Math.max(mDisplaySize.x, mDisplaySize.y);
+
+                    smallestSize.x -= mDisplayInsets.left + mDisplayInsets.right;
+                    largestSize.x -= mDisplayInsets.left + mDisplayInsets.right;
+
+                    smallestSize.y -= mDisplayInsets.top + mDisplayInsets.bottom;
+                    largestSize.y -= mDisplayInsets.top + mDisplayInsets.bottom;
+                    return null;
+                }).when(display).getCurrentSizeRange(any(), any());
+                DisplayController.Info mockInfo = new Info(helper.sandboxContext, display);
+
+                DisplayController controller =
+                        DisplayController.INSTANCE.get(helper.sandboxContext);
+                controller.close();
+                ReflectionHelpers.setField(controller, "mInfo", mockInfo);
+
+                mDeviceProfile = InvariantDeviceProfile.INSTANCE.get(helper.sandboxContext)
+                        .getBestMatch(mAppBounds.width(), mAppBounds.height());
+                mDeviceProfile.updateInsets(mLauncherInsets);
+
+                TaskViewSimulator tvs = new TaskViewSimulator(helper.sandboxContext,
+                        FallbackActivityInterface.INSTANCE);
+                tvs.setDp(mDeviceProfile);
+
+                int launcherRotation = mockInfo.rotation;
+                if (mAppRotation < 0) {
+                    mAppRotation = launcherRotation;
+                }
+
+                tvs.getOrientationState().update(launcherRotation, mAppRotation);
+                if (mAppInsets == null) {
+                    mAppInsets = new Rect(mLauncherInsets);
+                }
+                tvs.setPreviewBounds(mAppBounds, mAppInsets);
+
+                tvs.fullScreenProgress.value = 1;
+                tvs.recentsViewScale.value = tvs.getFullScreenScale();
+                tvs.apply(this);
+            } finally {
+                helper.destroy();
+            }
+        }
+
+        @Override
+        public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
+            SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
+            proxy.onBuildTargetParams(builder, mock(RemoteAnimationTargetCompat.class), this);
+            return new SurfaceParams[] {builder.build()};
+        }
+
+        @Override
+        public void applySurfaceParams(SurfaceParams[] params) {
+            // Verify that the task position remains the same
+            RectF newAppBounds = new RectF(mAppBounds);
+            params[0].matrix.mapRect(newAppBounds);
+            Assert.assertThat(newAppBounds, new AlmostSame(mAppBounds));
+
+            System.err.println("Bounds mapped: " + mAppBounds + " => " + newAppBounds);
+        }
+    }
+
+    private static class AlmostSame extends TypeSafeMatcher<RectF>  {
+
+        // Allow .1% error margin to account for float to int conversions
+        private final float mErrorFactor = .001f;
+        private final Rect mExpected;
+
+        AlmostSame(Rect expected) {
+            mExpected = expected;
+        }
+
+        @Override
+        protected boolean matchesSafely(RectF item) {
+            float errorWidth = mErrorFactor * mExpected.width();
+            float errorHeight = mErrorFactor * mExpected.height();
+            return Math.abs(item.left - mExpected.left) < errorWidth
+                    && Math.abs(item.top - mExpected.top) < errorHeight
+                    && Math.abs(item.right - mExpected.right) < errorWidth
+                    && Math.abs(item.bottom - mExpected.bottom) < errorHeight;
+        }
+
+        @Override
+        public void describeTo(Description description) {
+            description.appendValue(mExpected);
+        }
+    }
+}
diff --git a/res/color-night-v31/folder_background_dark.xml b/res/color-night-v31/folder_background_dark.xml
index a5bd636..d607395 100644
--- a/res/color-night-v31/folder_background_dark.xml
+++ b/res/color-night-v31/folder_background_dark.xml
@@ -16,5 +16,5 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android" >
     <item
         android:color="@android:color/system_neutral2_50"
-        android:lStar="30" />
+        android:lStar="35" />
 </selector>
diff --git a/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml b/res/color-night-v31/folder_preview_dark.xml
similarity index 70%
copy from quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml
copy to res/color-night-v31/folder_preview_dark.xml
index 9c95497..a5bd636 100644
--- a/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml
+++ b/res/color-night-v31/folder_preview_dark.xml
@@ -1,17 +1,20 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2020 The Android Open Source Project
+<!-- 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.
 -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-  <solid android:color="@color/gesture_tutorial_fake_previous_task_view_color" />
-</shape>
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="@android:color/system_neutral2_50"
+        android:lStar="30" />
+</selector>
diff --git a/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml b/res/color-v31/folder_preview_light.xml
similarity index 70%
rename from quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml
rename to res/color-v31/folder_preview_light.xml
index 9c95497..fe30c87 100644
--- a/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml
+++ b/res/color-v31/folder_preview_light.xml
@@ -1,17 +1,20 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2020 The Android Open Source Project
+<!-- 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.
 -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-  <solid android:color="@color/gesture_tutorial_fake_previous_task_view_color" />
-</shape>
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="@android:color/system_accent2_50"
+        android:lStar="80" />
+</selector>
diff --git a/res/color-v31/overview_scrim_dark.xml b/res/color-v31/overview_scrim_dark.xml
index b8ed774..2ab8ecd 100644
--- a/res/color-v31/overview_scrim_dark.xml
+++ b/res/color-v31/overview_scrim_dark.xml
@@ -14,5 +14,5 @@
      limitations under the License.
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-  <item android:color="@android:color/system_neutral1_800" />
+  <item android:color="@android:color/system_neutral1_500" android:lStar="35" />
 </selector>
diff --git a/res/drawable-v28/round_rect_folder.xml b/res/drawable-v28/round_rect_folder.xml
index 0403be0..77a4aa4 100644
--- a/res/drawable-v28/round_rect_folder.xml
+++ b/res/drawable-v28/round_rect_folder.xml
@@ -16,6 +16,6 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="?attr/folderFillColor" />
+    <solid android:color="?attr/folderBackgroundColor" />
     <corners android:radius="?android:attr/dialogCornerRadius" />
 </shape>
diff --git a/res/drawable-v28/widgets_bottom_sheet_background.xml b/res/drawable-v28/widgets_bottom_sheet_background.xml
deleted file mode 100644
index 7fb8681..0000000
--- a/res/drawable-v28/widgets_bottom_sheet_background.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-    <solid android:color="@color/surface" />
-    <corners
-        android:topLeftRadius="?android:attr/dialogCornerRadius"
-        android:topRightRadius="?android:attr/dialogCornerRadius"
-        android:bottomLeftRadius="0dp"
-        android:bottomRightRadius="0dp"
-        />
-</shape>
\ No newline at end of file
diff --git a/res/drawable-v31/bg_deferred_app_widget.xml b/res/drawable-v31/bg_deferred_app_widget.xml
new file mode 100644
index 0000000..a08998d
--- /dev/null
+++ b/res/drawable-v31/bg_deferred_app_widget.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+    android:inset="8dp">
+    <shape android:shape="rectangle">
+        <corners android:radius="@android:dimen/system_app_widget_background_radius" />
+        <solid android:color="#77000000" />
+    </shape>
+</inset>
diff --git a/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml b/res/drawable/bg_widgets_full_sheet.xml
similarity index 70%
copy from quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml
copy to res/drawable/bg_widgets_full_sheet.xml
index 9c95497..dfcd354 100644
--- a/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml
+++ b/res/drawable/bg_widgets_full_sheet.xml
@@ -1,17 +1,23 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2020 The Android Open Source Project
+<!-- 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.
 -->
+
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-  <solid android:color="@color/gesture_tutorial_fake_previous_task_view_color" />
-</shape>
+    android:shape="rectangle" >
+    <solid android:color="?android:attr/colorBackground" />
+    <corners
+        android:topLeftRadius="@dimen/dialogCornerRadius"
+        android:topRightRadius="@dimen/dialogCornerRadius" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/bg_widgets_picker_handle.xml b/res/drawable/bg_widgets_picker_handle.xml
deleted file mode 100644
index 68681a6..0000000
--- a/res/drawable/bg_widgets_picker_handle.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item>
-        <shape android:shape="rectangle">
-            <solid android:color="?android:attr/colorBackground" />
-            <padding android:top="16dp"/>
-        </shape>
-    </item>
-    <item android:gravity="center">
-        <shape android:shape="rectangle">
-            <solid android:color="?android:attr/textColorSecondary" />
-            <size android:width="48dp" android:height="2dp" />
-        </shape>
-    </item>
-</layer-list>
\ No newline at end of file
diff --git a/res/drawable/gesture_tutorial_motion_overview_light_mode.xml b/res/drawable/gesture_tutorial_motion_overview_light_mode.xml
deleted file mode 100644
index 75887c9..0000000
--- a/res/drawable/gesture_tutorial_motion_overview_light_mode.xml
+++ /dev/null
@@ -1,1587 +0,0 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:aapt="http://schemas.android.com/aapt">
-    <target android:name="_R_G_L_4_G_N_3_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="1050"
-                    android:pathData="M 206,446C 206,446 206,395 206,395"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="217">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_4_G_N_3_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="1050"
-                    android:propertyName="scaleX"
-                    android:startOffset="217"
-                    android:valueFrom="1"
-                    android:valueTo="0.6"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="1050"
-                    android:propertyName="scaleY"
-                    android:startOffset="217"
-                    android:valueFrom="1"
-                    android:valueTo="0.6"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_4_G_N_3_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="3400"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_27_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_26_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_25_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_24_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_23_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_22_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_21_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_20_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_19_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_18_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_17_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_16_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_15_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_14_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_13_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_12_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_11_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_10_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_9_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_8_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_7_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_6_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_5_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_4_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_3_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_2_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_1_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G_L_0_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_L_0_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_N_2_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="350"
-                    android:pathData="M 206,395C 206,403.5 206,437.5 206,446"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="2083">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.34,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_N_2_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="350"
-                    android:propertyName="scaleX"
-                    android:startOffset="2083"
-                    android:valueFrom="0.6"
-                    android:valueTo="0.72718"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.34,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="350"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0.6"
-                    android:valueTo="0.72718"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.34,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="scaleX"
-                    android:startOffset="2433"
-                    android:valueFrom="0.72718"
-                    android:valueTo="0.72"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.51,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="scaleY"
-                    android:startOffset="2433"
-                    android:valueFrom="0.72718"
-                    android:valueTo="0.72"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.51,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_N_2_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0"
-                    android:valueTo="0.6"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_D_0_P_0_G_0_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="scaleX"
-                    android:startOffset="2567"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="scaleY"
-                    android:startOffset="2567"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_N_2_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="350"
-                    android:pathData="M 206,395C 206,403.5 206,437.5 206,446"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="2083">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.34,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_N_2_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="350"
-                    android:propertyName="scaleX"
-                    android:startOffset="2083"
-                    android:valueFrom="0.6"
-                    android:valueTo="0.72718"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.34,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="350"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0.6"
-                    android:valueTo="0.72718"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.34,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="scaleX"
-                    android:startOffset="2433"
-                    android:valueFrom="0.72718"
-                    android:valueTo="0.72"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.51,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="scaleY"
-                    android:startOffset="2433"
-                    android:valueFrom="0.72718"
-                    android:valueTo="0.72"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.51,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_N_2_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="2567"
-                    android:valueFrom="0"
-                    android:valueTo="0.6"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="250"
-                    android:pathData="M -556.176,-7.307C -556.176,-7.307 -421.176,-7.307 -421.176,-7.307"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="1350">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.272,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="417"
-                    android:pathData="M -421.176,-7.307C -421.176,-7.307 -429.51,-7.307 -429.51,-7.307"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="1600">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_N_2_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="350"
-                    android:pathData="M 206,395C 206,403.5 206,437.5 206,446"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="2083">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.34,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_N_2_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="350"
-                    android:propertyName="scaleX"
-                    android:startOffset="2083"
-                    android:valueFrom="0.6"
-                    android:valueTo="0.72718"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.34,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="350"
-                    android:propertyName="scaleY"
-                    android:startOffset="2083"
-                    android:valueFrom="0.6"
-                    android:valueTo="0.72718"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.34,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="scaleX"
-                    android:startOffset="2433"
-                    android:valueFrom="0.72718"
-                    android:valueTo="0.72"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.51,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="scaleY"
-                    android:startOffset="2433"
-                    android:valueFrom="0.72718"
-                    android:valueTo="0.72"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.51,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_N_2_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleY"
-                    android:startOffset="1350"
-                    android:valueFrom="0"
-                    android:valueTo="0.6"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0.75"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="1833"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="217"
-                    android:valueFrom="0.75"
-                    android:valueTo="0.75"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="2050"
-                    android:valueFrom="0.75"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="pathData"
-                    android:startOffset="0"
-                    android:valueFrom="M0 406 C21.54,406 39,423.46 39,445 C39,466.54 21.54,484 0,484 C-21.54,484 -39,466.54 -39,445 C-39,423.46 -21.54,406 0,406c "
-                    android:valueTo="M0 395 C27.61,395 50,417.39 50,445 C50,472.61 27.61,495 0,495 C-27.61,495 -50,472.61 -50,445 C-50,417.39 -27.61,395 0,395c "
-                    android:valueType="pathType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="1050"
-                    android:propertyName="pathData"
-                    android:startOffset="217"
-                    android:valueFrom="M0 395 C27.61,395 50,417.39 50,445 C50,472.61 27.61,495 0,495 C-27.61,495 -50,472.61 -50,445 C-50,417.39 -27.61,395 0,395c "
-                    android:valueTo="M0 166 C27.61,166 50,188.39 50,216 C50,243.61 27.61,266 0,266 C-27.61,266 -50,243.61 -50,216 C-50,188.39 -27.61,166 0,166c "
-                    android:valueType="pathType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="783"
-                    android:propertyName="pathData"
-                    android:startOffset="1267"
-                    android:valueFrom="M0 166 C27.61,166 50,188.39 50,216 C50,243.61 27.61,266 0,266 C-27.61,266 -50,243.61 -50,216 C-50,188.39 -27.61,166 0,166c "
-                    android:valueTo="M0 166 C27.61,166 50,188.39 50,216 C50,243.61 27.61,266 0,266 C-27.61,266 -50,243.61 -50,216 C-50,188.39 -27.61,166 0,166c "
-                    android:valueType="pathType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="pathData"
-                    android:startOffset="2050"
-                    android:valueFrom="M0 166 C27.61,166 50,188.39 50,216 C50,243.61 27.61,266 0,266 C-27.61,266 -50,243.61 -50,216 C-50,188.39 -27.61,166 0,166c "
-                    android:valueTo="M0 180 C19.88,180 36,196.12 36,216 C36,235.88 19.88,252 0,252 C-19.88,252 -36,235.88 -36,216 C-36,196.12 -19.88,180 0,180c "
-                    android:valueType="pathType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="time_group">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="2750"
-                    android:propertyName="translateX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <aapt:attr name="android:drawable">
-        <vector
-            android:width="412dp"
-            android:height="892dp"
-            android:viewportHeight="892"
-            android:viewportWidth="412">
-            <group android:name="_R_G">
-                <group
-                    android:name="_R_G_L_5_G"
-                    android:translateX="206"
-                    android:translateY="446">
-                    <path
-                        android:name="_R_G_L_5_G_D_0_P_0"
-                        android:fillAlpha="1"
-                        android:fillColor="@color/fake_wallpaper_color_light_mode"
-                        android:fillType="nonZero"
-                        android:pathData=" M206 -446 C206,-446 206,446 206,446 C206,446 -206,446 -206,446 C-206,446 -206,-446 -206,-446 C-206,-446 206,-446 206,-446c " />
-                </group>
-                <group
-                    android:name="_R_G_L_4_G_N_3_T_0"
-                    android:scaleX="1"
-                    android:scaleY="1"
-                    android:translateX="206"
-                    android:translateY="446">
-                    <group
-                        android:name="_R_G_L_4_G"
-                        android:translateX="-206"
-                        android:translateY="-446">
-                        <group android:name="_R_G_L_4_G_L_0_G">
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_27_G"
-                                android:translateX="206"
-                                android:translateY="422.5">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_27_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#dadce0"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -395.5 C206,-395.5 206,395.5 206,395.5 C206,395.5 -206,395.5 -206,395.5 C-206,395.5 -206,-395.5 -206,-395.5 C-206,-395.5 206,-395.5 206,-395.5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_26_G"
-                                android:translateX="206"
-                                android:translateY="496.5">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_26_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#dadce0"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -377.5 C206,-377.5 206,377.5 206,377.5 C206,387.43 197.93,395.5 188,395.5 C188,395.5 -188,395.5 -188,395.5 C-197.93,395.5 -206,387.43 -206,377.5 C-206,377.5 -206,-377.5 -206,-377.5 C-206,-387.43 -197.93,-395.5 -188,-395.5 C-188,-395.5 188,-395.5 188,-395.5 C197.93,-395.5 206,-387.43 206,-377.5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_25_G"
-                                android:translateX="206"
-                                android:translateY="50.5">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_25_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#e8eaed"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -23.5 C206,-23.5 206,50.5 206,50.5 C206,50.5 -206,50.5 -206,50.5 C-206,50.5 -206,-23.5 -206,-23.5 C-206,-23.5 206,-23.5 206,-23.5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_24_G"
-                                android:translateX="206"
-                                android:translateY="50.5">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_24_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#e8eaed"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -32.5 C206,-32.5 206,32.5 206,32.5 C206,42.43 197.93,50.5 188,50.5 C188,50.5 -188,50.5 -188,50.5 C-197.93,50.5 -206,42.43 -206,32.5 C-206,32.5 -206,-32.5 -206,-32.5 C-206,-42.43 -197.93,-50.5 -188,-50.5 C-188,-50.5 188,-50.5 188,-50.5 C197.93,-50.5 206,-42.43 206,-32.5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_23_G"
-                                android:translateX="54"
-                                android:translateY="157">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_23_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_22_G"
-                                android:translateX="54"
-                                android:translateY="157">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_22_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_21_G"
-                                android:translateX="148.5"
-                                android:translateY="148">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_21_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M46.5 -5 C46.5,-5 46.5,5 46.5,5 C46.5,7.21 44.71,9 42.5,9 C42.5,9 -42.5,9 -42.5,9 C-44.71,9 -46.5,7.21 -46.5,5 C-46.5,5 -46.5,-5 -46.5,-5 C-46.5,-7.21 -44.71,-9 -42.5,-9 C-42.5,-9 42.5,-9 42.5,-9 C44.71,-9 46.5,-7.21 46.5,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_20_G"
-                                android:translateX="186"
-                                android:translateY="169">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_20_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M84 -4 C84,-4 84,4 84,4 C84,6.21 82.21,8 80,8 C80,8 -80,8 -80,8 C-82.21,8 -84,6.21 -84,4 C-84,4 -84,-4 -84,-4 C-84,-6.21 -82.21,-8 -80,-8 C-80,-8 80,-8 80,-8 C82.21,-8 84,-6.21 84,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_19_G"
-                                android:translateX="54"
-                                android:translateY="245">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_19_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_18_G"
-                                android:translateX="162"
-                                android:translateY="236">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_18_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M60 -5 C60,-5 60,5 60,5 C60,7.21 58.21,9 56,9 C56,9 -56,9 -56,9 C-58.21,9 -60,7.21 -60,5 C-60,5 -60,-5 -60,-5 C-60,-7.21 -58.21,-9 -56,-9 C-56,-9 56,-9 56,-9 C58.21,-9 60,-7.21 60,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_17_G"
-                                android:translateX="171.5"
-                                android:translateY="257">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_17_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M69.5 -4 C69.5,-4 69.5,4 69.5,4 C69.5,6.21 67.71,8 65.5,8 C65.5,8 -65.5,8 -65.5,8 C-67.71,8 -69.5,6.21 -69.5,4 C-69.5,4 -69.5,-4 -69.5,-4 C-69.5,-6.21 -67.71,-8 -65.5,-8 C-65.5,-8 65.5,-8 65.5,-8 C67.71,-8 69.5,-6.21 69.5,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_16_G"
-                                android:translateX="54"
-                                android:translateY="333">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_16_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_15_G"
-                                android:translateX="158"
-                                android:translateY="324">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_15_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M56 -5 C56,-5 56,5 56,5 C56,7.21 54.21,9 52,9 C52,9 -52,9 -52,9 C-54.21,9 -56,7.21 -56,5 C-56,5 -56,-5 -56,-5 C-56,-7.21 -54.21,-9 -52,-9 C-52,-9 52,-9 52,-9 C54.21,-9 56,-7.21 56,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_14_G"
-                                android:translateX="217.5"
-                                android:translateY="345">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_14_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M115.5 -4 C115.5,-4 115.5,4 115.5,4 C115.5,6.21 113.71,8 111.5,8 C111.5,8 -111.5,8 -111.5,8 C-113.71,8 -115.5,6.21 -115.5,4 C-115.5,4 -115.5,-4 -115.5,-4 C-115.5,-6.21 -113.71,-8 -111.5,-8 C-111.5,-8 111.5,-8 111.5,-8 C113.71,-8 115.5,-6.21 115.5,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_13_G"
-                                android:translateX="54"
-                                android:translateY="421">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_13_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_12_G"
-                                android:translateX="170"
-                                android:translateY="412">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_12_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M68 -5 C68,-5 68,5 68,5 C68,7.21 66.21,9 64,9 C64,9 -64,9 -64,9 C-66.21,9 -68,7.21 -68,5 C-68,5 -68,-5 -68,-5 C-68,-7.21 -66.21,-9 -64,-9 C-64,-9 64,-9 64,-9 C66.21,-9 68,-7.21 68,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_11_G"
-                                android:translateX="198.5"
-                                android:translateY="433">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_11_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M96.5 -4 C96.5,-4 96.5,4 96.5,4 C96.5,6.21 94.71,8 92.5,8 C92.5,8 -92.5,8 -92.5,8 C-94.71,8 -96.5,6.21 -96.5,4 C-96.5,4 -96.5,-4 -96.5,-4 C-96.5,-6.21 -94.71,-8 -92.5,-8 C-92.5,-8 92.5,-8 92.5,-8 C94.71,-8 96.5,-6.21 96.5,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_10_G"
-                                android:translateX="54"
-                                android:translateY="509">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_10_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_9_G"
-                                android:translateX="135"
-                                android:translateY="500">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_9_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M33 -5 C33,-5 33,5 33,5 C33,7.21 31.21,9 29,9 C29,9 -29,9 -29,9 C-31.21,9 -33,7.21 -33,5 C-33,5 -33,-5 -33,-5 C-33,-7.21 -31.21,-9 -29,-9 C-29,-9 29,-9 29,-9 C31.21,-9 33,-7.21 33,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_8_G"
-                                android:translateX="185.5"
-                                android:translateY="521">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_8_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M83.5 -4 C83.5,-4 83.5,4 83.5,4 C83.5,6.21 81.71,8 79.5,8 C79.5,8 -79.5,8 -79.5,8 C-81.71,8 -83.5,6.21 -83.5,4 C-83.5,4 -83.5,-4 -83.5,-4 C-83.5,-6.21 -81.71,-8 -79.5,-8 C-79.5,-8 79.5,-8 79.5,-8 C81.71,-8 83.5,-6.21 83.5,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_7_G"
-                                android:translateX="54"
-                                android:translateY="597">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_7_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_6_G"
-                                android:translateX="168.5"
-                                android:translateY="588">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_6_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M66.5 -5 C66.5,-5 66.5,5 66.5,5 C66.5,7.21 64.71,9 62.5,9 C62.5,9 -62.5,9 -62.5,9 C-64.71,9 -66.5,7.21 -66.5,5 C-66.5,5 -66.5,-5 -66.5,-5 C-66.5,-7.21 -64.71,-9 -62.5,-9 C-62.5,-9 62.5,-9 62.5,-9 C64.71,-9 66.5,-7.21 66.5,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_5_G"
-                                android:translateX="198.5"
-                                android:translateY="609">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_5_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M96.5 -4 C96.5,-4 96.5,4 96.5,4 C96.5,6.21 94.71,8 92.5,8 C92.5,8 -92.5,8 -92.5,8 C-94.71,8 -96.5,6.21 -96.5,4 C-96.5,4 -96.5,-4 -96.5,-4 C-96.5,-6.21 -94.71,-8 -92.5,-8 C-92.5,-8 92.5,-8 92.5,-8 C94.71,-8 96.5,-6.21 96.5,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_4_G"
-                                android:translateX="54"
-                                android:translateY="685">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_4_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_3_G"
-                                android:translateX="162.5"
-                                android:translateY="676">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_3_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M60.5 -5 C60.5,-5 60.5,5 60.5,5 C60.5,7.21 58.71,9 56.5,9 C56.5,9 -56.5,9 -56.5,9 C-58.71,9 -60.5,7.21 -60.5,5 C-60.5,5 -60.5,-5 -60.5,-5 C-60.5,-7.21 -58.71,-9 -56.5,-9 C-56.5,-9 56.5,-9 56.5,-9 C58.71,-9 60.5,-7.21 60.5,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_2_G"
-                                android:translateX="174"
-                                android:translateY="697">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_2_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M72 -4 C72,-4 72,4 72,4 C72,6.21 70.21,8 68,8 C68,8 -68,8 -68,8 C-70.21,8 -72,6.21 -72,4 C-72,4 -72,-4 -72,-4 C-72,-6.21 -70.21,-8 -68,-8 C-68,-8 68,-8 68,-8 C70.21,-8 72,-6.21 72,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_1_G"
-                                android:translateX="313.5"
-                                android:translateY="798">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_1_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M74.5 0 C74.5,0 74.5,0 74.5,0 C74.5,15.45 61.95,28 46.5,28 C46.5,28 -46.5,28 -46.5,28 C-61.95,28 -74.5,15.45 -74.5,0 C-74.5,0 -74.5,0 -74.5,0 C-74.5,-15.45 -61.95,-28 -46.5,-28 C-46.5,-28 46.5,-28 46.5,-28 C61.95,-28 74.5,-15.45 74.5,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_4_G_L_0_G_L_0_G"
-                                android:translateX="205.5"
-                                android:translateY="61">
-                                <path
-                                    android:name="_R_G_L_4_G_L_0_G_L_0_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#f8f9fa"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M171.5 -14 C171.5,-14 171.5,14 171.5,14 C171.5,16.21 169.71,18 167.5,18 C167.5,18 -167.5,18 -167.5,18 C-169.71,18 -171.5,16.21 -171.5,14 C-171.5,14 -171.5,-14 -171.5,-14 C-171.5,-16.21 -169.71,-18 -167.5,-18 C-167.5,-18 167.5,-18 167.5,-18 C169.71,-18 171.5,-16.21 171.5,-14c " />
-                            </group>
-                        </group>
-                    </group>
-                </group>
-                <group
-                    android:name="_R_G_L_3_G_N_2_T_0"
-                    android:scaleX="0.6"
-                    android:scaleY="0"
-                    android:translateX="206"
-                    android:translateY="395">
-                    <group
-                        android:name="_R_G_L_3_G"
-                        android:translateX="-206"
-                        android:translateY="-446">
-                        <group
-                            android:name="_R_G_L_3_G_L_0_G"
-                            android:scaleY="0">
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_27_G"
-                                android:scaleY="0"
-                                android:translateX="206"
-                                android:translateY="422.5">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_27_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#dadce0"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -395.5 C206,-395.5 206,395.5 206,395.5 C206,395.5 -206,395.5 -206,395.5 C-206,395.5 -206,-395.5 -206,-395.5 C-206,-395.5 206,-395.5 206,-395.5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_26_G"
-                                android:scaleY="0"
-                                android:translateX="206"
-                                android:translateY="496.5">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_26_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#dadce0"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -377.5 C206,-377.5 206,377.5 206,377.5 C206,387.43 197.93,395.5 188,395.5 C188,395.5 -188,395.5 -188,395.5 C-197.93,395.5 -206,387.43 -206,377.5 C-206,377.5 -206,-377.5 -206,-377.5 C-206,-387.43 -197.93,-395.5 -188,-395.5 C-188,-395.5 188,-395.5 188,-395.5 C197.93,-395.5 206,-387.43 206,-377.5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_25_G"
-                                android:scaleY="0"
-                                android:translateX="206"
-                                android:translateY="50.5">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_25_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#e8eaed"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -23.5 C206,-23.5 206,50.5 206,50.5 C206,50.5 -206,50.5 -206,50.5 C-206,50.5 -206,-23.5 -206,-23.5 C-206,-23.5 206,-23.5 206,-23.5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_24_G"
-                                android:scaleY="0"
-                                android:translateX="206"
-                                android:translateY="50.5">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_24_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#e8eaed"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M206 -32.5 C206,-32.5 206,32.5 206,32.5 C206,42.43 197.93,50.5 188,50.5 C188,50.5 -188,50.5 -188,50.5 C-197.93,50.5 -206,42.43 -206,32.5 C-206,32.5 -206,-32.5 -206,-32.5 C-206,-42.43 -197.93,-50.5 -188,-50.5 C-188,-50.5 188,-50.5 188,-50.5 C197.93,-50.5 206,-42.43 206,-32.5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_23_G"
-                                android:scaleY="0"
-                                android:translateX="54"
-                                android:translateY="157">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_23_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_22_G"
-                                android:scaleY="0"
-                                android:translateX="54"
-                                android:translateY="157">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_22_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_21_G"
-                                android:scaleY="0"
-                                android:translateX="148.5"
-                                android:translateY="148">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_21_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M46.5 -5 C46.5,-5 46.5,5 46.5,5 C46.5,7.21 44.71,9 42.5,9 C42.5,9 -42.5,9 -42.5,9 C-44.71,9 -46.5,7.21 -46.5,5 C-46.5,5 -46.5,-5 -46.5,-5 C-46.5,-7.21 -44.71,-9 -42.5,-9 C-42.5,-9 42.5,-9 42.5,-9 C44.71,-9 46.5,-7.21 46.5,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_20_G"
-                                android:scaleY="0"
-                                android:translateX="186"
-                                android:translateY="169">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_20_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M84 -4 C84,-4 84,4 84,4 C84,6.21 82.21,8 80,8 C80,8 -80,8 -80,8 C-82.21,8 -84,6.21 -84,4 C-84,4 -84,-4 -84,-4 C-84,-6.21 -82.21,-8 -80,-8 C-80,-8 80,-8 80,-8 C82.21,-8 84,-6.21 84,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_19_G"
-                                android:scaleY="0"
-                                android:translateX="54"
-                                android:translateY="245">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_19_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_18_G"
-                                android:scaleY="0"
-                                android:translateX="162"
-                                android:translateY="236">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_18_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M60 -5 C60,-5 60,5 60,5 C60,7.21 58.21,9 56,9 C56,9 -56,9 -56,9 C-58.21,9 -60,7.21 -60,5 C-60,5 -60,-5 -60,-5 C-60,-7.21 -58.21,-9 -56,-9 C-56,-9 56,-9 56,-9 C58.21,-9 60,-7.21 60,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_17_G"
-                                android:scaleY="0"
-                                android:translateX="171.5"
-                                android:translateY="257">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_17_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M69.5 -4 C69.5,-4 69.5,4 69.5,4 C69.5,6.21 67.71,8 65.5,8 C65.5,8 -65.5,8 -65.5,8 C-67.71,8 -69.5,6.21 -69.5,4 C-69.5,4 -69.5,-4 -69.5,-4 C-69.5,-6.21 -67.71,-8 -65.5,-8 C-65.5,-8 65.5,-8 65.5,-8 C67.71,-8 69.5,-6.21 69.5,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_16_G"
-                                android:scaleY="0"
-                                android:translateX="54"
-                                android:translateY="333">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_16_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_15_G"
-                                android:scaleY="0"
-                                android:translateX="158"
-                                android:translateY="324">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_15_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M56 -5 C56,-5 56,5 56,5 C56,7.21 54.21,9 52,9 C52,9 -52,9 -52,9 C-54.21,9 -56,7.21 -56,5 C-56,5 -56,-5 -56,-5 C-56,-7.21 -54.21,-9 -52,-9 C-52,-9 52,-9 52,-9 C54.21,-9 56,-7.21 56,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_14_G"
-                                android:scaleY="0"
-                                android:translateX="217.5"
-                                android:translateY="345">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_14_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M115.5 -4 C115.5,-4 115.5,4 115.5,4 C115.5,6.21 113.71,8 111.5,8 C111.5,8 -111.5,8 -111.5,8 C-113.71,8 -115.5,6.21 -115.5,4 C-115.5,4 -115.5,-4 -115.5,-4 C-115.5,-6.21 -113.71,-8 -111.5,-8 C-111.5,-8 111.5,-8 111.5,-8 C113.71,-8 115.5,-6.21 115.5,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_13_G"
-                                android:scaleY="0"
-                                android:translateX="54"
-                                android:translateY="421">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_13_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_12_G"
-                                android:scaleY="0"
-                                android:translateX="170"
-                                android:translateY="412">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_12_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M68 -5 C68,-5 68,5 68,5 C68,7.21 66.21,9 64,9 C64,9 -64,9 -64,9 C-66.21,9 -68,7.21 -68,5 C-68,5 -68,-5 -68,-5 C-68,-7.21 -66.21,-9 -64,-9 C-64,-9 64,-9 64,-9 C66.21,-9 68,-7.21 68,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_11_G"
-                                android:scaleY="0"
-                                android:translateX="198.5"
-                                android:translateY="433">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_11_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M96.5 -4 C96.5,-4 96.5,4 96.5,4 C96.5,6.21 94.71,8 92.5,8 C92.5,8 -92.5,8 -92.5,8 C-94.71,8 -96.5,6.21 -96.5,4 C-96.5,4 -96.5,-4 -96.5,-4 C-96.5,-6.21 -94.71,-8 -92.5,-8 C-92.5,-8 92.5,-8 92.5,-8 C94.71,-8 96.5,-6.21 96.5,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_10_G"
-                                android:scaleY="0"
-                                android:translateX="54"
-                                android:translateY="509">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_10_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_9_G"
-                                android:scaleY="0"
-                                android:translateX="135"
-                                android:translateY="500">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_9_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M33 -5 C33,-5 33,5 33,5 C33,7.21 31.21,9 29,9 C29,9 -29,9 -29,9 C-31.21,9 -33,7.21 -33,5 C-33,5 -33,-5 -33,-5 C-33,-7.21 -31.21,-9 -29,-9 C-29,-9 29,-9 29,-9 C31.21,-9 33,-7.21 33,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_8_G"
-                                android:scaleY="0"
-                                android:translateX="185.5"
-                                android:translateY="521">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_8_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M83.5 -4 C83.5,-4 83.5,4 83.5,4 C83.5,6.21 81.71,8 79.5,8 C79.5,8 -79.5,8 -79.5,8 C-81.71,8 -83.5,6.21 -83.5,4 C-83.5,4 -83.5,-4 -83.5,-4 C-83.5,-6.21 -81.71,-8 -79.5,-8 C-79.5,-8 79.5,-8 79.5,-8 C81.71,-8 83.5,-6.21 83.5,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_7_G"
-                                android:scaleY="0"
-                                android:translateX="54"
-                                android:translateY="597">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_7_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_6_G"
-                                android:scaleY="0"
-                                android:translateX="168.5"
-                                android:translateY="588">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_6_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M66.5 -5 C66.5,-5 66.5,5 66.5,5 C66.5,7.21 64.71,9 62.5,9 C62.5,9 -62.5,9 -62.5,9 C-64.71,9 -66.5,7.21 -66.5,5 C-66.5,5 -66.5,-5 -66.5,-5 C-66.5,-7.21 -64.71,-9 -62.5,-9 C-62.5,-9 62.5,-9 62.5,-9 C64.71,-9 66.5,-7.21 66.5,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_5_G"
-                                android:scaleY="0"
-                                android:translateX="198.5"
-                                android:translateY="609">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_5_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M96.5 -4 C96.5,-4 96.5,4 96.5,4 C96.5,6.21 94.71,8 92.5,8 C92.5,8 -92.5,8 -92.5,8 C-94.71,8 -96.5,6.21 -96.5,4 C-96.5,4 -96.5,-4 -96.5,-4 C-96.5,-6.21 -94.71,-8 -92.5,-8 C-92.5,-8 92.5,-8 92.5,-8 C94.71,-8 96.5,-6.21 96.5,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_4_G"
-                                android:scaleY="0"
-                                android:translateX="54"
-                                android:translateY="685">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_4_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#9aa0a6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_3_G"
-                                android:scaleY="0"
-                                android:translateX="162.5"
-                                android:translateY="676">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_3_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M60.5 -5 C60.5,-5 60.5,5 60.5,5 C60.5,7.21 58.71,9 56.5,9 C56.5,9 -56.5,9 -56.5,9 C-58.71,9 -60.5,7.21 -60.5,5 C-60.5,5 -60.5,-5 -60.5,-5 C-60.5,-7.21 -58.71,-9 -56.5,-9 C-56.5,-9 56.5,-9 56.5,-9 C58.71,-9 60.5,-7.21 60.5,-5c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_2_G"
-                                android:scaleY="0"
-                                android:translateX="174"
-                                android:translateY="697">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_2_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M72 -4 C72,-4 72,4 72,4 C72,6.21 70.21,8 68,8 C68,8 -68,8 -68,8 C-70.21,8 -72,6.21 -72,4 C-72,4 -72,-4 -72,-4 C-72,-6.21 -70.21,-8 -68,-8 C-68,-8 68,-8 68,-8 C70.21,-8 72,-6.21 72,-4c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_1_G"
-                                android:scaleY="0"
-                                android:translateX="313.5"
-                                android:translateY="798">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_1_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#bdc1c6"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M74.5 0 C74.5,0 74.5,0 74.5,0 C74.5,15.45 61.95,28 46.5,28 C46.5,28 -46.5,28 -46.5,28 C-61.95,28 -74.5,15.45 -74.5,0 C-74.5,0 -74.5,0 -74.5,0 C-74.5,-15.45 -61.95,-28 -46.5,-28 C-46.5,-28 46.5,-28 46.5,-28 C61.95,-28 74.5,-15.45 74.5,0c " />
-                            </group>
-                            <group
-                                android:name="_R_G_L_3_G_L_0_G_L_0_G"
-                                android:scaleY="0"
-                                android:translateX="205.5"
-                                android:translateY="61">
-                                <path
-                                    android:name="_R_G_L_3_G_L_0_G_L_0_G_D_0_P_0"
-                                    android:fillAlpha="1"
-                                    android:fillColor="#f8f9fa"
-                                    android:fillType="nonZero"
-                                    android:pathData=" M171.5 -14 C171.5,-14 171.5,14 171.5,14 C171.5,16.21 169.71,18 167.5,18 C167.5,18 -167.5,18 -167.5,18 C-169.71,18 -171.5,16.21 -171.5,14 C-171.5,14 -171.5,-14 -171.5,-14 C-171.5,-16.21 -169.71,-18 -167.5,-18 C-167.5,-18 167.5,-18 167.5,-18 C169.71,-18 171.5,-16.21 171.5,-14c " />
-                            </group>
-                        </group>
-                    </group>
-                </group>
-                <group
-                    android:name="_R_G_L_2_G_N_2_T_0"
-                    android:scaleX="0.6"
-                    android:scaleY="0"
-                    android:translateX="206"
-                    android:translateY="395">
-                    <group
-                        android:name="_R_G_L_2_G"
-                        android:scaleX="1.3767699999999998"
-                        android:scaleY="1.3767699999999998"
-                        android:translateY="-508.163">
-                        <group
-                            android:name="_R_G_L_2_G_D_0_P_0_G_0_T_0"
-                            android:scaleX="0"
-                            android:scaleY="0">
-                            <path
-                                android:name="_R_G_L_2_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="#9aa0a6"
-                                android:fillType="nonZero"
-                                android:pathData=" M0 25 C13.81,25 25,13.81 25,0 C25,-13.81 13.81,-25 0,-25 C-13.81,-25 -25,-13.81 -25,0 C-25,13.81 -13.81,25 0,25c " />
-                        </group>
-                    </group>
-                </group>
-                <group
-                    android:name="_R_G_L_1_G_N_2_T_0"
-                    android:scaleX="0.6"
-                    android:scaleY="0"
-                    android:translateX="206"
-                    android:translateY="395">
-                    <group
-                        android:name="_R_G_L_1_G"
-                        android:scaleX="1.39"
-                        android:scaleY="1.39"
-                        android:translateX="-556.176"
-                        android:translateY="-7.307">
-                        <path
-                            android:name="_R_G_L_1_G_D_0_P_0"
-                            android:fillAlpha="1"
-                            android:fillColor="@color/gesture_tutorial_fake_previous_task_view_color"
-                            android:fillType="nonZero"
-                            android:pathData=" M135 -301 C135,-301 135,311 135,311 C135,319.28 128.28,326 120,326 C120,326 -120,326 -120,326 C-128.28,326 -135,319.28 -135,311 C-135,311 -135,-301 -135,-301 C-135,-309.28 -128.28,-316 -120,-316 C-120,-316 120,-316 120,-316 C128.28,-316 135,-309.28 135,-301c " />
-                    </group>
-                </group>
-                <group
-                    android:name="_R_G_L_0_G"
-                    android:translateX="206"
-                    android:translateY="446">
-                    <path
-                        android:name="_R_G_L_0_G_D_0_P_0"
-                        android:fillAlpha="0"
-                        android:fillColor="#84ba69"
-                        android:fillType="nonZero"
-                        android:pathData=" M0 406 C21.54,406 39,423.46 39,445 C39,466.54 21.54,484 0,484 C-21.54,484 -39,466.54 -39,445 C-39,423.46 -21.54,406 0,406c " />
-                </group>
-            </group>
-            <group android:name="time_group" />
-        </vector>
-    </aapt:attr>
-</animated-vector>
\ No newline at end of file
diff --git a/res/drawable/round_rect_folder.xml b/res/drawable/round_rect_folder.xml
index 8b3d06c..6c5864e 100644
--- a/res/drawable/round_rect_folder.xml
+++ b/res/drawable/round_rect_folder.xml
@@ -16,6 +16,6 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="?attr/folderFillColor" />
+    <solid android:color="?attr/folderBackgroundColor" />
     <corners android:radius="@dimen/bg_round_rect_radius" />
 </shape>
diff --git a/res/layout/add_item_confirmation_activity.xml b/res/layout/add_item_confirmation_activity.xml
index 0e06690..e29e1b1 100644
--- a/res/layout/add_item_confirmation_activity.xml
+++ b/res/layout/add_item_confirmation_activity.xml
@@ -37,7 +37,7 @@
             android:id="@+id/add_item_bottom_sheet_content"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:padding="24dp"
+            android:paddingVertical="24dp"
             android:background="@drawable/add_item_dialog_background"
             android:orientation="vertical" >
 
@@ -46,6 +46,7 @@
                 android:id="@+id/widget_appName"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
+                android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
                 android:gravity="center_horizontal"
                 android:textColor="?android:attr/textColorPrimary"
                 android:textSize="24sp"
@@ -55,8 +56,10 @@
                 android:maxLines="1" />
 
             <TextView
+                android:id="@+id/widget_drag_instruction"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
+                android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
                 android:gravity="center_horizontal"
                 android:paddingTop="8dp"
                 android:text="@string/add_item_request_drag_hint"
@@ -64,25 +67,37 @@
                 android:textColor="?android:attr/textColorSecondary"
                 android:alpha="0.7"/>
 
-            <include layout="@layout/widget_cell"
-                android:id="@+id/widget_cell"
+            <ScrollView
+                android:id="@+id/widget_preview_scroll_view"
                 android:layout_width="match_parent"
                 android:layout_height="0dp"
-                android:layout_weight="1"
-                android:layout_marginVertical="16dp" />
+                android:layout_marginVertical="16dp"
+                android:layout_weight="1">
+
+                <include
+                    android:id="@+id/widget_cell"
+                    layout="@layout/widget_cell"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin" />
+            </ScrollView>
 
             <LinearLayout
+                android:id="@+id/actions_container"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
+                android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
                 android:gravity="center_vertical|end"
                 android:paddingVertical="8dp"
                 android:orientation="horizontal">
                 <Button
                     style="@style/Button.FullRounded.Colored"
                     android:layout_width="wrap_content"
-                    android:layout_height="36dp"
+                    android:layout_height="wrap_content"
                     android:paddingHorizontal="16dp"
                     android:textSize="14sp"
+                    android:maxLines="2"
+                    android:ellipsize="end"
                     android:textColor="@color/button_text"
                     android:text="@android:string/cancel"
                     android:onClick="onCancelClick"/>
@@ -94,9 +109,11 @@
                 <Button
                     style="@style/Button.FullRounded.Colored"
                     android:layout_width="wrap_content"
-                    android:layout_height="36dp"
+                    android:layout_height="wrap_content"
                     android:paddingHorizontal="16dp"
                     android:textSize="14sp"
+                    android:maxLines="2"
+                    android:ellipsize="end"
                     android:textColor="@color/button_text"
                     android:text="@string/add_to_home_screen"
                     android:onClick="onPlaceAutomaticallyClick"/>
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 9ac6ed0..a34baef 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -42,10 +42,19 @@
         <include layout="@layout/floating_header_content" />
 
         <include layout="@layout/all_apps_personal_work_tabs" />
+
+        <Button
+            android:id="@+id/all_apps_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:text="@string/all_apps_label"
+            android:background="@drawable/padded_rounded_action_button"
+            android:visibility="gone"/>
+
     </com.android.launcher3.allapps.FloatingHeaderView>
 
-    <include
-        layout="@layout/search_container_all_apps"/>
+    <include layout="@layout/search_container_all_apps" />
 
     <include layout="@layout/all_apps_fast_scroller" />
 </com.android.launcher3.allapps.LauncherAllAppsContainerView>
\ No newline at end of file
diff --git a/res/layout/floating_split_select_view.xml b/res/layout/floating_split_select_view.xml
new file mode 100644
index 0000000..e184b91
--- /dev/null
+++ b/res/layout/floating_split_select_view.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.android.quickstep.views.FloatingTaskView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <ImageView
+        android:id="@+id/thumbnail"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="gone" />
+
+    <com.android.quickstep.views.SplitPlaceholderView
+        android:id="@+id/split_placeholder"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/split_placeholder_size"
+        android:background="@android:color/white"
+        android:visibility="gone" />
+
+</com.android.quickstep.views.FloatingTaskView>
\ No newline at end of file
diff --git a/res/layout/keyboard_drag_and_drop.xml b/res/layout/keyboard_drag_and_drop.xml
index e9463c4..bc3a9c1 100644
--- a/res/layout/keyboard_drag_and_drop.xml
+++ b/res/layout/keyboard_drag_and_drop.xml
@@ -26,7 +26,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_horizontal"
-        android:background="?attr/folderFillColor"
+        android:background="?attr/folderBackgroundColor"
         android:padding="8dp"
         android:textColor="?attr/folderTextColor"
         />
diff --git a/res/layout/launcher_preview_two_panel_layout.xml b/res/layout/launcher_preview_two_panel_layout.xml
new file mode 100644
index 0000000..f76fc5a
--- /dev/null
+++ b/res/layout/launcher_preview_two_panel_layout.xml
@@ -0,0 +1,57 @@
+<?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.
+-->
+<view class="com.android.launcher3.graphics.LauncherPreviewRenderer$LauncherPreviewLayout"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:focusable="false">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <com.android.launcher3.CellLayout
+            android:id="@+id/workspace"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:theme="@style/HomeScreenElementTheme"
+            launcher:containerType="workspace"
+            launcher:layout_constraintStart_toStartOf="parent"
+            launcher:layout_constraintTop_toTopOf="parent"
+            launcher:layout_constraintEnd_toStartOf="@id/workspace_right"
+            launcher:layout_constraintBottom_toBottomOf="parent"
+            launcher:pageIndicator="@+id/page_indicator" />
+
+        <com.android.launcher3.CellLayout
+            android:id="@+id/workspace_right"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:theme="@style/HomeScreenElementTheme"
+            launcher:containerType="workspace"
+            launcher:layout_constraintStart_toEndOf="@id/workspace"
+            launcher:layout_constraintTop_toTopOf="parent"
+            launcher:layout_constraintEnd_toEndOf="parent"
+            launcher:layout_constraintBottom_toBottomOf="parent"
+            launcher:pageIndicator="@+id/page_indicator" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <include
+        android:id="@+id/hotseat"
+        layout="@layout/hotseat" />
+
+</view>
\ No newline at end of file
diff --git a/res/layout/qsb_preview.xml b/res/layout/qsb_preview.xml
new file mode 100644
index 0000000..801fb04
--- /dev/null
+++ b/res/layout/qsb_preview.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<com.android.launcher3.qsb.QsbContainerView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="0dp"
+    android:id="@id/search_container_workspace"
+    android:padding="0dp" >
+
+    <fragment
+        android:name="com.android.launcher3.qsb.QsbContainerView$QsbFragment"
+        android:layout_width="match_parent"
+        android:tag="qsb_view"
+        android:layout_height="match_parent"/>
+</com.android.launcher3.qsb.QsbContainerView>
\ No newline at end of file
diff --git a/res/layout/system_shortcut.xml b/res/layout/system_shortcut.xml
index 331d2be..21d532e 100644
--- a/res/layout/system_shortcut.xml
+++ b/res/layout/system_shortcut.xml
@@ -16,7 +16,6 @@
 
 <com.android.launcher3.shortcuts.DeepShortcutView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:layout_width="@dimen/bg_popup_item_width"
     android:layout_height="wrap_content"
     android:minHeight="@dimen/bg_popup_item_height"
@@ -24,29 +23,6 @@
     android:background="@drawable/middle_item_primary"
     android:theme="@style/PopupItem" >
 
-    <com.android.launcher3.BubbleTextView
-        style="@style/BaseIconUnBounded"
-        android:id="@+id/bubble_text"
-        android:background="?android:attr/selectableItemBackground"
-        android:gravity="start|center_vertical"
-        android:textAlignment="viewStart"
-        android:paddingStart="@dimen/deep_shortcuts_text_padding_start"
-        android:paddingEnd="@dimen/popup_padding_end"
-        android:textSize="14sp"
-        android:minLines="1"
-        android:maxLines="2"
-        android:ellipsize="end"
-        android:textColor="?android:attr/textColorPrimary"
-        launcher:iconDisplay="shortcut_popup"
-        launcher:layoutHorizontal="true"
-        android:focusable="false" />
-
-    <View
-        android:id="@+id/icon"
-        android:layout_width="@dimen/system_shortcut_icon_size"
-        android:layout_height="@dimen/system_shortcut_icon_size"
-        android:layout_marginStart="@dimen/system_shortcut_margin_start"
-        android:layout_gravity="start|center_vertical"
-        android:backgroundTint="?android:attr/textColorPrimary"/>
+    <include layout="@layout/system_shortcut_content" />
 
 </com.android.launcher3.shortcuts.DeepShortcutView>
diff --git a/res/layout/system_shortcut_content.xml b/res/layout/system_shortcut_content.xml
index feab13d..e693dbd 100644
--- a/res/layout/system_shortcut_content.xml
+++ b/res/layout/system_shortcut_content.xml
@@ -32,6 +32,7 @@
         android:minLines="1"
         android:maxLines="2"
         android:ellipsize="end"
+        android:hyphenationFrequency="full"
         android:textColor="?android:attr/textColorPrimary"
         launcher:iconDisplay="shortcut_popup"
         launcher:layoutHorizontal="true"
diff --git a/res/layout/widgets_bottom_sheet_content.xml b/res/layout/widgets_bottom_sheet_content.xml
index 3b3ff8b..1a2cfc6 100644
--- a/res/layout/widgets_bottom_sheet_content.xml
+++ b/res/layout/widgets_bottom_sheet_content.xml
@@ -18,7 +18,7 @@
         android:id="@+id/widgets_bottom_sheet"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:background="@drawable/widgets_bottom_sheet_background"
+        android:background="@drawable/bg_rounded_corner_bottom_sheet"
         android:paddingTop="16dp"
         android:orientation="vertical">
         <View
@@ -47,6 +47,7 @@
             <include layout="@layout/widgets_table_container"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
+                android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
                 android:layout_gravity="center_horizontal" />
         </ScrollView>
     </LinearLayout>
diff --git a/res/layout/widgets_full_sheet.xml b/res/layout/widgets_full_sheet.xml
index 1b4f3b9..309dc42 100644
--- a/res/layout/widgets_full_sheet.xml
+++ b/res/layout/widgets_full_sheet.xml
@@ -19,13 +19,23 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
-    android:theme="?attr/widgetsTheme" >
+    android:theme="?attr/widgetsTheme">
 
-    <com.android.launcher3.views.TopRoundedCornerView
+    <com.android.launcher3.views.SpringRelativeLayout
         android:id="@+id/container"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:background="?android:attr/colorBackground">
+        android:background="@drawable/bg_widgets_full_sheet"
+        android:focusable="true"
+        android:importantForAccessibility="no">
+
+        <View
+            android:id="@+id/collapse_handle"
+            android:layout_width="48dp"
+            android:layout_height="2dp"
+            android:layout_marginTop="16dp"
+            android:layout_centerHorizontal="true"
+            android:background="?android:attr/textColorSecondary"/>
 
         <TextView
             android:id="@+id/no_widgets_text"
@@ -35,6 +45,7 @@
             android:visibility="gone"
             android:fontFamily="sans-serif-medium"
             android:textSize="20sp"
+            android:layout_below="@id/search_and_recommendations_container"
             tools:text="No widgets available" />
 
         <!-- Fast scroller popup -->
@@ -57,8 +68,10 @@
             android:id="@+id/search_widgets_list_view"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:layout_below="@id/collapse_handle"
+            android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
             android:visibility="gone"
             android:clipToPadding="false" />
 
-    </com.android.launcher3.views.TopRoundedCornerView>
+    </com.android.launcher3.views.SpringRelativeLayout>
 </com.android.launcher3.widget.picker.WidgetsFullSheet>
\ No newline at end of file
diff --git a/res/layout/widgets_full_sheet_paged_view.xml b/res/layout/widgets_full_sheet_paged_view.xml
index f0ddc2b..dfe226a 100644
--- a/res/layout/widgets_full_sheet_paged_view.xml
+++ b/res/layout/widgets_full_sheet_paged_view.xml
@@ -21,7 +21,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:clipToPadding="false"
-        android:paddingTop="@dimen/widget_picker_view_pager_top_padding"
+        android:layout_below="@id/collapse_handle"
         android:descendantFocusability="afterDescendants"
         launcher:pageIndicator="@+id/tabs">
 
@@ -29,15 +29,96 @@
             android:id="@+id/primary_widgets_list_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
+            android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
             android:clipToPadding="false" />
 
         <com.android.launcher3.widget.picker.WidgetsRecyclerView
             android:id="@+id/work_widgets_list_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
+            android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
             android:clipToPadding="false" />
 
     </com.android.launcher3.workprofile.PersonalWorkPagedView>
 
-    <include layout="@layout/widgets_personal_work_tabs"/>
+    <!-- SearchAndRecommendationsView contains the tab layout as well -->
+    <com.android.launcher3.widget.picker.SearchAndRecommendationsView
+        android:id="@+id/search_and_recommendations_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
+        android:layout_below="@id/collapse_handle"
+        android:paddingBottom="0dp"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center_horizontal"
+            android:textSize="24sp"
+            android:layout_marginTop="24dp"
+            android:textColor="?android:attr/textColorSecondary"
+            android:text="@string/widget_button_text"/>
+
+        <FrameLayout
+            android:id="@+id/search_bar_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:elevation="0.1dp"
+            android:background="?android:attr/colorBackground"
+            android:paddingBottom="8dp"
+            android:clipToPadding="false">
+            <include layout="@layout/widgets_search_bar" />
+        </FrameLayout>
+
+        <com.android.launcher3.widget.picker.WidgetsRecommendationTableLayout
+            android:id="@+id/recommended_widget_table"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            android:background="@drawable/widgets_recommendation_background"
+            android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
+            android:visibility="gone" />
+
+        <com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip
+            android:id="@+id/tabs"
+            android:layout_width="match_parent"
+            android:layout_height="64dp"
+            android:gravity="center_horizontal"
+            android:orientation="horizontal"
+            android:paddingVertical="8dp"
+            android:paddingLeft="@dimen/widget_tabs_horizontal_padding"
+            android:paddingRight="@dimen/widget_tabs_horizontal_padding"
+            android:background="?android:attr/colorBackground"
+            style="@style/TextHeadline">
+
+            <Button
+                android:id="@+id/tab_personal"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_marginEnd="@dimen/widget_tabs_button_horizontal_padding"
+                android:layout_marginVertical="@dimen/widget_apps_tabs_vertical_padding"
+                android:layout_weight="1"
+                android:background="@drawable/all_apps_tabs_background"
+                android:text="@string/widgets_full_sheet_personal_tab"
+                android:textColor="@color/all_apps_tab_text"
+                android:textSize="14sp"
+                style="?android:attr/borderlessButtonStyle" />
+
+            <Button
+                android:id="@+id/tab_work"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_marginEnd="@dimen/widget_tabs_button_horizontal_padding"
+                android:layout_marginVertical="@dimen/widget_apps_tabs_vertical_padding"
+                android:layout_weight="1"
+                android:background="@drawable/all_apps_tabs_background"
+                android:text="@string/widgets_full_sheet_work_tab"
+                android:textColor="@color/all_apps_tab_text"
+                android:textSize="14sp"
+                style="?android:attr/borderlessButtonStyle" />
+        </com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip>
+
+    </com.android.launcher3.widget.picker.SearchAndRecommendationsView>
 </merge>
\ No newline at end of file
diff --git a/res/layout/widgets_full_sheet_recyclerview.xml b/res/layout/widgets_full_sheet_recyclerview.xml
index fbe559c..6a5d6cb 100644
--- a/res/layout/widgets_full_sheet_recyclerview.xml
+++ b/res/layout/widgets_full_sheet_recyclerview.xml
@@ -13,9 +13,54 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.widget.picker.WidgetsRecyclerView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/primary_widgets_list_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:clipToPadding="false" />
\ No newline at end of file
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <com.android.launcher3.widget.picker.WidgetsRecyclerView
+        android:id="@+id/primary_widgets_list_view"
+        android:layout_below="@id/collapse_handle"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
+        android:clipToPadding="false" />
+
+    <!-- SearchAndRecommendationsView without the tab layout as well -->
+    <com.android.launcher3.widget.picker.SearchAndRecommendationsView
+        android:id="@+id/search_and_recommendations_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
+        android:layout_below="@id/collapse_handle"
+        android:paddingBottom="16dp"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center_horizontal"
+            android:textSize="24sp"
+            android:layout_marginTop="24dp"
+            android:textColor="?android:attr/textColorSecondary"
+            android:text="@string/widget_button_text"/>
+
+        <FrameLayout
+            android:id="@+id/search_bar_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:elevation="0.1dp"
+            android:background="?android:attr/colorBackground"
+            android:paddingBottom="8dp"
+            android:clipToPadding="false">
+            <include layout="@layout/widgets_search_bar" />
+        </FrameLayout>
+
+        <com.android.launcher3.widget.picker.WidgetsRecommendationTableLayout
+            android:id="@+id/recommended_widget_table"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            android:background="@drawable/widgets_recommendation_background"
+            android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
+            android:visibility="gone" />
+    </com.android.launcher3.widget.picker.SearchAndRecommendationsView>
+
+</merge>
\ No newline at end of file
diff --git a/res/layout/widgets_full_sheet_search_and_recommendations.xml b/res/layout/widgets_full_sheet_search_and_recommendations.xml
deleted file mode 100644
index 4a3e20d..0000000
--- a/res/layout/widgets_full_sheet_search_and_recommendations.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<com.android.launcher3.widget.picker.SearchAndRecommendationsView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/search_and_recommendations_container"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginBottom="16dp"
-    android:orientation="vertical">
-
-    <View
-        android:id="@+id/collapse_handle"
-        android:layout_width="match_parent"
-        android:layout_height="18dp"
-        android:elevation="0.1dp"
-        android:background="@drawable/bg_widgets_picker_handle"/>
-
-    <TextView
-        android:id="@+id/title"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center_horizontal"
-        android:textSize="24sp"
-        android:layout_marginTop="24dp"
-        android:textColor="?android:attr/textColorSecondary"
-        android:text="@string/widget_button_text"/>
-
-    <FrameLayout
-        android:id="@+id/search_bar_container"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:elevation="0.1dp"
-        android:background="?android:attr/colorBackground"
-        android:paddingBottom="8dp"
-        android:clipToPadding="false">
-        <include layout="@layout/widgets_search_bar" />
-    </FrameLayout>
-
-    <com.android.launcher3.widget.picker.WidgetsRecommendationTableLayout
-        android:id="@+id/recommended_widget_table"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
-        android:layout_marginTop="8dp"
-        android:background="@drawable/widgets_recommendation_background"
-        android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
-        android:visibility="gone" />
-</com.android.launcher3.widget.picker.SearchAndRecommendationsView>
diff --git a/res/layout/widgets_list_row_header.xml b/res/layout/widgets_list_row_header.xml
index 7f84050..8f0eae7 100644
--- a/res/layout/widgets_list_row_header.xml
+++ b/res/layout/widgets_list_row_header.xml
@@ -19,7 +19,6 @@
     android:id="@+id/widgets_list_header"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
     android:paddingVertical="@dimen/widget_list_header_view_vertical_padding"
     android:orientation="horizontal"
     launcher:appIconSize="48dp">
diff --git a/res/layout/widgets_personal_work_tabs.xml b/res/layout/widgets_personal_work_tabs.xml
deleted file mode 100644
index 532c422..0000000
--- a/res/layout/widgets_personal_work_tabs.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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.
--->
-
-<com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/tabs"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/all_apps_header_pill_height"
-    android:gravity="center_horizontal"
-    android:orientation="horizontal"
-    android:layout_marginHorizontal="@dimen/widget_tabs_horizontal_margin"
-    style="@style/TextHeadline">
-
-    <Button
-        android:id="@+id/tab_personal"
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_marginEnd="@dimen/widget_tabs_button_horizontal_padding"
-        android:layout_marginVertical="@dimen/widget_apps_tabs_vertical_padding"
-        android:layout_weight="1"
-        android:background="@drawable/all_apps_tabs_background"
-        android:text="@string/widgets_full_sheet_personal_tab"
-        android:textColor="@color/all_apps_tab_text"
-        android:textSize="14sp"
-        style="?android:attr/borderlessButtonStyle" />
-
-    <Button
-        android:id="@+id/tab_work"
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_marginEnd="@dimen/widget_tabs_button_horizontal_padding"
-        android:layout_marginVertical="@dimen/widget_apps_tabs_vertical_padding"
-        android:layout_weight="1"
-        android:background="@drawable/all_apps_tabs_background"
-        android:text="@string/widgets_full_sheet_work_tab"
-        android:textColor="@color/all_apps_tab_text"
-        android:textSize="14sp"
-        style="?android:attr/borderlessButtonStyle" />
-</com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip>
\ No newline at end of file
diff --git a/res/layout/widgets_search_bar.xml b/res/layout/widgets_search_bar.xml
index cb27f4f..9178a75 100644
--- a/res/layout/widgets_search_bar.xml
+++ b/res/layout/widgets_search_bar.xml
@@ -6,7 +6,6 @@
     android:layout_height="wrap_content"
     android:orientation="horizontal"
     android:layout_marginTop="24dp"
-    android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
     android:background="@drawable/bg_widgets_searchbox">
 
     <com.android.launcher3.ExtendedEditText
diff --git a/res/layout/widgets_table_container.xml b/res/layout/widgets_table_container.xml
index ab470d8..ab96b1343 100644
--- a/res/layout/widgets_table_container.xml
+++ b/res/layout/widgets_table_container.xml
@@ -17,5 +17,4 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/widgets_table"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin" />
+    android:layout_height="wrap_content" />
diff --git a/res/raw/downgrade_schema.json b/res/raw/downgrade_schema.json
index bc25cec..14eac9f 100644
--- a/res/raw/downgrade_schema.json
+++ b/res/raw/downgrade_schema.json
@@ -2,8 +2,9 @@
   // Note: Comments are not supported in JSON schema, but android parser is lenient.
 
   // Maximum DB version supported by this schema
-  "version" : 29,
+  "version" : 30,
 
+  "downgrade_to_29" : [],
   "downgrade_to_28" : [
     "ALTER TABLE favorites RENAME TO temp_favorites;",
     "CREATE TABLE favorites(_id INTEGER PRIMARY KEY, title TEXT, intent TEXT, container INTEGER, screen INTEGER, cellX INTEGER, cellY INTEGER, spanX INTEGER, spanY INTEGER, itemType INTEGER, appWidgetId INTEGER NOT NULL DEFAULT - 1, iconPackage TEXT, iconResource TEXT, icon BLOB, appWidgetProvider TEXT, modified INTEGER NOT NULL DEFAULT 0, restored INTEGER NOT NULL DEFAULT 0, profileId INTEGER DEFAULT 0, rank INTEGER NOT NULL DEFAULT 0, options INTEGER NOT NULL DEFAULT 0);",
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index cdf8322..e9e1478 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Raak en hou die legstuk om dit op die Tuisskerm rond te beweeg"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Voeg by Tuisskerm"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>-legstuk by tuisskerm gevoeg"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> legstukke</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> legstuk</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> kortpaaie</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> kortpad</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# legstuk}other{# legstukke}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# kortpad}other{# kortpaaie}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Legstukke"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Soek"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Kon geen programme kry wat by \"<xliff:g id="QUERY">%1$s</xliff:g>\" pas nie"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Soek meer programme"</string>
     <string name="label_application" msgid="8531721983832654978">"Program"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Alle programme"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Kennisgewings"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Raak en hou om \'n kortpad te skuif."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Dubbeltik en hou om \'n kortpad te skuif of gebruik gepasmaakte handelinge."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Dit is \'n stelselprogram en kan nie gedeïnstalleer word nie."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Wysig naam"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Het <xliff:g id="APP_NAME">%1$s</xliff:g> gedeaktiveer"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, het <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> kennisgewings</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, het <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> kennisgewing</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} het # kennisgewing}other{{app_name} het # kennisgewings}}"</string>
     <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>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 1228495..99c08f1 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"በመነሻ ገጽ አካባቢ ላይ ለማንቀሳቀስ ነክተው ይያዙት"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"ወደ መነሻ ገጽ አክል"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ምግብር ወደ መነሻ ማያ ገጽ ታክሏል"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ንዑስ ፕሮግራሞች</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ንዑስ ፕሮግራሞች</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> አቋራጮች</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> አቋራጮች</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ምግብር}one{# ምግብሮች}other{# ምግብሮች}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# አቋራጭ}one{# አቋራጭ}other{# አቋራጮች}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>፣ <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ፍርግሞች"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"ፍለጋ"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"ከ«<xliff:g id="QUERY">%1$s</xliff:g>» ጋር የሚዛመዱ ምንም መተግበሪያዎች አልተገኙም"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"ተጨማሪ መተግበሪያዎች ይፈልጉ"</string>
     <string name="label_application" msgid="8531721983832654978">"መተግበሪያ"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"ሁሉም መተግበሪያዎች"</string>
     <string name="notifications_header" msgid="1404149926117359025">"ማሳወቂያዎች"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"አቋራጭን ለማንቀሳቀስ ይንኩ እና ይያዙ"</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"አቋራጭን ለማንቀሳቀስ ወይም ብጁ እርምጃዎችን ለመጠቀም ሁለቴ መታ ያድርጉ እና ይያዙ።"</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ይህ የስርዓት መተግበሪያ ነው እና ማራገፍ አይቻልም።"</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"ስም ያርትዑ"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ተሰናክሏል"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>፣ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ማሳወቂያ አለው</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>፣ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ማሳወቂያ አለው</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name}፣ # ማሳወቂያ አለው}one{{app_name}፣ # ማሳወቂያ አለው}other{{app_name}፣ # ማሳወቂያዎች አሉት}}"</string>
     <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>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 218029b..8f293df 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -36,22 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"يمكنك النقر على الأداة مع الاستمرار لتحريكها على الشاشة الرئيسية."</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"إضافة إلى الشاشة الرئيسية"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"تمت إضافة الأداة <xliff:g id="WIDGET_NAME">%1$s</xliff:g> إلى الشاشة الرئيسية."</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="zero"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> أداة</item>
-      <item quantity="two">أداتان (<xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g>)</item>
-      <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> أدوات</item>
-      <item quantity="many"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> أداة</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> أداة</item>
-      <item quantity="one">أداة واحدة (<xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g>)</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="zero"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> اختصار</item>
-      <item quantity="two">اختصاران (<xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g>)</item>
-      <item quantity="few"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> اختصارات</item>
-      <item quantity="many"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> اختصارًا</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> اختصار</item>
-      <item quantity="one">اختصار واحد (<xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g>)</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{أداة واحدة}zero{# أداة}two{أداتان}few{# أدوات}many{# أداة}other{# أداة}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{اختصار واحد}zero{# اختصار}two{اختصاران}few{# اختصارات}many{# اختصارًا}other{# اختصار}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>، <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"الأدوات"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"بحث"</string>
@@ -71,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"لم يتم العثور على أي تطبيقات تتطابق مع \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"البحث عن مزيد من التطبيقات"</string>
     <string name="label_application" msgid="8531721983832654978">"تطبيق"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"جميع التطبيقات"</string>
     <string name="notifications_header" msgid="1404149926117359025">"الإشعارات"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"انقر مع الاستمرار لنقل اختصار"</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"انقر مرتين مع تثبيت إصبعك لنقل اختصار أو استخدام الإجراءات المخصّصة."</string>
@@ -99,14 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"هذا تطبيق نظام وتتعذر إزالته."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"تعديل الاسم"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"تم إيقاف <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="zero">يتضمن تطبيق <xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> إشعار.​</item>
-      <item quantity="two">يتضمن تطبيق <xliff:g id="APP_NAME_2">%1$s</xliff:g> إشعارين (<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>).</item>
-      <item quantity="few">يتضمن تطبيق <xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> إشعارات.​</item>
-      <item quantity="many">يتضمن تطبيق <xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> إشعارًا.</item>
-      <item quantity="other">يتضمن تطبيق <xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> إشعار.</item>
-      <item quantity="one">يتضمن تطبيق <xliff:g id="APP_NAME_0">%1$s</xliff:g> إشعارًا واحدًا (<xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g>).</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{هناك إشعار واحد في تطبيق {app_name}.}zero{هناك # إشعار في تطبيق {app_name}.}two{هناك إشعاران في تطبيق {app_name}.}few{هناك # إشعارات في تطبيق {app_name}.}many{هناك # إشعارًا في تطبيق {app_name}.}other{هناك # إشعار في تطبيق {app_name}.}}"</string>
     <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>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 4cca0ad..7f92939 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"ৱিজেটটো গৃহ স্ক্ৰীনৰ আশে-পাশে নিবলৈ সেইটোত স্পৰ্শ কৰি ধৰি ৰাখক"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"গৃহ স্ক্ৰীনত যোগ কৰক"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ৱিজেটটো গৃহ স্ক্ৰীনত যোগ দিয়া হৈছে"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> টা ৱিজেট</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> টা ৱিজেট</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> টা শ্বৰ্টকাট</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> টা শ্বৰ্টকাট</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# টা ৱিজেট}one{# টা ৱিজেট}other{# টা ৱিজেট}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# টা শ্বৰ্টকাট}one{# টা শ্বৰ্টকাট}other{# টা শ্বৰ্টকাট}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ৱিজেটসমূহ"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"সন্ধান"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"ৰ সৈতে মিলা কোনো এপ্ বিচাৰি পোৱা নগ\'ল"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"আৰু অধিক এপবোৰ সন্ধান কৰক"</string>
     <string name="label_application" msgid="8531721983832654978">"এপ্"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"আটাইবোৰ এপ্"</string>
     <string name="notifications_header" msgid="1404149926117359025">"জাননীসমূহ"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"শ্বৰ্টকাট স্থানান্তৰ কৰিবলৈ দুবাৰ টিপি ধৰি ৰাখক।"</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"কোনো শ্বৰ্টকাট স্থানান্তৰ কৰিবলৈ দুবাৰ টিপি ধৰি ৰাখক অথবা কাষ্টম কাৰ্য ব্যৱহাৰ কৰক।"</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"এইটো এটা ছিষ্টেম এপ আৰু ইয়াক আনইনষ্টল কৰিব নোৱৰি"</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"নাম সম্পাদনা কৰক"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> অক্ষম কৰা হ’ল"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>ৰ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>টা জাননী আছে</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>ৰ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>টা জাননী আছে</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name}ৰ # টা জাননী আছে}one{{app_name}ৰ # টা জাননী আছে}other{{app_name}ৰ # টা জাননী আছে}}"</string>
     <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>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index ec13d3c..54da5f6 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Əsas ekranda hərəkət etdirmək üçün vidcetə toxunub saxlayın"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Əsas ekrana əlavə edin"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> vidceti əsas ekrana əlavə edildi"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> vidcet</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> vidcet</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> qısayol</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> qısayol</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# vidcet}other{# vidcet}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# qısayol}other{# qısayol}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Vidcet"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Axtarış"</string>
@@ -63,6 +57,8 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"<xliff:g id="QUERY">%1$s</xliff:g> sorğusuna uyğun tətbiq tapılmadı"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Daha çox tətbiq üçün axtarış edin"</string>
     <string name="label_application" msgid="8531721983832654978">"Tətbiq"</string>
+    <!-- no translation found for all_apps_label (5015784846527570951) -->
+    <skip />
     <string name="notifications_header" msgid="1404149926117359025">"Bildirişlər"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Qısayolu daşımaq üçün toxunub saxlayın."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Qısayolu daşımaq üçün iki dəfə toxunub saxlayın və ya fərdi əməliyyatlardan istifadə edin."</string>
@@ -91,10 +87,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu sistem tətbiqi olduğu üçün sistemdən silinə bilməz."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Adı redaktə edin"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> deaktiv edildi"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> tətbiqində <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> bildiriş var</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> tətbiqində <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> bildiriş var</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} tətbiqində # bildiriş var}other{{app_name} tətbiqində # bildiriş var}}"</string>
     <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>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 73db7df..10b26e2 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -36,16 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Dodirnite i zadržite vidžet da biste ga pomerali po početnom ekranu"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Dodaj na početni ekran"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Dodali ste vidžet <xliff:g id="WIDGET_NAME">%1$s</xliff:g> na početni ekran"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> vidžet</item>
-      <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> vidžeta</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> vidžeta</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> prečica</item>
-      <item quantity="few"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> prečice</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> prečica</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# vidžet}one{# vidžet}few{# vidžeta}other{# vidžeta}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# prečica}one{# prečica}few{# prečice}other{# prečica}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Vidžeti"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Pretražite"</string>
@@ -65,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nije pronađena nijedna aplikacija za „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Pretraži još aplikacija"</string>
     <string name="label_application" msgid="8531721983832654978">"Aplikacija"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Sve aplikacije"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Obaveštenja"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Dodirnite i zadržite radi pomeranja prečice."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Dvaput dodirnite i zadržite da biste pomerali prečicu ili koristite prilagođene radnje."</string>
@@ -93,11 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ovo je sistemska aplikacija i ne može da se deinstalira."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Izmenite naziv"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogućena"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obaveštenje</item>
-      <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obaveštenja</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obaveštenja</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name}, ima # obaveštenje}one{{app_name}, ima # obaveštenje}few{{app_name}, ima # obaveštenja}other{{app_name}, ima # obaveštenja}}"</string>
     <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>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 96d71a2..4b0b6a1 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -36,18 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Утрымліваючы віджэт націснутым, перамяшчайце яго па Галоўным экране"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Дадаць на Галоўны экран"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Віджэт \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\" дададзены на галоўны экран"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> віджэт</item>
-      <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> віджэты</item>
-      <item quantity="many"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> віджэтаў</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> віджэта</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ярлык</item>
-      <item quantity="few"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ярлыкі</item>
-      <item quantity="many"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ярлыкоў</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ярлыка</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# віджэт}one{# віджэт}few{# віджэты}many{# віджэтаў}other{# віджэта}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# ярлык}one{# ярлык}few{# ярлыкі}many{# ярлыкоў}other{# ярлыка}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Віджэты"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Пошук"</string>
@@ -67,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Праграм, якія адпавядаюць запыту \"<xliff:g id="QUERY">%1$s</xliff:g>\", не знойдзена"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Шукаць іншыя праграмы"</string>
     <string name="label_application" msgid="8531721983832654978">"Праграма"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Усе праграмы"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Апавяшчэнні"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Націсніце і ўтрымлівайце ярлык для перамяшчэння."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Дакраніцеся двойчы і ўтрымлівайце, каб перамясціць ярлык або выкарыстоўваць спецыяльныя дзеянні."</string>
@@ -95,12 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Гэта сістэмная праграма, яе нельга выдаліць."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Змяніць назву"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> адключана"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, мае <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэнне</item>
-      <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, мае <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэнні</item>
-      <item quantity="many"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, мае <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэнняў</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, мае <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэння</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{У праграмы \"{app_name}\" ёсць # апавяшчэнне}one{У праграмы \"{app_name}\" ёсць # апавяшчэнне}few{У праграмы \"{app_name}\" ёсць # апавяшчэнні}many{У праграмы \"{app_name}\" ёсць # апавяшчэнняў}other{У праграмы \"{app_name}\" ёсць # апавяшчэння}}"</string>
     <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>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 05eb63c..42d5712 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Докоснете приспособлението и го задръжте, за да го местите по началния екран"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Добавяне към началния екран"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Приспособлението <xliff:g id="WIDGET_NAME">%1$s</xliff:g> е добавено към началния екран"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> приспособления</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> приспособление</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> преки пътя</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> пряк път</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# приспособление}other{# приспособления}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# пряк път}other{# преки пътя}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Приспособления"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Търсене"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Няма намерени приложения, съответстващи на „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Търсене на още приложения"</string>
     <string name="label_application" msgid="8531721983832654978">"Приложение"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Всички приложения"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Известия"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Докоснете и задръжте за преместване на пряк път."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Докоснете двукратно и задръжте за преместване на пряк път или използвайте персонализирани действия."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Това е системно приложение и не може да се деинсталира."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Редактиране на името"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Деактивирахте <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> – има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> известия</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> – има <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> известие</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} има # известие}other{{app_name} има # известия}}"</string>
     <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>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index db7ddcc..2cfa816 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"হোম স্ক্রিনের যেকোনও জায়গায় উইজেটটি নিয়ে যেতে, টাচ করে ধরে থাকুন"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"হোম স্ক্রিনে যোগ করুন"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> উইজেট হোম স্ক্রিনে যোগ করা হয়েছে"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g>টি উইজেট</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g>টি উইজেট</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g>টি শর্টকাট</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g>টি শর্টকাট</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{#টি উইজেট}one{#টি উইজেট}other{#টি উইজেট}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{#টি শর্টকাট}one{#টি শর্টকাট}other{#টি শর্টকাট}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"উইজেট"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"সার্চ করুন"</string>
@@ -63,6 +57,8 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" এর সাথে মেলে এমন কোনো অ্যাপ পাওয়া যায়নি"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"আরও অ্যাপ্লিকেশানের জন্য খুঁজুন"</string>
     <string name="label_application" msgid="8531721983832654978">"অ্যাপ"</string>
+    <!-- no translation found for all_apps_label (5015784846527570951) -->
+    <skip />
     <string name="notifications_header" msgid="1404149926117359025">"বিজ্ঞপ্তি"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"একটি শর্টকাট সরাতে টাচ করে ধরে রাখুন।"</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"একটি শর্টকাট সরাতে বা কাস্টম অ্যাকশন ব্যবহার করতে ডবল ট্যাপ করে ধরে রাখুন।"</string>
@@ -91,10 +87,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"এটি একটি সিস্টেম অ্যাপ্লিকেশান এবং আনইনস্টল করা যাবে না৷"</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"নাম এডিট করুন"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> অক্ষম করা হয়েছে"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>টি বিজ্ঞপ্তি আছে</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>টি বিজ্ঞপ্তি আছে</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name}-এর #টি বিজ্ঞপ্তি আছে}one{{app_name}-এর #টি বিজ্ঞপ্তি আছে}other{{app_name}-এর #টি বিজ্ঞপ্তি আছে}}"</string>
     <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>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 6bafbb6..da5ffab 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -36,16 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Dodirnite i držite vidžet da ga pomjerate po Početnom ekranu"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Dodaj na početni ekran"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Vidžet <xliff:g id="WIDGET_NAME">%1$s</xliff:g> je dodan na početni ekran"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> vidžet</item>
-      <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> vidžeta</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> vidžeta</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> prečica</item>
-      <item quantity="few"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> prečice</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> prečica</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# vidžet}one{# vidžet}few{# vidžeta}other{# vidžeta}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# prečica}one{# prečica}few{# prečice}other{# prečica}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Vidžeti"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Pretražite"</string>
@@ -65,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nije pronađena nijedna aplikacija za upit \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Pretraži više aplikacija"</string>
     <string name="label_application" msgid="8531721983832654978">"Aplikacija"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Sve aplikacije"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Obavještenja"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Dodirnite i zadržite da pomjerite prečicu."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Dvaput dodirnite i zadržite da pomjerite prečicu ili da koristite prilagođene radnje."</string>
@@ -93,11 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ovo je sistemska aplikacija i ne može se deinstalirati."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Uređivanje naziva"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogućena"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one">Aplikacija <xliff:g id="APP_NAME_2">%1$s</xliff:g> ima<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavještenje​</item>
-      <item quantity="few">Aplikacija <xliff:g id="APP_NAME_2">%1$s</xliff:g> ima<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavještenja​</item>
-      <item quantity="other">Aplikacija <xliff:g id="APP_NAME_2">%1$s</xliff:g> ima<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavještenja​</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} ima # obavještenje}one{{app_name} ima # obavještenje}few{{app_name} ima # obavještenja}other{{app_name} ima # obavještenja}}"</string>
     <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>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 394045d..f9a56bb 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Mantén premut el widget per moure\'l per la pantalla d\'inici"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Afegeix a la pantalla d\'inici"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"El widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> s\'ha afegit a la pantalla d\'inici"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> dreceres</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> drecera</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# drecera}other{# dreceres}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Cerca"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"No s\'ha trobat cap aplicació que coincideixi amb \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Cerca més aplicacions"</string>
     <string name="label_application" msgid="8531721983832654978">"Aplicació"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Totes les aplicacions"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notificacions"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Fes doble toc i mantén premut per moure una drecera."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Fes doble toc i mantén premut per moure una drecera o per utilitzar accions personalitzades."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Aquesta aplicació és una aplicació del sistema i no es pot desinstal·lar."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Edita el nom"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"S\'ha desactivat <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>​ té <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificacions</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> té <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificació</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} té # notificació}other{{app_name} té # notificacions}}"</string>
     <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>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 183a3bb..f0f9266 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -36,18 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Pokud chcete widgetem pohybovat po ploše, podržte ho"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Přidat na plochu"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> byl přidán na plochu"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgety</item>
-      <item quantity="many"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgetu</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgetů</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="few"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> zkratky</item>
-      <item quantity="many"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> zkratky</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> zkratek</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> zkratka</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{ # widget}few{# widgety}many{# widgetu}other{# widgetů}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# zkratka}few{# zkratky}many{# zkratky}other{# zkratek}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgety"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Vyhledávání"</string>
@@ -67,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Dotazu „<xliff:g id="QUERY">%1$s</xliff:g>“ neodpovídají žádné aplikace"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Vyhledat další aplikace"</string>
     <string name="label_application" msgid="8531721983832654978">"Aplikace"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Všechny aplikace"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Oznámení"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Klepnutím a podržením přesunete zkratku."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Dvojitým klepnutím a podržením přesunete zkratku, případně použijte vlastní akce."</string>
@@ -95,12 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Toto je systémová aplikace a nelze ji odinstalovat."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Upravit název"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> je zakázána"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="few">Aplikace <xliff:g id="APP_NAME_2">%1$s</xliff:g> má <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> oznámení</item>
-      <item quantity="many">Aplikace <xliff:g id="APP_NAME_2">%1$s</xliff:g> má <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> oznámení</item>
-      <item quantity="other">Aplikace <xliff:g id="APP_NAME_2">%1$s</xliff:g> má <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> oznámení</item>
-      <item quantity="one">Aplikace <xliff:g id="APP_NAME_0">%1$s</xliff:g> má <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> oznámení</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{Aplikace {app_name} má # oznámení}few{Aplikace {app_name} má # oznámení}many{Aplikace {app_name} má # oznámení}other{Aplikace {app_name} má # oznámení}}"</string>
     <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>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 711d602..5400eb1 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Hold widgetten nede for at flytte den rundt på startskærmen"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Føj til startskærm"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widgetten <xliff:g id="WIDGET_NAME">%1$s</xliff:g> blev føjet til startskærmen"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> genvej</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> genveje</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}other{# widgets}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# genvej}one{# genvej}other{# genveje}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Søg"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Der blev ikke fundet nogen apps, som matcher \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Søg efter flere apps"</string>
     <string name="label_application" msgid="8531721983832654978">"App"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Alle apps"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notifikationer"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Hold en genvej nede for at flytte den."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Tryk to gange, og hold en genvej nede for at flytte den eller bruge tilpassede handlinger."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Dette er en systemapp, som ikke kan afinstalleres."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Rediger navn"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> er deaktiveret"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, har <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifikation</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, har <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifikationer</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} har # notifikation}one{{app_name} har # notifikation}other{{app_name} har # notifikationer}}"</string>
     <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>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 4a3853f..20e06e1 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Wenn du das Widget auf dem Startbildschirm verschieben möchtest, halte es gedrückt"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Zum Startbildschirm hinzufügen"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>-Widget zum Startbildschirm hinzugefügt"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> Widgets</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> Widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> Verknüpfungen</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> Verknüpfung</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# Widget}other{# Widgets}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# Verknüpfung}other{# Verknüpfungen}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Suche"</string>
@@ -63,6 +57,8 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Keine Apps für \"<xliff:g id="QUERY">%1$s</xliff:g>\" gefunden"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Weitere Apps suchen"</string>
     <string name="label_application" msgid="8531721983832654978">"App"</string>
+    <!-- no translation found for all_apps_label (5015784846527570951) -->
+    <skip />
     <string name="notifications_header" msgid="1404149926117359025">"Benachrichtigungen"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Zum Verschieben einer Verknüpfung berühren und halten"</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Doppeltippen und halten, um eine Verknüpfung zu bewegen oder benutzerdefinierte Aktionen zu nutzen."</string>
@@ -91,10 +87,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Dies ist eine Systemanwendung, die nicht deinstalliert werden kann."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Name bearbeiten"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> deaktiviert"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, hat <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> Benachrichtigungen</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, hat <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> Benachrichtigung</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} hat # Benachrichtigung}other{{app_name} hat # Benachrichtigungen}}"</string>
     <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>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index cc9b672..538506c 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Αγγίξτε παρατεταμένα το γραφικό στοιχείο για να το μετακινήσετε στην Αρχική οθόνη"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Προσθήκη στην Αρχική οθόνη"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Το γραφικό στοιχείο <xliff:g id="WIDGET_NAME">%1$s</xliff:g> προστέθηκε στην αρχική οθόνη."</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> γραφικά στοιχεία</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> γραφικό στοιχείο</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> συντομεύσεις</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> συντόμευση</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# γραφικό στοιχείο}other{# γραφικά στοιχεία}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# συντόμευση}other{# συντομεύσεις}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Γραφικά στοιχεία"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Αναζήτηση"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Δεν βρέθηκαν εφαρμογές αντιστοίχισης για \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Αναζήτηση περισσότερων εφαρμογών"</string>
     <string name="label_application" msgid="8531721983832654978">"Εφαρμογή"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Όλες οι εφαρμογές"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Ειδοποιήσεις"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Πατήστε παρατεταμένα για μετακίνηση συντόμευσης."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Πατήστε δύο φορές παρατεταμένα για μετακίνηση συντόμευσης ή χρήση προσαρμοσμένων ενεργειών."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Αυτή είναι μια εφαρμογή συστήματος και δεν είναι δυνατή η κατάργηση της εγκατάστασής της."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Επεξεργασία ονόματος"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> είναι απενεργοποιημένη"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other">Η εφαρμογή <xliff:g id="APP_NAME_2">%1$s</xliff:g>, έχει <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ειδοποιήσεις</item>
-      <item quantity="one">Η εφαρμογή <xliff:g id="APP_NAME_0">%1$s</xliff:g>, έχει <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ειδοποίηση</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{Η εφαρμογή {app_name} έχει # ειδοποίηση}other{Η εφαρμογή {app_name} έχει # ειδοποιήσεις}}"</string>
     <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>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 674b738..f5be3b4 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Touch and hold the widget to move it around the home screen"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Add to home screen"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget added to home screen"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> shortcuts</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> shortcut</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# shortcut}other{# shortcuts}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Search"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"No apps found matching \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Search for more apps"</string>
     <string name="label_application" msgid="8531721983832654978">"App"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"All apps"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Touch &amp; hold to move a shortcut."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Double-tap &amp; hold to move a shortcut or use custom actions."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Edit Name"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Disabled <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notification</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} has # notification}other{{app_name} has # notifications}}"</string>
     <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>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 674b738..f5be3b4 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Touch and hold the widget to move it around the home screen"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Add to home screen"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget added to home screen"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> shortcuts</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> shortcut</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# shortcut}other{# shortcuts}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Search"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"No apps found matching \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Search for more apps"</string>
     <string name="label_application" msgid="8531721983832654978">"App"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"All apps"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Touch &amp; hold to move a shortcut."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Double-tap &amp; hold to move a shortcut or use custom actions."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Edit Name"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Disabled <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notification</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} has # notification}other{{app_name} has # notifications}}"</string>
     <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>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 674b738..f5be3b4 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Touch and hold the widget to move it around the home screen"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Add to home screen"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget added to home screen"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> shortcuts</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> shortcut</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# shortcut}other{# shortcuts}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Search"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"No apps found matching \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Search for more apps"</string>
     <string name="label_application" msgid="8531721983832654978">"App"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"All apps"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Touch &amp; hold to move a shortcut."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Double-tap &amp; hold to move a shortcut or use custom actions."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Edit Name"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Disabled <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notification</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} has # notification}other{{app_name} has # notifications}}"</string>
     <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>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 674b738..f5be3b4 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Touch and hold the widget to move it around the home screen"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Add to home screen"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget added to home screen"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> shortcuts</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> shortcut</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# shortcut}other{# shortcuts}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Search"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"No apps found matching \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Search for more apps"</string>
     <string name="label_application" msgid="8531721983832654978">"App"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"All apps"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Touch &amp; hold to move a shortcut."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Double-tap &amp; hold to move a shortcut or use custom actions."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Edit Name"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Disabled <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notification</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} has # notification}other{{app_name} has # notifications}}"</string>
     <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>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index 4b54fe1..3b78b7f 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‏‏‎‏‎‎‏‎‎‎‎‏‏‎‎‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‏‎‏‎‎‏‏‏‎‏‎Touch &amp; hold the widget to move it around the Home screen‎‏‎‎‏‎"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‎‎‏‎‏‏‎‏‏‎‎‎‎‏‏‎‏‎‏‏‎‎‏‎‏‎‎‏‎‏‎‏‎‎‏‏‏‎‏‏‏‎‎‎‏‏‎‎‏‎‎‎Add to Home screen‎‏‎‎‏‎"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‎‎‏‏‏‎‎‎‏‎‎‎‏‎‏‎‎‏‎‎‏‏‏‎‏‎‎‏‎‏‏‏‏‏‎‎‏‏‎‏‎‎‎‎‎‏‎‏‏‏‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="WIDGET_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ widget added to home screen‎‏‎‎‏‎"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‏‎‏‎‏‏‎‎‏‏‏‎‏‎‏‏‎‎‎‎‎‏‏‏‎‎‎‎‏‎‏‏‎‎‎‏‎‎‏‎‏‎‏‏‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g>‎‏‎‎‏‏‏‎ widgets‎‏‎‎‏‎</item>
-      <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‏‎‏‎‏‏‎‎‏‏‏‎‏‎‏‏‎‎‎‎‎‏‏‏‎‎‎‎‏‎‏‏‎‎‎‏‎‎‏‎‏‎‏‏‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g>‎‏‎‎‏‏‏‎ widget‎‏‎‎‏‎</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‎‎‏‎‏‏‏‏‏‎‎‏‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g>‎‏‎‎‏‏‏‎ shortcuts‎‏‎‎‏‎</item>
-      <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‎‎‏‎‏‏‏‏‏‎‎‏‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g>‎‏‎‎‏‏‏‎ shortcut‎‏‎‎‏‎</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‏‎‏‎‎‏‎‎‏‏‏‎‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‎‎‎‎# widget‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‏‎‏‎‎‏‎‎‏‏‏‎‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‎‎‎‎# widgets‎‏‎‎‏‎}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‎‎‎‏‏‎‏‎‎‏‏‏‎‎‏‏‎‏‎‏‏‎‏‏‎‏‎‏‏‏‎‎‎‏‏‎‏‏‏‏‎‏‎# shortcut‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‎‎‎‏‏‎‏‎‎‏‏‏‎‎‏‏‎‏‎‏‏‎‏‏‎‏‎‏‏‏‎‎‎‏‏‎‏‏‏‏‎‏‎# shortcuts‎‏‎‎‏‎}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‏‏‎‎‏‎‏‎‎‎‎‎‎‏‎‎‏‏‎‏‏‏‏‎‏‏‏‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>‎‏‎‎‏‏‏‎, ‎‏‎‎‏‏‎<xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‏‎‎‎‎‎‎‏‏‎‎‏‎‏‏‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎Widgets‎‏‎‎‏‎"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‏‎‎‏‏‏‏‎‎‎‎‏‎‎‎‏‏‎‏‏‏‎‎‏‏‏‎‏‎‎‏‎Search‎‏‎‎‏‎"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‎‏‎‎‏‏‏‏‎‏‎‏‎‏‎‎‎‎‎‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‏‏‏‏‎‏‎‏‎‎‎‎‎‎‏‎‏‎‎No apps found matching \"‎‏‎‎‏‏‎<xliff:g id="QUERY">%1$s</xliff:g>‎‏‎‎‏‏‏‎\"‎‏‎‎‏‎"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‏‏‎‏‎‏‏‏‏‏‎‎‎‏‎‏‎‎‏‏‏‏‏‎‎‏‏‎‏‏‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‎‎‏‏‎‏‎‎‎‎Search for more apps‎‏‎‎‏‎"</string>
     <string name="label_application" msgid="8531721983832654978">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‎‎‏‎‏‏‏‏‎‎‏‎‎‎‎‏‎‎‏‎‎‎‎‎‏‎‎App‎‏‎‎‏‎"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‏‎‏‏‏‎‏‎‎‏‎‏‏‎‏‏‏‏‏‎‎‎‏‏‏‏‏‏‎‎‎‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‎‎‏‏‏‎All apps‎‏‎‎‏‎"</string>
     <string name="notifications_header" msgid="1404149926117359025">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‏‎‏‎‏‏‎‎‎‏‎‏‎‏‏‎‎‏‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎Notifications‎‏‎‎‏‎"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‎‎‏‏‏‎‎‏‎‏‎‏‏‏‎‏‎‏‏‏‏‎‎‎‏‏‏‎‏‎‎‏‏‎‎‏‎‎‏‎‏‏‎‏‎‏‏‎‏‎‎‏‎Touch &amp; hold to move a shortcut.‎‏‎‎‏‎"</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‏‏‎‎‏‎‏‎‎‏‏‎‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‎‎‎‏‏‎‎‏‎‏‎‎‏‎‏‎‏‎‏‏‎‏‎‎‎Double-tap &amp; hold to move a shortcut or use custom actions.‎‏‎‎‏‎"</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‎‏‏‏‎‏‎‎‎‎‏‎‏‎‏‎‏‎‎‏‏‏‏‎‏‏‎‎‏‎‎‎‏‎‏‏‎‏‎‏‎‎‎This is a system app and can\'t be uninstalled.‎‏‎‎‏‎"</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‎‎‎‎‏‎‏‏‏‏‎‎‎‏‎‎‏‎‏‎‏‏‎‏‎‎‎‏‎‏‎‎‏‎‎‎‎‏‏‏‎‏‏‎‏‎‏‏‎‎‎‎‎Edit Name‎‏‎‎‏‎"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‏‏‎‏‏‏‎‏‏‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‏‎‏‎‏‏‎‏‏‏‏‎‎‏‏‏‎‎‎Disabled ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‏‎‏‏‎‏‎‏‏‎‏‎‎‏‏‏‎‎‎‏‎‏‏‏‏‏‎‎‎‎‎‏‏‎‏‎‎‏‏‎‏‎‎‎‏‎‏‎‎‏‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME_2">%1$s</xliff:g>‎‏‎‎‏‏‏‎, has ‎‏‎‎‏‏‎<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>‎‏‎‎‏‏‏‎ notifications‎‏‎‎‏‎</item>
-      <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‏‎‏‏‎‏‎‏‏‎‏‎‎‏‏‏‎‎‎‏‎‏‏‏‏‏‎‎‎‎‎‏‏‎‏‎‎‏‏‎‏‎‎‎‏‎‏‎‎‏‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎, has ‎‏‎‎‏‏‎<xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g>‎‏‎‎‏‏‏‎ notification‎‏‎‎‏‎</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‎‎‏‏‎‎‎‏‎‎‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‏‏‏‎‏‎‎‎‏‎‎‎‏‏‏‎‎‏‎‎‎‎‎‎‏‎‎‏‏‎{app_name}‎‏‎‎‏‏‏‎ has # notification‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‎‎‏‏‎‎‎‏‎‎‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‏‏‏‎‏‎‎‎‏‎‎‎‏‏‏‎‎‏‎‎‎‎‎‎‏‎‎‏‏‎{app_name}‎‏‎‎‏‏‏‎ has # notifications‎‏‎‎‏‎}}"</string>
     <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>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 75c8151..3bd5f22 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Mantén presionado el widget para moverlo por la pantalla principal"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Agregar a pantalla principal"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Se agregó el widget de <xliff:g id="WIDGET_NAME">%1$s</xliff:g> a la pantalla principal"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> accesos directos</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> acceso directo</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# acceso directo}other{# accesos directos}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Buscar"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"No hay apps que coincidan con \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Buscar más apps"</string>
     <string name="label_application" msgid="8531721983832654978">"App"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Todas las apps"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notificaciones"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Mantén presionado para mover un acceso directo."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Presiona dos veces y mantén presionado para mover un acceso directo o usar acciones personalizadas."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Esta es una aplicación del sistema y no se puede desinstalar."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Editar nombre"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Se inhabilitó <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> tiene <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificaciones</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> tiene <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificación</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} tiene # notificación}other{{app_name} tiene # notificaciones}}"</string>
     <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>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index f8be80a..8fc279b 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Mantén pulsado el widget para moverlo por la pantalla de inicio"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Añadir a la pantalla de inicio"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> añadido a la pantalla de inicio"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other">Accesos directos: <xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g></item>
-      <item quantity="one">Acceso directo: <xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g></item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# combinación de teclas}other{# combinaciones de teclas}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Buscar"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"No se han encontrado aplicaciones que contengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Buscar más aplicaciones"</string>
     <string name="label_application" msgid="8531721983832654978">"Aplicación"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Todas las aplicaciones"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notificaciones"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Mantén pulsado un acceso directo para moverlo."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Toca dos veces y mantén pulsado un acceso directo para moverlo o usar acciones personalizadas."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Esta aplicación es del sistema y no se puede desinstalar."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Editar nombre"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Se ha inhabilitado <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> tiene <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificaciones</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> tiene <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificación</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} tiene # notificación}other{{app_name} tiene # notificaciones}}"</string>
     <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>
@@ -133,7 +125,7 @@
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Añadir a la pantalla de inicio"</string>
     <string name="action_move_here" msgid="2170188780612570250">"Mover elemento aquí"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Elemento añadido a la pantalla de inicio"</string>
-    <string name="item_removed" msgid="851119963877842327">"Elemento eliminado"</string>
+    <string name="item_removed" msgid="851119963877842327">"Elemento quitado"</string>
     <string name="undo" msgid="4151576204245173321">"Deshacer"</string>
     <string name="action_move" msgid="4339390619886385032">"Mover elemento"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover a la fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 291080a..72b88d3 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Vidina teisaldamiseks avakuval puudutage vidinat ja hoidke seda all"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Lisa avakuvale"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Vidin <xliff:g id="WIDGET_NAME">%1$s</xliff:g> lisati avakuvale"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> vidinat</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> vidin</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> otseteed</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> otsetee</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# vidin}other{# vidinat}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# otsetee}other{# otseteed}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Vidinad"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Otsing"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Päringule „<xliff:g id="QUERY">%1$s</xliff:g>” ei vastanud ükski rakendus"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Otsi rohkem rakendusi"</string>
     <string name="label_application" msgid="8531721983832654978">"Rakendus"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Kõik rakendused"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Märguanded"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Otsetee teisaldamiseks puudutage ja hoidke all."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Otsetee teisaldamiseks või kohandatud toimingute kasutamiseks topeltpuudutage ja hoidke all."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"See on süsteemirakendus ja seda ei saa desinstallida."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Muuda nime"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> on keelatud"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> märguannet</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> märguanne</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{Rakenduses {app_name} on # märguanne}other{Rakenduses {app_name} on # märguannet}}"</string>
     <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>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 0642030..6f6c0ca 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Eduki sakatuta widgeta hasierako pantailan zehar mugitzeko"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Gehitu hasierako pantailan"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widgeta hasierako pantailan gehitu da"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> lasterbide</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> lasterbide</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widget}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# lasterbide}other{# lasterbide}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgetak"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Bilatu"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Ez da aurkitu \"<xliff:g id="QUERY">%1$s</xliff:g>\" bilaketaren emaitzarik"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Bilatu aplikazio gehiago"</string>
     <string name="label_application" msgid="8531721983832654978">"Aplikazioa"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Aplikazio guztiak"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Jakinarazpenak"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Eduki sakatuta lasterbide bat mugitzeko."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Sakatu birritan eta eduki sakatuta lasterbide bat mugitzeko edo ekintza pertsonalizatuak erabiltzeko."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Sistema-aplikazioa da hau eta ezin da desinstalatu."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Editatu izena"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> desgaituta dago"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> aplikazioak <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> jakinarazpen ditu</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> aplikazioak <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> jakinarazpen du</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} aplikazioak # jakinarazpen dauka}other{{app_name} aplikazioak # jakinarazpen dauzka}}"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d/%2$d orria"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d/%2$d hasierako pantaila"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Hasierako pantailaren orri berria"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 13c6f2b..13ed7b2 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"ابزارک را لمس کنید و نگه دارید تا آن را در صفحه اصلی حرکت دهید"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"افزودن به صفحه اصلی"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"ابزارک <xliff:g id="WIDGET_NAME">%1$s</xliff:g> به صفحه اصلی اضافه شد"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ابزارک</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ابزارک</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> میان‌بر</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> میان‌بر</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{‏# ابزارک}one{‏# ابزارک}other{‏# ابزارک}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{‏# میان‌بر}one{‏# میان‌بر}other{‏# میان‌بر}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>،<xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ابزارک‌ها"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"جستجو"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"هیچ برنامه‌ای در مطابقت با «<xliff:g id="QUERY">%1$s</xliff:g>» پیدا نشد"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"جستجوی برنامه‌های بیشتر"</string>
     <string name="label_application" msgid="8531721983832654978">"برنامه"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"همه برنامه‌ها"</string>
     <string name="notifications_header" msgid="1404149926117359025">"اعلان‌ها"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"برای جابه‌جا کردن میان‌بر، لمس کنید و نگه دارید."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"برای جابه‌جا کردن میان‌بر یا استفاده از کنش‌های سفارشی، دوضربه بزنید و نگه دارید."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"این برنامه سیستمی است و حذف نصب نمی‌شود."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"ویرایش نام"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> غیرفعال شد"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>، <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> اعلان دارد</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>، <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> اعلان دارد</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} ‏# اعلان دارد}one{{app_name} ‏# اعلان دارد}other{{app_name} ‏# اعلان دارد}}"</string>
     <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>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 8764e51..68bc053 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Voit siirtää widgetiä aloitusnäytöllä koskettamalla sitä pitkään"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Lisää aloitusnäytölle"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget lisätty aloitusnäytölle: <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgetiä</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> pikakuvaketta</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> pikakuvake</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgetiä}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# pikakuvake}other{# pikakuvaketta}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgetit"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Haku"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"<xliff:g id="QUERY">%1$s</xliff:g> ei palauttanut sovelluksia."</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Hae lisää sovelluksia"</string>
     <string name="label_application" msgid="8531721983832654978">"Sovellus"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Kaikki sovellukset"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Ilmoitukset"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Kosketa pitkään, niin voit siirtää pikakuvaketta."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Kaksoisnapauta ja paina pitkään, niin voit siirtää pikakuvaketta tai käyttää muokattuja toimintoja."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Tämä on järjestelmäsovellus, eikä sitä voi poistaa."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Muokkaa nimeä"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> poistettiin käytöstä"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>: <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ilmoitusta</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>: <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ilmoitus</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name}: # ilmoitus}other{{app_name}: # ilmoitusta}}"</string>
     <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>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 6d9dcfd..3b85234 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Maintenez le doigt sur le widget pour le déplacer sur l\'écran d\'accueil"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Ajouter à l\'écran d\'accueil"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Le widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> a été ajouté à l\'écran d\'accueil"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> raccourci</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> raccourcis</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}other{# widgets}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# raccourci}one{# raccourci}other{# raccourcis}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Rechercher"</string>
@@ -63,6 +57,8 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Aucune application trouvée correspondant à « <xliff:g id="QUERY">%1$s</xliff:g> »"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Rechercher plus d\'applications"</string>
     <string name="label_application" msgid="8531721983832654978">"Application"</string>
+    <!-- no translation found for all_apps_label (5015784846527570951) -->
+    <skip />
     <string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Maintenez le doigt sur un raccourci pour le déplacer."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Touchez deux fois un raccourci et maintenez le doigt dessus pour le déplacer ou utiliser des actions personnalisées."</string>
@@ -91,10 +87,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Impossible de désinstaller cette application, car il s\'agit d\'une application système."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Modifier le nom"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"L\'application <xliff:g id="APP_NAME">%1$s</xliff:g> est désactivée"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> a <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notification</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> a <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificatio​ns</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} a # notification}one{{app_name} a # notification}other{{app_name} a # notifications}}"</string>
     <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>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index a6723b8..7e28c83 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Appuyez de manière prolongée sur le widget pour le déplacer sur l\'écran d\'accueil"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Ajouter à l\'écran d\'accueil"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ajouté à l\'écran d\'accueil"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> raccourci</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> raccourcis</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}other{# widgets}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# raccourci}one{# raccourci}other{# raccourcis}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Rechercher"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Aucune application ne correspond à la requête \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Rechercher plus d\'applications"</string>
     <string name="label_application" msgid="8531721983832654978">"Application"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Toutes les applis"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Appuyez de manière prolongée pour déplacer un raccourci."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Appuyez deux fois et maintenez la pression pour déplacer un raccourci ou utiliser les actions personnalisées."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Impossible de désinstaller cette application, car il s\'agit d\'une application système."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Modifier le nom"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> est désactivé."</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> comporte <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notification</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> comporte <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} a # notification}one{{app_name} a # notification}other{{app_name} a # notifications}}"</string>
     <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>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index aa225d9..46c017c 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Mantén premido o widget para movelo pola pantalla de inicio"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Engadir á pantalla de inicio"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Engadiuse o widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> á pantalla de inicio"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> atallos</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> atallo</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# atallo}other{# atallos}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Busca Widgets"</string>
@@ -63,6 +57,8 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Non se atoparon aplicacións que coincidan con \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Buscar máis aplicacións"</string>
     <string name="label_application" msgid="8531721983832654978">"Aplicación"</string>
+    <!-- no translation found for all_apps_label (5015784846527570951) -->
+    <skip />
     <string name="notifications_header" msgid="1404149926117359025">"Notificacións"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Mantén premido un atallo para movelo."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Toca dúas veces un atallo e manteno premido para movelo ou utiliza accións personalizadas."</string>
@@ -91,10 +87,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Esta aplicación é do sistema e non se pode desinstalar."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Edita o nome"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Desactivouse <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other">A aplicación <xliff:g id="APP_NAME_2">%1$s</xliff:g> ten <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificacións</item>
-      <item quantity="one">A aplicación <xliff:g id="APP_NAME_0">%1$s</xliff:g> ten <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificación</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} ten # notificación}other{{app_name} ten # notificacións}}"</string>
     <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>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index ae62c34..ec8a708 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"વિજેટને હોમ સ્ક્રીનની આજુબાજુ ખસેડવા માટે, તેને ટચ કરીને થોડીવાર દબાવી રાખો"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"હોમ સ્ક્રીન પર ઉમેરો"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"હોમ સ્ક્રીન પર <xliff:g id="WIDGET_NAME">%1$s</xliff:g> વિજેટ ઉમેર્યુ"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> વિજેટ</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> વિજેટ</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> શૉર્ટકટ</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> શૉર્ટકટ</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# વિજેટ}one{# વિજેટ}other{# વિજેટ}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# શૉર્ટકટ}one{# શૉર્ટકટ}other{# શૉર્ટકટ}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"વિજેટ"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"શોધ"</string>
@@ -63,6 +57,8 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"થી મેળ ખાતી કોઈ ઍપ્લિકેશનો મળી નથી"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"વધુ ઍપ્લિકેશનો શોધો"</string>
     <string name="label_application" msgid="8531721983832654978">"ઍપ"</string>
+    <!-- no translation found for all_apps_label (5015784846527570951) -->
+    <skip />
     <string name="notifications_header" msgid="1404149926117359025">"નોટિફિકેશન"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"શૉર્ટકટ ખસેડવા ટચ કરીને થોડી વાર દબાવી રાખો."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"શૉર્ટકટ ખસેડવા બે વાર ટૅપ કરીને દબાવી રાખો અથવા કસ્ટમ ક્રિયાઓનો ઉપયોગ કરો."</string>
@@ -91,10 +87,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"આ એક સિસ્ટમ ઍપ્લિકેશન છે અને અનઇન્સ્ટોલ કરી શકાતી નથી."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"નામમાં ફેરફાર કરો"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> અક્ષમ કરી"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>ના <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> નોટિફિકેશન છે</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>ના <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> નોટિફિકેશન છે</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name}ના # નોટિફિકેશન છે}one{{app_name}ના # નોટિફિકેશન છે}other{{app_name}ના # નોટિફિકેશન છે}}"</string>
     <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>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index af3a5bc..b9de9fc 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"होम स्क्रीन पर यहां-वहां ले जाने के लिए विजेट को दबाकर रखें"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"होम स्क्रीन पर जोड़ें"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> विजेट को होम स्क्रीन पर जोड़ा गया"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> विजेट</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> विजेट</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> शॉर्टकट</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> शॉर्टकट</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# विजेट}one{# विजेट}other{# विजेट}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# शॉर्टकट}one{# शॉर्टकट}other{# शॉर्टकट}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"विजेट"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"खोजें"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" से मिलता-जुलता कोई ऐप्लिकेशन नहीं मिला"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"और ऐप सर्च करें"</string>
     <string name="label_application" msgid="8531721983832654978">"ऐप्लिकेशन"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"सभी ऐप्लिकेशन"</string>
     <string name="notifications_header" msgid="1404149926117359025">"सूचनाएं"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"किसी शॉर्टकट को एक से दूसरी जगह ले जाने के लिए, उसे दबाकर रखें."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"किसी शॉर्टकट को एक से दूसरी जगह ले जाने के लिए, उस पर दो बार टैप करके दबाकर रखें या पसंद के मुताबिक कार्रवाइयां इस्तेमाल करें."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"यह एक सिस्टम ऐप्लिकेशन है और इसे अनइंस्टॉल नहीं किया जा सकता."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"नाम में बदलाव करें"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> अक्षम है"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> की <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचना है</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> की <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचनाएं हैं</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} से जुड़ी # सूचना है}one{{app_name} से जुड़ी # सूचना है}other{{app_name} से जुड़ी # सूचनाएं हैं}}"</string>
     <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>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index db7a237..899bd31 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -36,16 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Dodirnite i zadržite widget da biste ga pomicali po početnom zaslonu"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Dodaj na početni zaslon"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> dodan je na početni zaslon"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
-      <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgeta</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgeta</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> prečac</item>
-      <item quantity="few"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> prečaca</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> prečaca</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}few{# widgeta}other{# widgeta}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# prečac}one{# prečac}few{# prečaca}other{# prečaca}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgeti"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Pretražite"</string>
@@ -65,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nema aplikacija podudarnih s upitom \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Traži više aplikacija"</string>
     <string name="label_application" msgid="8531721983832654978">"Aplikacija"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Sve aplikacije"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Obavijesti"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Dodirnite i zadržite da biste premjestili prečac."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Dvaput dodirnite i zadržite pritisak da biste premjestili prečac ili upotrijebite prilagođene radnje."</string>
@@ -93,11 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ovo je aplikacija sustava i ne može se ukloniti."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Uređivanje naziva"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> onemogućena"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavijest</item>
-      <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavijesti</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavijesti</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{Aplikacija {app_name} ima # obavijest}one{Aplikacija {app_name} ima # obavijest}few{Aplikacija {app_name} ima # obavijesti}other{Aplikacija {app_name} ima # obavijesti}}"</string>
     <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>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index d6b2fef..8ea2386 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Tartsa lenyomva a modult a kezdőképernyőn való mozgatáshoz"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Hozzáadás a kezdőképernyőhöz"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> modul hozzáadva a kezdőképernyőhöz"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> modul</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> modul</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> parancsikon</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> parancsikon</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# modul}other{# modul}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# gyorsparancs}other{# gyorsparancs}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Modulok"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Keresés"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nem található alkalmazás a(z) „<xliff:g id="QUERY">%1$s</xliff:g>” lekérdezésre"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"További alkalmazások keresése"</string>
     <string name="label_application" msgid="8531721983832654978">"Alkalmazás"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Összes alkalmazás"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Értesítések"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Tartsa lenyomva a parancsikont az áthelyezéshez."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Parancsikon áthelyezéséhez koppintson duplán, és tartsa nyomva az ujját, vagy használjon egyéni műveleteket."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ez egy rendszeralkalmazás, és nem lehet eltávolítani."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Név módosítása"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> letiltva"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other">A(z) <xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> értesítéssel rendelkezik</item>
-      <item quantity="one">A(z) <xliff:g id="APP_NAME_0">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> értesítéssel rendelkezik</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{A(z) {app_name} # értesítéssel rendelkezik}other{A(z) {app_name} # értesítéssel rendelkezik}}"</string>
     <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>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 388cf27..b757d2b 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Հպեք վիջեթին և պահեք տեղափոխելու համար"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Ավելացնել հիմնական էկրանին"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> վիջեթն ավելացվել է հիմնական էկրանին"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> վիջեթ</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> վիջեթ</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> դյուրանցում</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> դյուրանցում</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# վիջեթ}one{# վիջեթ}other{# վիջեթ}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# դյուրանցում}one{# դյուրանցում}other{# դյուրանցում}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Վիջեթներ"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Որոնեք"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"«<xliff:g id="QUERY">%1$s</xliff:g>» հարցմանը համապատասխանող հավելվածներ չեն գտնվել"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Որոնել այլ հավելվածներ"</string>
     <string name="label_application" msgid="8531721983832654978">"Հավելված"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Բոլոր հավելվածները"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Ծանուցումներ"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Հպեք և պահեք՝ դյուրանցում տեղափոխելու համար։"</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Կրկնակի հպեք և պահեք՝ դյուրանցում տեղափոխելու համար, կամ օգտվեք հատուկ գործողություններից։"</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Սա համակարգային ծրագիր է և չի կարող ապատեղադրվել:"</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Փոխել անունը"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածն անջատված է"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ունի <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ծանուցում</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ունի <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ծանուցում</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{«{app_name}» հավելվածն ունի # ծանուցում}one{«{app_name}» հավելվածն ունի # ծանուցում}other{«{app_name}» հավելվածն ունի # ծանուցում}}"</string>
     <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>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 4aafc5f..1846883 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Sentuh lama widget untuk memindahkannya di sekitar Layar utama"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Tambahkan ke Layar utama"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ditambahkan ke layar utama"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> pintasan</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> pintasan</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widget}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# pintasan}other{# pintasan}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Telusuri"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Tidak ditemukan aplikasi yang cocok dengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Telusuri aplikasi lainnya"</string>
     <string name="label_application" msgid="8531721983832654978">"Aplikasi"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Semua aplikasi"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notifikasi"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Sentuh lama untuk memindahkan pintasan."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Ketuk dua kali &amp; tahan untuk memindahkan pintasan atau gunakan tindakan khusus."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ini adalah aplikasi sistem dan tidak dapat dicopot pemasangannya."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Sunting Nama"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> dinonaktifkan"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, memiliki <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifikasi</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, memiliki <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notifikasi</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} memiliki # notifikasi}other{{app_name} memiliki # notifikasi}}"</string>
     <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>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 8c2d472..2b0a97f 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Haltu fingri á græjunni til að hreyfa hana um heimaskjáinn"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Bæta á heimaskjá"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> græju bætt við heimaskjá"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> græja</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> græjur</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> flýtileið</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> flýtileiðir</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# græja}one{# græja}other{# græjur}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# flýtileið}one{# flýtileið}other{# flýtileiðir}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Græjur"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Leit"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Ekki fundust forrit sem samsvara „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Leita að fleiri forritum"</string>
     <string name="label_application" msgid="8531721983832654978">"Forrit"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Öll forrit"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Tilkynningar"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Haltu fingri á flýtileið til að færa hana."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Ýttu tvisvar og haltu fingri á flýtileið til að færa hana eða notaðu sérsniðnar aðgerðir."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Þetta er kerfisforrit sem ekki er hægt að fjarlægja."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Breyta nafni"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Óvirkt <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, er með <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> tilkynningu</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, er með <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> tilkynningar</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} er með # tilkynningu}one{{app_name} er með # tilkynningu}other{{app_name} er með # tilkynningar}}"</string>
     <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>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index cedcafa..3758093 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Tocca e tieni premuto il widget per spostarlo nella schermata Home"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Aggiungi a schermata Home"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> aggiunto alla schermata Home"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> scorciatoie</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> scorciatoia</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widget}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# scorciatoia}other{# scorciatoie}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Cerca"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nessuna app trovata corrispondente a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Cerca altre app"</string>
     <string name="label_application" msgid="8531721983832654978">"App"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Tutte le app"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notifiche"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Tocca e tieni premuto per spostare una scorciatoia."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Tocca due volte e tieni premuto per spostare una scorciatoia o per usare le azioni personalizzate."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Questa è un\'app di sistema e non può essere disinstallata."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Modifica nome"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"App <xliff:g id="APP_NAME">%1$s</xliff:g> disattivata"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ha <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifiche</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, ha <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notifica</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} ha # notifica}other{{app_name} ha # notifiche}}"</string>
     <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>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 14153f7..52fe570 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -36,18 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"יש ללחוץ לחיצה ארוכה על הווידג\'ט כדי להזיז אותו ברחבי מסך הבית"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"הוספה למסך הבית"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"הווידג\'ט <xliff:g id="WIDGET_NAME">%1$s</xliff:g> נוסף למסך הבית"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="two"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ווידג\'טים</item>
-      <item quantity="many"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ווידג\'טים</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ווידג\'טים</item>
-      <item quantity="one">ווידג\'ט אחד (<xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g>)</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="two"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> קיצורי דרך</item>
-      <item quantity="many"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> קיצורי דרך</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> קיצורי דרך</item>
-      <item quantity="one">קיצור דרך אחד (<xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g>)</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{ווידג\'ט אחד}two{# ווידג\'טים}many{# ווידג\'טים}other{# ווידג\'טים}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{קיצור דרך אחד}two{# קיצורי דרך}many{# קיצורי דרך}other{# קיצורי דרך}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ווידג\'טים"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"חיפוש"</string>
@@ -67,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"לא נמצאו אפליקציות התואמות ל-\"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"חיפוש אפליקציות נוספות"</string>
     <string name="label_application" msgid="8531721983832654978">"אפליקציה"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"כל האפליקציות"</string>
     <string name="notifications_header" msgid="1404149926117359025">"התראות"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"כדי להעביר קיצור דרך למקום אחר יש לגעת ולא להרפות."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"כדי להעביר קיצור דרך למקום אחר או להשתמש בפעולות מותאמות אישית\' יש ללחוץ פעמיים ולא להרפות."</string>
@@ -95,12 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"זוהי אפליקציית מערכת ולא ניתן להסיר את התקנתה."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"עריכת השם"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> מושבתת"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="two">לאפליקציה <xliff:g id="APP_NAME_2">%1$s</xliff:g> יש <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> התראות</item>
-      <item quantity="many">לאפליקציה <xliff:g id="APP_NAME_2">%1$s</xliff:g> יש <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> התראות</item>
-      <item quantity="other">לאפליקציה <xliff:g id="APP_NAME_2">%1$s</xliff:g> יש <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> התראות</item>
-      <item quantity="one">לאפליקציה <xliff:g id="APP_NAME_0">%1$s</xliff:g> יש התראה אחת (<xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g>)</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{לאפליקציה {app_name} יש התראה אחת}two{לאפליקציה {app_name} יש # התראות}many{לאפליקציה {app_name} יש # התראות}other{לאפליקציה {app_name} יש # התראות}}"</string>
     <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>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 594f340..d789a39 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"ウィジェットを押し続けると、ホーム画面上に移動できます。"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"ホーム画面に追加"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"「<xliff:g id="WIDGET_NAME">%1$s</xliff:g>」ウィジェットをホーム画面に追加しました"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> 件のウィジェット</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> 件のウィジェット</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> 件のショートカット</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> 件のショートカット</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# 件のウィジェット}other{# 件のウィジェット}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# 件のショートカット}other{# 件のショートカット}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>、<xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ウィジェット"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"検索"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"「<xliff:g id="QUERY">%1$s</xliff:g>」に一致するアプリは見つかりませんでした"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"他のアプリを検索"</string>
     <string name="label_application" msgid="8531721983832654978">"アプリ"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"すべてのアプリ"</string>
     <string name="notifications_header" msgid="1404149926117359025">"通知"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"長押ししてショートカットを移動してください。"</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ショートカットをダブルタップして長押ししながら移動するか、カスタム操作を使用してください。"</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"このシステムアプリはアンインストールできません。"</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"名前の編集"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」は無効です"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> の通知が <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> 件あります</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> の通知が <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> 件あります</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} の通知が # 件あります}other{{app_name} の通知が # 件あります}}"</string>
     <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>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 7cc364f..6dd7cbe 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"ხანგრძლივად შეეხეთ ვიჯეტს მთავარ ეკრანზე მის გადასაადგილებლად"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"მთავარ ეკრანზე დამატება"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ვიჯეტი დამატებულია მთავარ ეკრანზე"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ვიჯეტი</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> ვიჯეტი</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> მალსახმობი</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> მალსახმობი</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ვიჯეტი}other{# ვიჯეტი}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# მალსახმობი}other{# მალსახმობი}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ვიჯეტები"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"ძიება"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"„<xliff:g id="QUERY">%1$s</xliff:g>“-ის თანხვედრი აპები არ მოიძებნა"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"მეტი აპის პოვნა"</string>
     <string name="label_application" msgid="8531721983832654978">"აპი"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"ყველა აპი"</string>
     <string name="notifications_header" msgid="1404149926117359025">"შეტყობინებები"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"შეხებით აირჩიეთ და გეჭიროთ მალსახმობის გადასაადგილებლად."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ორმაგი შეხებით აირჩიეთ და გეჭიროთ მალსახმობის გადასაადგილებლად ან მორგებული მოქმედებების გამოსაყენებლად."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ეს სისტემური აპია და მისი წაშლა შეუძლებელია."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"სახელის რედაქტირება"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> გაითიშა"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>-ში არის <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> შეტყობინება</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>-ში არის <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> შეტყობინება</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name}-ში # შეტყობინებაა}other{{app_name}-ში # შეტყობინებაა}}"</string>
     <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>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index ebacc83..e6a5aaf 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Негізгі экранда қозғалту үшін виджетті басып тұрыңыз."</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Негізгі экранға қосу"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> виджеті негізгі экранға енгізілді."</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> виджет</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> виджет</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> таңбаша</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> таңбаша</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# виджет}other{# виджет}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# таңбаша}other{# таңбаша}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Іздеу"</string>
@@ -63,6 +57,8 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" сұрауына сәйкес келетін қолданбалар жоқ"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Қосымша қолданбалар іздеу"</string>
     <string name="label_application" msgid="8531721983832654978">"Қолданба"</string>
+    <!-- no translation found for all_apps_label (5015784846527570951) -->
+    <skip />
     <string name="notifications_header" msgid="1404149926117359025">"Хабарландырулар"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Таңбашаны жылжыту үшін басып тұрыңыз."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Таңбашаны жылжыту үшін екі рет түртіңіз де, ұстап тұрыңыз немесе арнаулы әрекеттерді пайдаланыңыз."</string>
@@ -91,10 +87,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Бұл жүйе қолданбасы, сондықтан оны алу мүмкін емес."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Атын өңдеу"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> өшірілді"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> қолданбасында <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> хабарландыру бар</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> қолданбасында <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> хабарландыру бар</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} қолданбасында # хабарландыру бар}other{{app_name} қолданбасында # хабарландыру бар}}"</string>
     <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>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 4ad70df..8089b94 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"ចុចធាតុក្រាហ្វិក​ឱ្យជាប់ ដើម្បីផ្លាស់ទីវា​ជុំវិញអេក្រង់ដើម"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"បញ្ចូល​ទៅអេក្រង់ដើម"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"បានបញ្ចូល​ធាតុក្រាហ្វិក <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ទៅ​អេក្រង់ដើម"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other">ធាតុ​ក្រាហ្វិក <xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g></item>
-      <item quantity="one">ធាតុ​ក្រាហ្វិក <xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g></item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other">ផ្លូវកាត់ <xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g></item>
-      <item quantity="one">ផ្លូវកាត់ <xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g></item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{ធាតុ​ក្រាហ្វិក #}other{ធាតុ​ក្រាហ្វិក #}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{ផ្លូវកាត់ #}other{ផ្លូវកាត់ #}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ធាតុ​ក្រាហ្វិក"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"ស្វែងរក"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"រកមិនឃើញកម្មវិធី​ដែលត្រូវគ្នាជាមួយ \"<xliff:g id="QUERY">%1$s</xliff:g>\" ទេ"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"ស្វែងរកកម្មវិធីច្រើនទៀត"</string>
     <string name="label_application" msgid="8531721983832654978">"កម្មវិធី"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"កម្មវិធី​ទាំងអស់"</string>
     <string name="notifications_header" msgid="1404149926117359025">"ការ​ជូនដំណឹង"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ចុចឱ្យជាប់​ដើម្បីផ្លាស់ទី​ផ្លូវកាត់​។"</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ចុចពីរដង រួចសង្កត់ឱ្យជាប់ ដើម្បីផ្លាស់ទី​ផ្លូវកាត់ ឬប្រើ​សកម្មភាព​តាមបំណង​។"</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"នេះ​​​ជា​កម្មវិធី​ប្រព័ន្ធ មិន​អាច​លុប​បាន​ទេ។"</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"កែ​ឈ្មោះ"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"បានបិទដំណើរការ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, មាន​ការជូនដំណឹង <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g></item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, មាន​ការជូនដំណឺង <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g></item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} មានការជូនដំណឹង #}other{{app_name} មានការជូនដំណឹង #}}"</string>
     <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>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 707eb5c..e358d29 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"ಮುಖಪುಟದ ಪರದೆ ಸುತ್ತ ವಿಜೆಟ್ ಅನ್ನು ಸರಿಸಲು, ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಒತ್ತಿ ಹಿಡಿದುಕೊಳ್ಳಿ"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"ಮುಖಪುಟಕ್ಕೆ ಸೇರಿಸಿ"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"ಹೋಮ್‌ಸ್ಕ್ರೀನ್‌ಗೆ <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ವಿಜೆಟ್ ಅನ್ನು ಸೇರಿಸಲಾಗಿದೆ"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ವಿಜೆಟ್‌ಗಳು</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ವಿಜೆಟ್‌ಗಳು</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ವಿಜೆಟ್}one{# ವಿಜೆಟ್‌ಗಳು}other{# ವಿಜೆಟ್‌ಗಳು}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# ಶಾರ್ಟ್‌ಕಟ್}one{# ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು}other{# ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ವಿಜೆಟ್‌ಗಳು"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"ಹುಡುಕಿ"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ಹೊಂದಿಕೆಯ ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಕಂಡುಬಂದಿಲ್ಲ"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"ಮತ್ತಷ್ಟು ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಹುಡುಕಿ"</string>
     <string name="label_application" msgid="8531721983832654978">"ಆ್ಯಪ್"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳು"</string>
     <string name="notifications_header" msgid="1404149926117359025">"ಅಧಿಸೂಚನೆಗಳು"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ಶಾರ್ಟ್‌ಕಟ್ ಸರಿಸಲು ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ಶಾರ್ಟ್‌ಕಟ್ ಸರಿಸಲು ಅಥವಾ ಕಸ್ಟಮ್ ಕ್ರಿಯೆಗಳನ್ನು ಬಳಸಲು ಡಬಲ್-ಟ್ಯಾಪ್ ಮಾಡಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ಇದೊಂದು ಅಪ್ಲಿಕೇಶನ್ ಆಗಿದೆ ಮತ್ತು ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"ಹೆಸರನ್ನು ಎಡಿಟ್ ಮಾಡಿ"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ಅಧಿಸೂಚನೆಗಳನ್ನು ಹೊಂದಿದೆ</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ಅಧಿಸೂಚನೆಗಳನ್ನು ಹೊಂದಿದೆ</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} ಆ್ಯಪ್‌ # ಅಧಿಸೂಚನೆಯನ್ನು ಹೊಂದಿದೆ}one{{app_name} ಆ್ಯಪ್‌ # ಅಧಿಸೂಚನೆಗಳನ್ನು ಹೊಂದಿದೆ}other{{app_name} ಆ್ಯಪ್‌ # ಅಧಿಸೂಚನೆಗಳನ್ನು ಹೊಂದಿದೆ}}"</string>
     <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>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 48ddc98..e2d29d4 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"홈 화면에서 위젯을 이동하려면 길게 터치하세요."</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"홈 화면에 추가"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> 위젯이 홈 화면에 추가됨"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other">위젯 <xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g>개</item>
-      <item quantity="one">위젯 <xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g>개</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other">바로가기 <xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g>개</item>
-      <item quantity="one">바로가기 <xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g>개</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{위젯 #개}other{위젯 #개}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{바로가기 #개}other{바로가기 #개}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"위젯"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"검색"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\'<xliff:g id="QUERY">%1$s</xliff:g>\'과(와) 일치하는 앱이 없습니다."</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"더 많은 앱 검색"</string>
     <string name="label_application" msgid="8531721983832654978">"앱"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"모든 앱"</string>
     <string name="notifications_header" msgid="1404149926117359025">"알림"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"길게 터치하여 바로가기를 이동하세요."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"두 번 탭한 다음 길게 터치하여 바로가기를 이동하거나 맞춤 작업을 사용하세요."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"시스템 앱은 제거할 수 없습니다."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"이름 수정"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> 사용 안함"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>에 <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>개의 알림이 있음</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>에 <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g>개의 알림이 있음</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} 알림 #개}other{{app_name}알림 #개}}"</string>
     <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>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 1b1ebef..8769ef1 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Башкы экранга жылдыруу үчүн виджетти коё бербей басып туруңуз"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Башкы экранга кошуу"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> виджети башкы экранга кошулду"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> виджет</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> виджет</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ыкчам баскыч</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> ыкчам баскыч</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# виджет}other{# виджет}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# ыкчам баскыч}other{# ыкчам баскыч}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Издөө"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" сурамына дал келген колдонмолор табылган жок"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Көбүрөөк колдонмолорду издөө"</string>
     <string name="label_application" msgid="8531721983832654978">"Колдонмо"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Бардык колдонмолор"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Билдирмелер"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Ыкчам баскычты жылдыруу үчүн коё бербей басып туруңуз."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Ыкчам баскычты жылдыруу үчүн эки жолу таптап, кармап туруңуз же ыңгайлаштырылган аракеттерди колдонуңуз."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Бул системдик колдонмо жана аны чечкенге болбойт."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Аталышын түзөтүү"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> өчүрүлгөн"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> билдирмеси бар</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> билдирмеси бар</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name}, # билдирмеси бар}other{{app_name}, # билдирмеси бар}}"</string>
     <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>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 3698f62..83c9ae3 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"ແຕະວິດເຈັດຄ້າງໄວ້ເພື່ອຍ້າຍມັນໄປມາຢູ່ໂຮມສະກຣີນ"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"ເພີ່ມໄປໃສ່ໂຮມສະກຣີນ"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"ເພີ່ມວິດເຈັດ <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ໃສ່ໂຮມສະກຣີນແລ້ວ"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ວິດເຈັດ</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> ວິດເຈັດ</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ທາງລັດ</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> ທາງລັດ</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ວິດເຈັດ}other{# ວິດເຈັດ}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# ທາງລັດ}other{# ທາງລັດ}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ວິດເຈັດ"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"ຊອກຫາ"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"ບໍ່ພົບແອັບທີ່ກົງກັບ \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"ຊອກຫາແອັບເພີ່ມເຕີມ"</string>
     <string name="label_application" msgid="8531721983832654978">"ແອັບ"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"ແອັບທັງໝົດ"</string>
     <string name="notifications_header" msgid="1404149926117359025">"ການແຈ້ງເຕືອນ"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ແຕະຄ້າງໄວ້ເພື່ອຍ້າຍທາງລັດ."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ແຕະສອງເທື່ອຄ້າງໄວ້ເພື່ອຍ້າຍທາງລັດ ຫຼື ໃຊ້ຄຳສັ່ງກຳນົດເອງ."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ນີ້ແມ່ນແອັບຯຂອງລະບົບ ແລະບໍ່ສາມາດຖອນການຕິດຕັ້ງອອກໄດ້."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"ແກ້ໄຂຊື່"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"ປິດການນຳໃຊ້ <xliff:g id="APP_NAME">%1$s</xliff:g> ແລ້ວ"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ມີ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ການແຈ້ງເຕືອນ</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, ມີ <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ການແຈ້ງເຕືອນ</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} ມີ # ການແຈ້ງເຕືອນ}other{{app_name} ມີ # ການແຈ້ງເຕືອນ}}"</string>
     <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>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 854fc30..5876cdb 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -36,18 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Paliesdami ir palaikydami valdiklį galite judėti pagrindiniame ekrane"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Pridėti prie pagrindinio ekrano"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Valdiklis „<xliff:g id="WIDGET_NAME">%1$s</xliff:g>“ pridėtas prie pagrindinio ekrano"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> valdiklis</item>
-      <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> valdikliai</item>
-      <item quantity="many"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> valdiklio</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> valdiklių</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> spartusis klavišas</item>
-      <item quantity="few"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> spartieji klavišai</item>
-      <item quantity="many"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> sparčiojo klavišo</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> sparčiųjų klavišų</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# valdiklis}one{# valdiklis}few{# valdikliai}many{# valdiklio}other{# valdiklių}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# spartusis klavišas}one{# spartusis klavišas}few{# spartieji klavišai}many{# sparčiojo klavišo}other{# sparčiųjų klavišų}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Valdikliai"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Paieška"</string>
@@ -67,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nerasta jokių užklausą „<xliff:g id="QUERY">%1$s</xliff:g>“ atitinkančių programų"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Ieškoti daugiau programų"</string>
     <string name="label_application" msgid="8531721983832654978">"Programa"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Visos programos"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Pranešimai"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Dukart pal. ir palaik., kad perk. spart. klavišą."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Dukart palieskite ir palaikykite, kad perkeltumėte spartųjį klavišą ar naudotumėte tinkintus veiksmus."</string>
@@ -95,12 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Tai sistemos programa ir jos negalima pašalinti."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Pavadinimo redagavimas"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ išjungta"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one">Programoje „<xliff:g id="APP_NAME_2">%1$s</xliff:g>“ yra <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pranešimas</item>
-      <item quantity="few">Programoje „<xliff:g id="APP_NAME_2">%1$s</xliff:g>“ yra <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pranešimai</item>
-      <item quantity="many">Programoje „<xliff:g id="APP_NAME_2">%1$s</xliff:g>“ yra <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pranešimo</item>
-      <item quantity="other">Programoje „<xliff:g id="APP_NAME_2">%1$s</xliff:g>“ yra <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pranešimų</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{Programoje „{app_name}“ yra # pranešimas}one{Programoje „{app_name}“ yra # pranešimas}few{Programoje „{app_name}“ yra # pranešimai}many{Programoje „{app_name}“ yra # pranešimo}other{Programoje „{app_name}“ yra # pranešimų}}"</string>
     <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>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 7f47f26..d262b23 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -36,16 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Pieskarieties logrīkam un turiet to, lai to pārvietotu pa sākuma ekrānu."</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Pievienot sākuma ekrānam"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Logrīks “<xliff:g id="WIDGET_NAME">%1$s</xliff:g>” ir pievienots sākuma ekrānam"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="zero"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> logrīku</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> logrīks</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> logrīki</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="zero"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> saīšņu</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> saīsne</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> saīsnes</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# logrīks}zero{# logrīku}one{# logrīks}other{# logrīki}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# saīsne}zero{# saīšņu}one{# saīsne}other{# saīsnes}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Logrīki"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Meklēt"</string>
@@ -65,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Vaicājumam “<xliff:g id="QUERY">%1$s</xliff:g>” neatbilda neviena lietotne"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Meklēt citas lietotnes"</string>
     <string name="label_application" msgid="8531721983832654978">"Lietotne"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Visas lietotnes"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Paziņojumi"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Lai pārvietotu saīsni, pieskarieties un turiet."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Lai pārvietotu saīsni, uz tās veiciet dubultskārienu un turiet. Varat arī veikt pielāgotas darbības."</string>
@@ -93,11 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Šī ir sistēmas lietotne, un to nevar atinstalēt."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Nosaukuma rediģēšana"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> ir atspējota"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="zero">Lietotnē <xliff:g id="APP_NAME_2">%1$s</xliff:g> ir <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> paziņojumi</item>
-      <item quantity="one">Lietotnē <xliff:g id="APP_NAME_2">%1$s</xliff:g> ir <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> paziņojums</item>
-      <item quantity="other">Lietotnē <xliff:g id="APP_NAME_2">%1$s</xliff:g> ir <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> paziņojumi</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{Lietotnē {app_name} ir # paziņojums}zero{Lietotnē {app_name} ir # paziņojumi}one{Lietotnē {app_name} ir # paziņojums}other{Lietotnē {app_name} ir # paziņojumi}}"</string>
     <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>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index aebbf6f..3936a67 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Допрете го и задржете го виџетот за да го движите наоколу на почетниот екран"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Додај на почетниот екран"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Виџетот <xliff:g id="WIDGET_NAME">%1$s</xliff:g> е додаден на почетниот екран"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> виџет</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> виџети</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> кратенка</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> кратенки</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# виџет}one{# виџет}other{# виџети}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# кратенка}one{# кратенка}other{# кратенки}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Виџети"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Пребарувајте"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Не се најдени апликации што одговараат на „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Пребарај други апликации"</string>
     <string name="label_application" msgid="8531721983832654978">"Апликација"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Сите апликации"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Известувања"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Допрете и задржете за да преместите кратенка."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Допрете двапати и задржете за да преместите кратенка или користете приспособени дејства."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ова е системска апликација и не може да се деинсталира."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Изменете го името"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> е оневозможена"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> известување</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> известувања</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} има # известување}one{{app_name} има # известување}other{{app_name} има # известувања}}"</string>
     <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>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 9334cf8..613db56 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"ഹോം സ്‌ക്രീനിന് ചുറ്റും വിജറ്റ് നീക്കാൻ അതിൽ സ്‌പർശിച്ച് പിടിക്കുക"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"ഹോം സ്‌ക്രീനിലേക്ക് ചേർക്കുക"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> വിജറ്റ് ഹോം സ്‌ക്രീനിലേക്ക് ചേർത്തു"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> വിജറ്റുകൾ</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> വിജറ്റ്</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> കുറുക്കുവഴികൾ</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> കുറുക്കുവഴി</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# വിജറ്റ്}other{# വിജറ്റുകൾ}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# കുറുക്കുവഴി}other{# കുറുക്കുവഴികൾ}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"വിജറ്റുകൾ"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"തിരയുക"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" എന്നതുമായി പൊരുത്തപ്പെടുന്ന ആപ്പുകളൊന്നും കണ്ടെത്തിയില്ല"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"കൂടുതൽ ആപ്പുകൾക്ക് തിരയുക"</string>
     <string name="label_application" msgid="8531721983832654978">"ആപ്പ്"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"എല്ലാ ആപ്പുകളും"</string>
     <string name="notifications_header" msgid="1404149926117359025">"അറിയിപ്പുകൾ"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"കുറുക്കുവഴി നീക്കാൻ സ്‌പർശിച്ച് പിടിക്കുക."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"കുറുക്കുവഴി നീക്കാൻ ഡബിൾ ടാപ്പ് ചെയ്യൂ, ഹോൾഡ് ചെയ്യൂ അല്ലെങ്കിൽ ഇഷ്‌ടാനുസൃത പ്രവർത്തനങ്ങൾ ഉപയോഗിക്കൂ."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ഇതൊരു സിസ്‌റ്റം അപ്ലിക്കേഷനായതിനാൽ അൺഇൻസ്‌റ്റാളുചെയ്യാനാവില്ല."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"പേര് എഡിറ്റ് ചെയ്യുക"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> പ്രവർത്തനരഹിതമാക്കി"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> എന്ന ആപ്പിന്, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> അറിയിപ്പുകൾ ഉണ്ട്</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> എന്ന ആപ്പിന്, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> അറിയിപ്പുണ്ട്</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} ആപ്പിന് # അറിയിപ്പുണ്ട്}other{{app_name} ആപ്പിന് # അറിയിപ്പുകളുണ്ട്}}"</string>
     <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>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 3e05cb4..99b3d2f 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Жижиг хэрэгслийг Үндсэн нүүрний эргэн тойронд зөөхийн тулд түүнд хүрээд, удаан дарна уу"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Үндсэн нүүрэнд нэмэх"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> виджетийг үндсэн нүүрэнд нэмсэн"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> жижиг хэрэгсэл</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> жижиг хэрэгсэл</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> товчлол</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> товчлол</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# виджет}other{# виджет}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# товчлол}other{# товчлол}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Виджет"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Хайх"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"-д тохирох апп олдсонгүй"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Бусад апп-г хайх"</string>
     <string name="label_application" msgid="8531721983832654978">"Апп"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Бүх апп"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Мэдэгдэл"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Товчлолыг зөөхийн тулд хүрээд, удаан дарна уу."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Товчлолыг зөөх эсвэл захиалгат үйлдлийг ашиглахын тулд хоёр товшоод, удаан дарна уу."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Энэ апп нь системийн апп ба устгах боломжгүй."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Нэр засах"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г идэвхгүй болгосон"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> мэдэгдэлтэй байна</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> мэдэгдэлтэй байна</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} дээр # мэдэгдэл байна}other{{app_name} дээр # мэдэгдэл байна}}"</string>
     <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>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 3eb3928..4b9ef33 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"होम स्क्रीनवर ते हलवण्यासाठी विजेटला स्पर्श करा आणि धरून ठेवा"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"होम स्‍क्रीनवर जोडा"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> हे विजेट तुमच्या होम स्क्रीनवर जोडले आहे"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> विजेट</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> विजेट</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> शॉर्टकट</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> शॉर्टकट</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# विजेट}other{# विजेट}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# शॉर्टकट}other{# शॉर्टकट}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"विजेट"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"शोधा"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" शी जुळणारे कोणतेही अ‍ॅप्स आढळले नाहीत"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"अधिक अ‍ॅप्स शोधा"</string>
     <string name="label_application" msgid="8531721983832654978">"ॲप"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"सर्व अ‍ॅप्स"</string>
     <string name="notifications_header" msgid="1404149926117359025">"सूचना"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"शॉर्टकट हलवण्यासाठी स्पर्श करा आणि धरून ठेवा."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"शॉर्टकट हलवण्यासाठी किंवा कस्टम कृती वापरण्यासाठी दोनदा टॅप करा आणि धरून ठेवा."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"हा सिस्टम अ‍ॅप आहे आणि अनइंस्टॉल केला जाऊ शकत नाही."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"नाव संपादित करा"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> अक्षम केला आहे"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>साठी <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचना आहेत</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>साठी<xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g>सूचना आहे</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} साठी # सूचना आहे}other{{app_name} साठी # सूचना आहेत}}"</string>
     <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>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 47cb808..07e96cc 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Sentuh &amp; tahan widget untuk menggerakkan widget di sekitar Skrin utama"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Tambahkan pada Skrin utama"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ditambahkan pada skrin utama"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> pintasan</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> pintasan</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widget}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# pintasan}other{# pintasan}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Cari"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Tiada apl yang ditemui sepadan dengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Cari lagi apl"</string>
     <string name="label_application" msgid="8531721983832654978">"Apl"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Semua apl"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Pemberitahuan"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Sentuh &amp; tahan untuk menggerakkan pintasan."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Ketik dua kali &amp; tahan untuk menggerakkan pintasan atau menggunakan tindakan tersuai."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ini ialah apl sistem dan tidak boleh dinyahpasang."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Edit Nama"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> dilumpuhkan"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, mempunyai <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pemberitahuan</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, mempunyai <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> pemberitahuan</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} mempunyai # pemberitahuan}other{{app_name} mempunyai # pemberitahuan}}"</string>
     <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>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 6f54dd4..787b877 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"ပင်မစာမျက်နှာအနီးတွင် ဝိဂျက်ကိုရွှေ့ရန် ၎င်းကို တို့ထိ၍ဖိထားပါ"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"ပင်မစာမျက်နှာသို့ ထည့်ရန်"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ဝိဂျက်ကို ပင်မစာမျက်နှာတွင် ထည့်လိုက်ပြီ"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other">ဝိဂျက် <xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ခု</item>
-      <item quantity="one">ဝိဂျက် <xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> ခု</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other">ဖြတ်လမ်းလင့်ခ် <xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ခု</item>
-      <item quantity="one">ဖြတ်လမ်းလင့်ခ် <xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> ခု</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{ဝိဂျက် # ခု}other{ဝိဂျက် # ခု}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{ဖြတ်လမ်းလင့်ခ် # ခု}other{ဖြတ်လမ်းလင့်ခ် # ခု}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>၊ <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ဝိဂျက်များ"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"ရှာရန်"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" နှင့်ကိုက်ညီသည့် အပ်ပ်များကို မတွေ့ပါ"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"နောက်ထပ် အက်ပ်များကို ရှာပါ"</string>
     <string name="label_application" msgid="8531721983832654978">"အက်ပ်"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"အက်ပ်အားလုံး"</string>
     <string name="notifications_header" msgid="1404149926117359025">"အကြောင်းကြားချက်များ"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ဖြတ်လမ်းလင့်ခ်ကို ရွှေ့ရန် နှစ်ချက်တို့ပြီး ဖိထားပါ။"</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ဖြတ်လမ်းလင့်ခ်ကို ရွှေ့ရန် (သို့) စိတ်ကြိုက်လုပ်ဆောင်ချက်များကို သုံးရန် နှစ်ချက်တို့ပြီး ဖိထားပါ။"</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ဤအပ်ပလီကေးရှင်းမှာ စစ်စတန်ပိုင်းဆိုင်ရာ အပ်ပလီကေးရှင်းဖြစ်ပါသည်။ ထုတ်ပစ်၍ မရပါ"</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"အမည်ကို တည်းဖြတ်ပါ"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို ပိတ်ထားသည်"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> တွင် သတိပေးချက် <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ခု ရှိသည်</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> တွင် သတိပေးချက် <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ခု ရှိသည်</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} တွင် အကြောင်းကြားချက် # ခု ရှိသည်}other{{app_name} တွင် အကြောင်းကြားချက် # ခု ရှိသည်}}"</string>
     <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>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index f204bb7..c588d3c 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Trykk og hold på modulen for å bevege den rundt på startskjermen"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Legg til på startskjermen"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>-modulen er lagt til på startskjermen"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> moduler</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> modul</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> snarveier</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> snarvei</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# modul}other{# moduler}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# snarvei}other{# snarveier}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Moduler"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Søk"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Fant ingen apper som samsvarer med «<xliff:g id="QUERY">%1$s</xliff:g>»"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Søk etter flere apper"</string>
     <string name="label_application" msgid="8531721983832654978">"App"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Alle apper"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Varsler"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Trykk og hold for å flytte en snarvei."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Dobbelttrykk og hold for å flytte en snarvei eller bruke tilpassede handlinger."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Dette er en systemapp som ikke kan avinstalleres."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Rediger navn"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Slo av <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> har <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> varsler</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> har <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> varsel</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} har # varsel}other{{app_name} har # varsler}}"</string>
     <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>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 98a364f..091820b 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"यो विजेट होम स्क्रिनमा यताउता सार्न त्यसमा टच एन्ड होल्ड गर्नुहोस्"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"होम स्क्रिनमा हाल्नुहोस्"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"होम स्क्रिनमा <xliff:g id="WIDGET_NAME">%1$s</xliff:g> विजेट हालियो"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> वटा विजेट</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> वटा विजेट</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> वटा सर्टकट</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> वटा सर्टकट</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# विजेट}other{# वटा विजेट}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# सर्टकट}other{# वटा सर्टकट}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"विजेटहरू"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"खोज्नुहोस्"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" सँग मिल्दो कुनै एप भेटिएन"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"थप एपहरू खोज्नुहोस्"</string>
     <string name="label_application" msgid="8531721983832654978">"एप"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"सबै एप"</string>
     <string name="notifications_header" msgid="1404149926117359025">"सूचनाहरू"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"कुनै सर्टकट सार्न डबल ट्याप गरेर छोइराख्नुहोस्।"</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"कुनै सर्टकट सार्न वा आफ्नो रोजाइका कारबाही प्रयोग गर्न डबल ट्याप गरेर छोइराख्नुहोस्।"</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"यो प्रणाली एप हो र यसलाई स्थापना रद्द गर्न सकिँदैन।"</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"नाम सम्पादन गर्नुहोस्"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"असक्षम पारिएको <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, का <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचनाहरू छन्</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, को <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> सूचना छ</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} सँग सम्बन्धित # सूचना छ}other{{app_name} सँग सम्बन्धित # वटा सूचना छन्}}"</string>
     <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>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 4f35a2e..04a93c9 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Houd de widget ingedrukt om deze te verplaatsen op het startscherm"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Toevoegen aan startscherm"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> toegevoegd aan startscherm"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> snelkoppelingen</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> snelkoppeling</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# snelkoppeling}other{# snelkoppelingen}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Zoeken"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Er zijn geen apps gevonden die overeenkomen met \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Zoeken naar meer apps"</string>
     <string name="label_application" msgid="8531721983832654978">"App"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Alle apps"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Meldingen"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Tik en houd vast om een snelkoppeling te verplaatsen."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Dubbeltik en houd vast om een snelkoppeling te verplaatsen of aangepaste acties te gebruiken."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Dit is een systeemapp die niet kan worden verwijderd."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Naam bewerken"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> staat uit"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, heeft <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> meldingen</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, heeft <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> melding</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} heeft # melding}other{{app_name} heeft # meldingen}}"</string>
     <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>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 9ee1cf3..d28272b 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"ମୂଳସ୍କ୍ରିନର ଆଖପାଖରେ ୱିଜେଟକୁ ମୁଭ୍ କରିବା ପାଇଁ ଏହାକୁ ସ୍ପର୍ଶ କରି ଧରି ରଖନ୍ତୁ"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"ମୂଳସ୍କ୍ରିନରେ ଯୋଗ କରନ୍ତୁ"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>ର ୱିଜେଟ୍ ମୂଳସ୍କ୍ରିନରେ ଯୋଡ଼ାଗଲା"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g>ଟି ୱିଜେଟ୍</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g>ଟି ୱିଜେଟ୍</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g>ଟି ସର୍ଟକଟ୍</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g>ଟି ସର୍ଟକଟ୍</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{#ଟି ୱିଜେଟ୍}other{#ଟି ୱିଜେଟ୍}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{#ଟି ସର୍ଟକଟ୍}other{#ଟି ସର୍ଟକଟ୍}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ୱିଜେଟ୍‌"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"ସନ୍ଧାନ କରନ୍ତୁ"</string>
@@ -63,6 +57,8 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ସହିତ ମେଳ ହେଉଥିବା କୌଣସି ଆପ୍‌ ମିଳିଲା ନାହିଁ"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"ଅଧିକ ଆପ୍‌ ଖୋଜନ୍ତୁ"</string>
     <string name="label_application" msgid="8531721983832654978">"ଆପ୍"</string>
+    <!-- no translation found for all_apps_label (5015784846527570951) -->
+    <skip />
     <string name="notifications_header" msgid="1404149926117359025">"ବିଜ୍ଞପ୍ତି"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ଏକ ସର୍ଟକଟକୁ ମୁଭ୍ କରିବା ପାଇଁ ସ୍ପର୍ଶ କରି ଧରି ରଖନ୍ତୁ।"</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ଏକ ସର୍ଟକଟକୁ ମୁଭ୍ କରିବା ପାଇଁ ଦୁଇଥର-ଟାପ୍ କରି ଧରି ରଖନ୍ତୁ କିମ୍ବା କଷ୍ଟମ୍ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
@@ -91,10 +87,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ଏହା ଏକ ସିଷ୍ଟମ୍‌ ଆପ୍‌ ଅଟେ ଏବଂ ଏହା ଅନଇନଷ୍ଟଲ୍‌ କରାଯାଇ ପାରିବ ନାହିଁ।"</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"ନାମ ସମ୍ପାଦନ କରନ୍ତୁ"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଅକ୍ଷମ କରାଗଲା"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>ଟି ବିଜ୍ଞପ୍ତି ରହିଛି</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g>ଟି ବିଜ୍ଞପ୍ତି ରହିଛି</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name}ର #ଟି ବିଜ୍ଞପ୍ତି ରହିଛି}other{{app_name}ର #ଟି ବିଜ୍ଞପ୍ତି ରହିଛି}}"</string>
     <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>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 300fedc..7323c3f 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"ਵਿਜੇਟ ਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਇੱਧਰ-ਉੱਧਰ ਲਿਜਾਉਣ ਲਈ ਸਪਰਸ਼ ਕਰਕੇ ਦਬਾਈ ਰੱਖੋ"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਸ਼ਾਮਲ ਕਰੋ"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ਵਿਜੇਟ ਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ਵਿਜੇਟ</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ਵਿਜੇਟ</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ਸ਼ਾਰਟਕੱਟ</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ਸ਼ਾਰਟਕੱਟ</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ਵਿਜੇਟ}one{# ਵਿਜੇਟ}other{# ਵਿਜੇਟ}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# ਸ਼ਾਰਟਕੱਟ}one{# ਸ਼ਾਰਟਕੱਟ}other{# ਸ਼ਾਰਟਕੱਟ}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ਵਿਜੇਟ"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"ਖੋਜੋ"</string>
@@ -63,6 +57,8 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ਨਾਲ ਮੇਲ ਖਾਂਦੀਆਂ ਕੋਈ ਐਪਾਂ ਨਹੀਂ ਮਿਲੀਆਂ"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"ਹੋਰ ਐਪਾਂ ਖੋਜੋ"</string>
     <string name="label_application" msgid="8531721983832654978">"ਐਪ"</string>
+    <!-- no translation found for all_apps_label (5015784846527570951) -->
+    <skip />
     <string name="notifications_header" msgid="1404149926117359025">"ਸੂਚਨਾਵਾਂ"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ਕਿਸੇ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਲਿਜਾਉਣ ਲਈ ਸਪੱਰਸ਼ ਕਰਕੇ ਦਬਾਈ ਰੱਖੋ।"</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ਕਿਸੇ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਲਿਜਾਉਣ ਲਈ ਡਬਲ ਟੈਪ ਕਰਕੇ ਦਬਾਈ ਰੱਖੋ ਜਾਂ ਵਿਉਂਤੀਆਂ ਕਾਰਵਾਈਆਂ ਵਰਤੋ।"</string>
@@ -91,10 +87,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ਇਹ ਇੱਕ ਸਿਸਟਮ ਐਪ ਹੈ ਅਤੇ ਇਸਨੂੰ ਅਣਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"ਨਾਮ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ਦੀ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ਸੂਚਨਾ</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ਦੀਆਂ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ਸੂਚਨਾਵਾਂ</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} \'ਤੇ # ਸੂਚਨਾ ਹੈ}one{{app_name} \'ਤੇ # ਸੂਚਨਾ ਹੈ}other{{app_name} \'ਤੇ # ਸੂਚਨਾਵਾਂ ਹਨ}}"</string>
     <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>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 9f5f7d4..6a588b7 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -36,18 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Kliknij i przytrzymaj widżet, by poruszać nim po ekranie głównym"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Dodaj do ekranu głównego"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widżet <xliff:g id="WIDGET_NAME">%1$s</xliff:g> został dodany do ekranu głównego"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widżety</item>
-      <item quantity="many"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widżetów</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widżetu</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widżet</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="few"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> skróty</item>
-      <item quantity="many"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> skrótów</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> skrótu</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> skrót</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widżet}few{# widżety}many{# widżetów}other{# widżetu}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# skrót}few{# skróty}many{# skrótów}other{# skrótu}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widżety"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Szukaj"</string>
@@ -67,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nie znaleziono aplikacji pasujących do zapytania „<xliff:g id="QUERY">%1$s</xliff:g>”"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Wyszukaj więcej aplikacji"</string>
     <string name="label_application" msgid="8531721983832654978">"Aplikacja"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Wszystkie aplikacje"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Powiadomienia"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Naciśnij i przytrzymaj, aby przenieść skrót."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Naciśnij dwukrotnie i przytrzymaj, aby przenieść skrót lub użyć działań niestandardowych."</string>
@@ -95,12 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"To aplikacja systemowa i nie można jej odinstalować."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Edytuj nazwę"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest wyłączona"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g> – <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> powiadomienia</item>
-      <item quantity="many"><xliff:g id="APP_NAME_2">%1$s</xliff:g> – <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> powiadomień</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> – <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> powiadomienia</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> – <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> powiadomienie</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} – # powiadomienie}few{{app_name} – # powiadomienia}many{{app_name} – # powiadomień}other{{app_name} – # powiadomienia}}"</string>
     <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>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 6367249..09a1200 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Toque sem soltar no widget para o mover à volta do ecrã principal"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Adicionar ao ecrã principal"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> adicionado ao ecrã principal"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> atalhos</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> atalho</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# atalho}other{# atalhos}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Pesquisar"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nenhuma app correspondente a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Pesquisar mais aplicações"</string>
     <string name="label_application" msgid="8531721983832654978">"Aplicação"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Todas as apps"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notificações"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Toque sem soltar para mover um atalho."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Toque duas vezes sem soltar para mover um atalho ou utilizar ações personalizadas."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"É uma app de sistema e não pode ser desinstalada."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Edite o nome"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> desativado"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other">A app <xliff:g id="APP_NAME_2">%1$s</xliff:g> tem <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificações.</item>
-      <item quantity="one">A app <xliff:g id="APP_NAME_0">%1$s</xliff:g> tem <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificação</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{A app {app_name} tem # notificação}other{A app {app_name} tem # notificações}}"</string>
     <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>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 453a8ef..55d9c81 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Toque no widget e mantenha-o pressionado para movê-lo pela tela inicial"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Adicionar à tela inicial"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> adicionado à tela inicial"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> atalho</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> atalhos</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}other{# widgets}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# atalho}one{# atalho}other{# atalhos}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Pesquisa"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nenhum app encontrado que corresponda a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Pesquisar mais apps"</string>
     <string name="label_application" msgid="8531721983832654978">"App"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Todos os apps"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notificações"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Toque e mantenha a tela pressionada para mover um atalho."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Toque duas vezes e mantenha a tela pressionada para mover um atalho ou usar ações personalizadas."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Este é um app do sistema e não pode ser desinstalado."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Editar nome"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> desativado"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one">O app <xliff:g id="APP_NAME_2">%1$s</xliff:g>tem <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificação</item>
-      <item quantity="other">O app <xliff:g id="APP_NAME_2">%1$s</xliff:g>tem <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificações</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{O app {app_name} tem # notificação}one{O app {app_name} tem # notificação}other{O app {app_name} tem # notificações}}"</string>
     <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>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index a72965b..304113d 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -36,16 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Atingeți lung widgetul pentru a-l muta pe ecranul de pornire"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Adăugați pe ecranul de pornire"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widgetul <xliff:g id="WIDGET_NAME">%1$s</xliff:g> a fost adăugat pe ecranul de pornire"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgeturi</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> de widgeturi</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="few"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> comenzi rapide</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> de comenzi rapide</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> comandă rapidă</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}few{# widgeturi}other{# de widgeturi}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# comandă rapidă}few{# comenzi rapide}other{# de comenzi rapide}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g> <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgeturi"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Căutare"</string>
@@ -65,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nu s-a găsit nicio aplicație pentru „<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Căutați mai multe aplicații"</string>
     <string name="label_application" msgid="8531721983832654978">"Aplicație"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Toate aplicațiile"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notificări"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Atingeți și țineți apăsat pentru a muta comanda rapidă."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Atingeți de două ori și țineți apăsat pentru a muta o comandă rapidă sau folosiți acțiuni personalizate."</string>
@@ -93,11 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Aceasta este o aplicație de sistem și nu poate fi dezinstalată."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Modificați numele"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"S-a dezactivat <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g> are <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificări</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>​ are <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> de notificări</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> are <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificare</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} are # notificare}few{{app_name} are # notificări}other{{app_name} are # de notificări}}"</string>
     <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>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 89c233e..2d9495b 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -36,18 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Чтобы переместить виджет, нажмите на него и удерживайте."</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Добавить на главный экран"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Виджет \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\" добавлен на главный экран"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> виджет</item>
-      <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> виджета</item>
-      <item quantity="many"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> виджетов</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> виджета</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ярлык</item>
-      <item quantity="few"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ярлыка</item>
-      <item quantity="many"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ярлыков</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ярлыка</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# виджет}one{# виджет}few{# виджета}many{# виджетов}other{# виджета}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# быстрая команда}one{# быстрая команда}few{# быстрые команды}many{# быстрых команд}other{# быстрой команды}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Виджеты"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Поиск"</string>
@@ -67,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"По запросу \"<xliff:g id="QUERY">%1$s</xliff:g>\" ничего не найдено"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Искать другие приложения"</string>
     <string name="label_application" msgid="8531721983832654978">"Приложение"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Все приложения"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Уведомления"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Нажмите и удерживайте для переноса ярлыка."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Чтобы использовать специальные действия или перенести ярлык, нажмите на него дважды и удерживайте."</string>
@@ -95,12 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Это системное приложение, его нельзя удалить."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Измените название"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Приложение <xliff:g id="APP_NAME">%1$s</xliff:g> отключено"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one">В приложении \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> уведомление</item>
-      <item quantity="few">В приложении \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> уведомления</item>
-      <item quantity="many">В приложении \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> уведомлений</item>
-      <item quantity="other">В приложении \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> уведомления</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{В приложении \"{app_name}\" # уведомление}one{В приложении \"{app_name}\" # уведомление}few{В приложении \"{app_name}\" # уведомления}many{В приложении \"{app_name}\" # уведомлений}other{В приложении \"{app_name}\" # уведомления}}"</string>
     <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>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 5e58e48..3214233 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"එය මුල් පිටු තිරය වටා ගෙන යාමට විජට් එක ස්පර්ශ කර අල්ලා ගන්න"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"මුල් පිටු තිරය වෙත එක් කරන්න"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> විජට්ටුව මුල් පිටු තිරය වෙත එක් කරන ලදි"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one">විජට් <xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g></item>
-      <item quantity="other">විජට් <xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g></item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one">කෙටි මං <xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g></item>
-      <item quantity="other">කෙටි මං <xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g></item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{විජට් #}one{විජට් #}other{විජට් #}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{කෙටි මං #}one{කෙටි මං #}other{කෙටි මං #}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"විජට්"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"සෙවීම"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" සමග ගැළපෙන යෙදුම් හමු නොවිණි"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"තව යෙදුම් සඳහා සොයන්න"</string>
     <string name="label_application" msgid="8531721983832654978">"යෙදුම"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"සියලු යෙදුම්"</string>
     <string name="notifications_header" msgid="1404149926117359025">"දැනුම්දීම්"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"කෙටි මගක් ගෙන යාමට ස්පර්ශ කර අල්ලාගෙන සිටින්න."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"කෙටි මගක් ගෙන යාමට හෝ අභිරුචි ක්‍රියා භාවිත කිරීමට දෙවරක් තට්ටු කර අල්ලා ගෙන සිටින්න."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"මෙය පද්ධති යෙදුමක් වන අතර අස්ථාපනය කළ නොහැක."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"නම සංස්කරණය කරන්න"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> අබල කෙරිණි"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, දැනුම්දීම් <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>ක් ඇත</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, දැනුම්දීම් <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>ක් ඇත</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} හට දැනුම්දීම් #ක් ඇත}one{{app_name} හට දැනුම්දීම් #ක් ඇත}other{{app_name} හට දැනුම්දීම් #ක් ඇත}}"</string>
     <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>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index b2e270c..1ba5cf1 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -36,18 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Po pridržaní môžete miniaplikáciu posúvať po ploche"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Pridať na plochu"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Na plochu bola pridaná miniaplikácia <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> miniaplikácie</item>
-      <item quantity="many"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> miniaplikácií</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> miniaplikácia</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="few"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> skratky</item>
-      <item quantity="many"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> shortcuts</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> skratiek</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> skratka</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# miniaplikácia}few{# miniaplikácie}many{# widgets}other{# miniaplikácií}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# odkaz}few{# odkazy}many{# shortcuts}other{# odkazov}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Miniaplikácie"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Vyhľadajte"</string>
@@ -67,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nenašli sa žiadne aplikácie zodpovedajúce dopytu <xliff:g id="QUERY">%1$s</xliff:g>"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Hľadať ďalšie aplikácie"</string>
     <string name="label_application" msgid="8531721983832654978">"Aplikácia"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Všetky aplikácie"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Upozornenia"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Pridržaním presuňte skratku."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Dvojitým klepnutím a pridržaním presuňte odkaz alebo použite vlastné akcie."</string>
@@ -95,12 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Toto je systémová aplikácia a nedá sa odinštalovať."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Úprava názvu"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> je deaktivovaná"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, má <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> upozornenia</item>
-      <item quantity="many"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, má <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> upozornení</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, má <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> upozornenie</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{Aplikácia {app_name} má # upozornenie}few{Aplikácia {app_name} má # upozornenia}many{{app_name} has # notifications}other{Aplikácia {app_name} má # upozornení}}"</string>
     <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>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 4c3126f..a0525cf 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -36,18 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Pridržite pripomoček, če ga želite premikati po začetnem zaslonu."</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Dodaj na začetni zaslon"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Pripomoček »<xliff:g id="WIDGET_NAME">%1$s</xliff:g>« je dodan na začetni zaslon."</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> pripomoček</item>
-      <item quantity="two"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> pripomočka</item>
-      <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> pripomočki</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> pripomočkov</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> bližnjica</item>
-      <item quantity="two"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> bližnjici</item>
-      <item quantity="few"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> bližnjice</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> bližnjic</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# pripomoček}one{# pripomoček}two{# pripomočka}few{# pripomočki}other{# pripomočkov}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# bližnjica}one{# bližnjica}two{# bližnjici}few{# bližnjice}other{# bližnjic}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Pripomočki"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Iskanje"</string>
@@ -67,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Ni aplikacij, ki bi ustrezale poizvedbi »<xliff:g id="QUERY">%1$s</xliff:g>«"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Iskanje več aplikacij"</string>
     <string name="label_application" msgid="8531721983832654978">"Aplikacija"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Vse aplikacije"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Obvestila"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Pridržite bližnjico, da jo premaknete."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Dvakrat se dotaknite bližnjice in jo pridržite, da jo premaknete, ali pa uporabite dejanja po meri."</string>
@@ -95,12 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"To je sistemska aplikacija in je ni mogoče odstraniti."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Urejanje imena"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogočena"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obvestilo</item>
-      <item quantity="two"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obvestili</item>
-      <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obvestila</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obvestil</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} ima # obvestilo}one{{app_name} ima # obvestilo}two{{app_name} ima # obvestili}few{{app_name} ima # obvestila}other{{app_name} ima # obvestil}}"</string>
     <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>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 6bc2d2f..218a745 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Prek dhe mbaj të shtypur miniaplikacionin për ta lëvizur nëpër \"Ekranin bazë\""</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Shto në \"Ekranin bazë\""</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Miniaplikacioni <xliff:g id="WIDGET_NAME">%1$s</xliff:g> u shtua në ekranin bazë"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> miniaplikacione</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> miniaplikacion</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> shkurtore</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> shkurtore</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# miniaplikacion}other{# miniaplikacione}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# shkurtore}other{# shkurtore}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Miniaplikacionet"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Kërko"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nuk u gjet asnjë aplikacion që përputhet me \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Kërko për më shumë aplikacione"</string>
     <string name="label_application" msgid="8531721983832654978">"Aplikacioni"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Të gjitha aplikacionet"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Njoftimet"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Prek dhe mbaj shtypur një shkurtore për ta zhvendosur."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Trokit dy herë dhe mbaje shtypur një shkurtore për ta zhvendosur atë ose për të përdorur veprimet e personalizuara."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ky është aplikacion sistemi dhe nuk mund të çinstalohet."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Redakto emrin"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> u çaktivizua"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ka <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> njoftime</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, ka <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> njoftim</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} ka # njoftim}other{{app_name} ka # njoftime}}"</string>
     <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>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 8e07ba7..8668b96 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -36,16 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Додирните и задржите виџет да бисте га померали по почетном екрану"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Додај на почетни екран"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Додали сте виџет <xliff:g id="WIDGET_NAME">%1$s</xliff:g> на почетни екран"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> виџет</item>
-      <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> виџета</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> виџета</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> пречица</item>
-      <item quantity="few"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> пречице</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> пречица</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# виџет}one{# виџет}few{# виџета}other{# виџета}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# пречица}one{# пречица}few{# пречице}other{# пречица}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Виџети"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Претражите"</string>
@@ -65,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Није пронађена ниједна апликација за „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Претражи још апликација"</string>
     <string name="label_application" msgid="8531721983832654978">"Апликација"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Све апликације"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Обавештења"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Додирните и задржите ради померања пречице."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Двапут додирните и задржите да бисте померали пречицу или користите прилагођене радње."</string>
@@ -93,11 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ово је системска апликација и не може да се деинсталира."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Измените назив"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> је онемогућена"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> обавештење</item>
-      <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> обавештења</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> обавештења</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name}, има # обавештење}one{{app_name}, има # обавештење}few{{app_name}, има # обавештења}other{{app_name}, има # обавештења}}"</string>
     <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>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 726d2ec..1a47df2 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Tryck länge på widgeten om du vill flytta den på startskärmen"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Lägg till på startskärmen"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget för <xliff:g id="WIDGET_NAME">%1$s</xliff:g> har lagts till på startskärmen"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgetar</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> genvägar</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> genväg</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgetar}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# genväg}other{# genvägar}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgetar"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Sök"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Inga appar som matchar <xliff:g id="QUERY">%1$s</xliff:g> hittades"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Sök efter fler appar"</string>
     <string name="label_application" msgid="8531721983832654978">"App"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Alla appar"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Aviseringar"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Tryck länge för att flytta en genväg."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Tryck snabbt två gånger och håll kvar för att flytta en genväg eller använda anpassade åtgärder."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Det här är en systemapp som inte kan avinstalleras."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Redigera namn"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> har inaktiverats"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> har <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> aviseringar</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> har <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> avisering</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} har # avisering}other{{app_name} har # aviseringar}}"</string>
     <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>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 2a9a187..af7ac67 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Gusa na ushikilie wijeti ili uisogeze kwenye Skrini ya kwanza"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Weka kwenye Skrini ya kwanza"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Umeongeza wijeti ya <xliff:g id="WIDGET_NAME">%1$s</xliff:g> kwenye skrini ya kwanza"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other">Wijeti <xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g></item>
-      <item quantity="one">Wijeti <xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g></item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other">Njia <xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> za mkato</item>
-      <item quantity="one">Njia <xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> ya mkato</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{Wijeti #}other{Wijeti #}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{Njia # ya mkato}other{Njia # za mkato}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Wijeti"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Tafuta"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Haikupata programu zozote zinazolingana na \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Tafuta programu zaidi"</string>
     <string name="label_application" msgid="8531721983832654978">"Programu"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Programu zote"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Arifa"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Gusa na ushikilie ili usogeze njia ya mkato."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Gusa mara mbili na ushikilie ili usogeze njia ya mkato au utumie vitendo maalum."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Hii ni programu ya mfumo na haiwezi kuondolewa."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Badilisha Jina"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> imezimwa"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ina arifa <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g></item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, ina arifa <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g></item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} ina arifa #}other{{app_name} ina arifa #}}"</string>
     <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>
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index 47a88f2..3727932 100644
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -17,4 +17,7 @@
 <resources>
 <!-- DragController -->
     <dimen name="drag_flingToDeleteMinVelocity">-1000dp</dimen>
+
+<!-- Widgets pickers -->
+    <dimen name="widget_list_horizontal_margin">32dp</dimen>
 </resources>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index d8ff289..4f1e21e 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"முகப்புத் திரைக்கு விட்ஜெட்டை நகர்த்த அதைத் தொட்டுப் பிடிக்கவும்"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"முகப்புத் திரையில் சேர்"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> விட்ஜெட் முகப்புத் திரையில் சேர்க்கப்பட்டது"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> விட்ஜெட்டுகள்</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> விட்ஜெட்</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ஷார்ட்கட்கள்</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> ஷார்ட்கட்</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# விட்ஜெட்}other{# விட்ஜெட்டுகள்}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# ஷார்ட்கட்}other{# ஷார்ட்கட்கள்}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"விட்ஜெட்கள்"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"தேடுக"</string>
@@ -58,11 +52,12 @@
     <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"விட்ஜெட் அமைப்புகளை மாற்றத் தட்டவும்"</string>
     <string name="widget_education_close_button" msgid="8676165703104836580">"சரி"</string>
     <string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"விட்ஜெட் அமைப்புகளை மாற்றும்"</string>
-    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"பயன்பாடுகளில் தேடுக"</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ஆப்ஸில் தேடுக"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"ஆப்ஸை ஏற்றுகிறது…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" உடன் பொருந்தும் ஆப்ஸ் இல்லை"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"கூடுதல் பயன்பாடுகளைத் தேடு"</string>
     <string name="label_application" msgid="8531721983832654978">"ஆப்ஸ்"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"அனைத்து ஆப்ஸும்"</string>
     <string name="notifications_header" msgid="1404149926117359025">"அறிவிப்புகள்"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ஷார்ட்கட்டை நகர்த்தத் தொட்டுப் பிடிக்கவும்."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ஷார்ட்கட்டை நகர்த்த இருமுறை தட்டிப் பிடிக்கவும் அல்லது பிரத்தியேகச் செயல்களைப் பயன்படுத்தவும்."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"இது அமைப்பு ஆப்ஸ் என்பதால் நிறுவல் நீக்கம் செய்ய முடியாது."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"பெயரைத் திருத்துதல்"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> முடக்கப்பட்டது"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ஆப்ஸில் <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> அறிவிப்புகள் வந்துள்ளன</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> ஆப்ஸில் <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> அறிவிப்பு வந்துள்ளது</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} ஆப்ஸில் # அறிவிப்பு வந்துள்ளது}other{{app_name} ஆப்ஸில் # அறிவிப்புகள் வந்துள்ளன}}"</string>
     <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>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index a1718a7..faeea40 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"ఈ విడ్జెట్‌ను మొదటి స్క్రీన్‌లో కావాల్సిన చోట ఉంచడానికి, దాన్ని తాకి అలాగే నొక్కి పట్టుకోండి"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"మొదటి స్క్రీన్‌కు జోడించు"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"మొదటి స్క్రీన్‌కు <xliff:g id="WIDGET_NAME">%1$s</xliff:g> విడ్జెట్ జోడించబడింది"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> విడ్జెట్‌లు</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> విడ్జెట్</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> షార్ట్‌కట్‌లు</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> షార్ట్‌కట్</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# విడ్జెట్}other{# విడ్జెట్‌లు}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# షార్ట్‌కట్}other{# షార్ట్‌కట్‌లు}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"విడ్జెట్‌లు"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"సెర్చ్ చేయండి"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"కి సరిపోలే అప్లికేషన్‌లేవీ కనుగొనబడలేదు"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"మరిన్ని యాప్‌ల కోసం సెర్చ్ చేయండి"</string>
     <string name="label_application" msgid="8531721983832654978">"యాప్"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"అన్ని యాప్‌లు"</string>
     <string name="notifications_header" msgid="1404149926117359025">"నోటిఫికేషన్‌లు"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"షార్ట్‌కట్‌ను తరలించడానికి తాకి &amp; నొక్కి ఉంచు."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"షార్ట్‌కట్‌ను తరలించడానికి లేదా అనుకూల చర్యలను ఉపయోగించడానికి రెండుసార్లు నొక్కండి &amp; హోల్డ్ చేయండి."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ఇది సిస్టమ్ యాప్ మరియు దీన్ని అన్‌ఇన్‌స్టాల్ చేయడం సాధ్యపడదు."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"పేరును ఎడిట్ చేయండి"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> నిలిపివేయబడింది"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, నుంచి <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> నోటిఫికేషన్‌లు ఉన్నాయి</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, నుంచి <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> నోటిఫికేషన్ ఉంది</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name}లో # నోటిఫికేషన్ ఉంది}other{{app_name}లో # నోటిఫికేషన్‌లు ఉన్నాయి}}"</string>
     <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>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index a3f6871..9278186 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"แตะวิดเจ็ตค้างไว้เพื่อย้ายไปรอบๆ หน้าจอหลัก"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"เพิ่มลงในหน้าจอหลัก"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"เพิ่มวิดเจ็ต <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ลงในหน้าจอหลักแล้ว"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other">วิดเจ็ต <xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> รายการ</item>
-      <item quantity="one">วิดเจ็ต <xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> รายการ</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other">ทางลัด <xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> รายการ</item>
-      <item quantity="one">ทางลัด <xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> รายการ</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{วิดเจ็ต # รายการ}other{วิดเจ็ต # รายการ}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{ทางลัด # รายการ}other{ทางลัด # รายการ}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"วิดเจ็ต"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"ค้นหา"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"ไม่พบแอปที่ตรงกับ \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"ค้นหาแอปเพิ่มเติม"</string>
     <string name="label_application" msgid="8531721983832654978">"แอป"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"แอปทั้งหมด"</string>
     <string name="notifications_header" msgid="1404149926117359025">"การแจ้งเตือน"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"แตะค้างไว้เพื่อย้ายทางลัด"</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"แตะสองครั้งค้างไว้เพื่อย้ายทางลัดหรือใช้การดำเนินการที่กำหนดเอง"</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"นี่เป็นแอประบบและไม่สามารถถอนการติดตั้งได้"</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"แก้ไขชื่อ"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"ปิดใช้ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> มีการแจ้งเตือน <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> รายการ</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> มีการแจ้งเตือน <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> รายการ</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} มีการแจ้งเตือน # รายการ}other{{app_name} มีการแจ้งเตือน # รายการ}}"</string>
     <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>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index c2065e3..1e93180 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Pindutin nang matagal ang widget para ilipat-lipat ito sa Home screen"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Idagdag sa Home screen"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Idinagdag sa home screen ang widget na <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> na widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> shortcut</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> na shortcut</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}other{# na widget}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# shortcut}one{# shortcut}other{# na shortcut}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Mga Widget"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Maghanap"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Walang nahanap na app na tumutugma sa \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Maghanap ng higit pang mga app"</string>
     <string name="label_application" msgid="8531721983832654978">"App"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Lahat ng app"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Mga Notification"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Pindutin nang matagal para ilipat ang shortcut."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"I-double tap at pindutin nang matagal para ilipat ang shortcut o gumamit ng mga custom na pagkilos."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Isa itong app ng system at hindi maaaring i-uninstall."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"I-edit ang Pangalan"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Naka-disable ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one">May <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> (na) notification ang <xliff:g id="APP_NAME_2">%1$s</xliff:g></item>
-      <item quantity="other">May <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> (na) notification ang <xliff:g id="APP_NAME_2">%1$s</xliff:g></item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{May # notification ang {app_name}}one{May # notification ang {app_name}}other{May # na notification ang {app_name}}}"</string>
     <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>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 534130d..bf0d9bc 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Ana ekranda taşımak için widget\'a dokunup basılı tutun"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Ana ekrana ekle"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget\'ı ana ekrana eklendi"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> kısayol</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> kısayol</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widget}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# kısayol}other{# kısayol}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widget\'lar"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Ara"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ile eşleşen uygulama bulunamadı"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Başka uygulamalar ara"</string>
     <string name="label_application" msgid="8531721983832654978">"Uygulama"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Tüm uygulamalar"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Bildirimler"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Kısayolu taşımak için dokunup basılı tutun."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Kısayolu taşımak veya özel işlemleri kullanmak için iki kez dokunup basılı tutun."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu bir sistem uygulamasıdır ve yüklemesi kaldırılamaz."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Adı Düzenle"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> devre dışı"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> uygulamasının <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> bildirimi var</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> uygulamasının <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> bildirimi var</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} uygulamasının # bildirimi var}other{{app_name} uygulamasının # bildirimi var}}"</string>
     <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>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 7431479..f0f2951 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -36,18 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Натисніть і втримуйте віджет, щоб перемістити його в потрібне місце на головному екрані"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Додати на головний екран"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Віджет <xliff:g id="WIDGET_NAME">%1$s</xliff:g> додано на головний екран"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> віджет</item>
-      <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> віджети</item>
-      <item quantity="many"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> віджетів</item>
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> віджета</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ярлик</item>
-      <item quantity="few"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ярлики</item>
-      <item quantity="many"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ярликів</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ярлика</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# віджет}one{# віджет}few{# віджети}many{# віджетів}other{# віджета}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# ярлик}one{# ярлик}few{# ярлики}many{# ярликів}other{# ярлика}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Віджети"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Пошук"</string>
@@ -67,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Немає додатків для запиту \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Шукати ще додатки"</string>
     <string name="label_application" msgid="8531721983832654978">"Додаток"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Усі додатки"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Сповіщення"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Натисніть і втримуйте, щоб перемістити ярлик."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Двічі натисніть і втримуйте ярлик, щоб перемістити його або виконати інші дії."</string>
@@ -95,12 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Це системна програма, її неможливо видалити."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Редагувати назву"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> вимкнено"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one">Додаток <xliff:g id="APP_NAME_2">%1$s</xliff:g> має <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> сповіщення</item>
-      <item quantity="few">Додаток <xliff:g id="APP_NAME_2">%1$s</xliff:g> має <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> сповіщення</item>
-      <item quantity="many">Додаток <xliff:g id="APP_NAME_2">%1$s</xliff:g> має <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> сповіщень</item>
-      <item quantity="other">Додаток <xliff:g id="APP_NAME_2">%1$s</xliff:g> має <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> сповіщення</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{Додаток {app_name} має # сповіщення}one{Додаток {app_name} має # сповіщення}few{Додаток {app_name} має # сповіщення}many{Додаток {app_name} має # сповіщень}other{Додаток {app_name} має # сповіщення}}"</string>
     <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>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 4e5b8ad..21a4bc0 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"ویجیٹ کو ہوم اسکرین کے چاروں طرف منتقل کرنے کیلئے اسے ٹچ کریں اور دبائے رکھیں"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"ہوم اسکرین میں شامل کریں"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ویجیٹ کو ہوم اسکرین میں شامل کیا گیا"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ویجیٹس</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> ویجیٹ</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> شارٹ کٹس</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> شارٹ کٹ</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ویجیٹ}other{# ویجیٹس}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# شارٹ کٹ}other{# شارٹ کٹس}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>، <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ویجیٹس"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"تلاش کریں"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" سے مماثل کوئی ایپس نہیں ملیں"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"مزید ایپس تلاش کریں"</string>
     <string name="label_application" msgid="8531721983832654978">"ایپ"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"سبھی ایپس"</string>
     <string name="notifications_header" msgid="1404149926117359025">"اطلاعات"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"شارٹ کٹ منتقل کرنے کیلیے ٹچ کریں اور پکڑ کر رکھیں۔"</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"شارٹ کٹ کو منتقل کرنے یا حسب ضرورت کارروائیاں استعمال کرنے کے لیے دوبار تھپتھپائیں اور پکڑ کر رکھیں۔"</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"یہ ایک سسٹم ایپ ہے اور اسے اَن انسٹال نہیں کیا جا سکتا ہے۔"</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"نام میں ترمیم کریں"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> غیر فعال ہے"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> میں<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> اطلاعات ہیں</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> میں<xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> اطلاع ہے</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} میں # اطلاع ہے}other{{app_name} میں # اطلاعات ہیں}}"</string>
     <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>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 3f11515..8010fc8 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Vidjetni ushlagan holda kerakli joyga siljiting"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Bosh ekranga chiqarish"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> vidjeti bosh ekranga qoʻshildi"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ta vidjet</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> ta vidjet</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> ta yorliq</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> ta yorliq</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ta vidjet}other{# ta vidjet}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# ta yorliq}other{# ta yorliq}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Vidjetlar"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Qidiruv"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"“<xliff:g id="QUERY">%1$s</xliff:g>” bilan mos hech qanday ilova topilmadi"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Boshqa ilovalarni qidirish"</string>
     <string name="label_application" msgid="8531721983832654978">"Ilova"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Barcha ilovalar"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Bildirishnomalar"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Yorliqni bosib turgan holatda suring."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Ikki marta bosing va yorliqni bosib turgan holatda suring yoki maxsus amaldan foydalaning."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu tizim ilovasi, shuning uchun o‘chirib bo‘lmaydi."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Nomini tahrirlash"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi o‘chirib qo‘yildi"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ilovasida <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ta bildirishnoma bor</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> ilovasida <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ta bildirishnoma bor</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} ilovasida # ta bildirishnoma bor}other{{app_name} ilovasida # ta bildirishnoma bor}}"</string>
     <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>
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
index c2ebeff..7bbdbd1 100644
--- a/res/values-v31/colors.xml
+++ b/res/values-v31/colors.xml
@@ -40,7 +40,9 @@
 
     <color name="wallpaper_popup_scrim">@android:color/system_neutral1_900</color>
 
-    <color name="folder_dot_color">@android:color/system_accent2_50</color>
+    <color name="folder_dot_color">@android:color/system_accent3_100</color>
+    <color name="folder_pagination_color_light">@android:color/system_accent1_600</color>
+    <color name="folder_pagination_color_dark">@android:color/system_accent2_100</color>
 
     <color name="home_settings_header_accent">@android:color/system_accent1_600</color>
     <color name="home_settings_header_collapsed">@android:color/system_neutral1_100</color>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 6ab04af..65c204f 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Chạm và giữ để di chuyển tiện ích xung quanh Màn hình chính"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Thêm vào Màn hình chính"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Đã thêm tiện ích <xliff:g id="WIDGET_NAME">%1$s</xliff:g> vào màn hình chính"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> tiện ích</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> tiện ích</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> lối tắt</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> lối tắt</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# tiện ích}other{# tiện ích}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# lối tắt}other{# lối tắt}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Tiện ích"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Tìm kiếm"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Không tìm thấy ứng dụng nào phù hợp với \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Tìm kiếm thêm ứng dụng"</string>
     <string name="label_application" msgid="8531721983832654978">"Ứng dụng"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Tất cả ứng dụng"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Thông báo"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Chạm và giữ để di chuyển một lối tắt."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Nhấn đúp và giữ để di chuyển một lối tắt hoặc sử dụng các thao tác tùy chỉnh."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Đây là ứng dụng hệ thống và không thể gỡ cài đặt."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Chỉnh sửa tên"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Đã vô hiệu hóa <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, có <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> thông báo</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, có <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> thông báo</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} có # thông báo}other{{app_name} có # thông báo}}"</string>
     <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>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index b02890c..1cd0d31 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"轻触并按住该微件即可将其在主屏幕上四处移动"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"添加到主屏幕"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"已将“<xliff:g id="WIDGET_NAME">%1$s</xliff:g>”微件添加到主屏幕"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> 个微件</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> 个微件</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> 个快捷方式</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> 个快捷方式</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# 个微件}other{# 个微件}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# 个快捷方式}other{# 个快捷方式}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>,<xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"微件"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"搜索"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"未找到与“<xliff:g id="QUERY">%1$s</xliff:g>”相符的应用"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"搜索更多应用"</string>
     <string name="label_application" msgid="8531721983832654978">"应用"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"所有应用"</string>
     <string name="notifications_header" msgid="1404149926117359025">"通知"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"轻触并按住快捷方式即可移动该快捷方式。"</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"点按两次并按住快捷方式即可移动该快捷方式或使用自定义操作。"</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"这是系统应用,无法卸载。"</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"修改名称"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"已停用<xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> 个通知</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> 个通知</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{“{app_name}”有 # 条通知}other{“{app_name}”有 # 条通知}}"</string>
     <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>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index d601345..e5bf742 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"按住小工具即可隨意在主畫面上移動"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"新增至主畫面"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"已經將「<xliff:g id="WIDGET_NAME">%1$s</xliff:g>」小工具加咗去主畫面"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> 個小工具</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> 個小工具</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> 個捷徑</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> 個捷徑</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# 個小工具}other{# 個小工具}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# 個捷徑}other{# 個捷徑}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>、<xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"小工具"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"搜尋"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"找不到與「<xliff:g id="QUERY">%1$s</xliff:g>」相符的應用程式"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"搜尋更多應用程式"</string>
     <string name="label_application" msgid="8531721983832654978">"應用程式"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"所有應用程式"</string>
     <string name="notifications_header" msgid="1404149926117359025">"通知"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"輕觸並按住即可移動捷徑。"</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"㩒兩下之後㩒住,就可以郁捷徑或者用自訂操作。"</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"這是系統應用程式,無法將其解除安裝。"</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"編輯名稱"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」已停用"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> 項通知</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> 項通知</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{「{app_name}」有 # 項通知}other{「{app_name}」有 # 項通知}}"</string>
     <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>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 74850f4..472ea46 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"按住小工具即可將它拖放到主畫面上的任何位置"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"新增到主畫面"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"已將「<xliff:g id="WIDGET_NAME">%1$s</xliff:g>」小工具新增到主畫面"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> 項小工具</item>
-      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> 項小工具</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> 個捷徑</item>
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> 個捷徑</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# 項小工具}other{# 項小工具}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# 個捷徑}other{# 個捷徑}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>、<xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"小工具"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"搜尋"</string>
@@ -63,6 +57,8 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"找不到與「<xliff:g id="QUERY">%1$s</xliff:g>」相符的應用程式"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"搜尋更多應用程式"</string>
     <string name="label_application" msgid="8531721983832654978">"應用程式"</string>
+    <!-- no translation found for all_apps_label (5015784846527570951) -->
+    <skip />
     <string name="notifications_header" msgid="1404149926117359025">"通知"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"按住即可移動捷徑。"</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"輕觸兩下並按住即可移動捷徑或使用自訂操作。"</string>
@@ -91,10 +87,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"這是系統應用程式,不可解除安裝。"</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"編輯名稱"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"已停用 <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> 則通知</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> 則通知</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{「{app_name}」有 # 則通知}other{「{app_name}」有 # 則通知}}"</string>
     <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>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index e3414a6..6da6772 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -36,14 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Thinta uphinde ubambe iwijethi ukuyihambisa Kusikrini sasekhaya"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Engeza kusikrini sasekhaya"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Iwijethi ye-<xliff:g id="WIDGET_NAME">%1$s</xliff:g> yengezwe kusikrini sasekhaya"</string>
-    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
-      <item quantity="one">Amawijethi angu-<xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g></item>
-      <item quantity="other">Amawijethi angu-<xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g></item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one">Izinqamuleli ezingu-<xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g></item>
-      <item quantity="other">Izinqamuleli ezingu-<xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g></item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{iwijethi #}one{amawijethi #}other{amawijethi #}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{isinqamuleli #}one{izinqamuleli #}other{izinqamuleli #}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Amawijethi"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Sesha"</string>
@@ -63,6 +57,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Azikho izinhlelo zokusebenza ezitholiwe ezifana ne-\"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Sesha izinhlelo zokusebenza eziningi"</string>
     <string name="label_application" msgid="8531721983832654978">"Uhlelo lokusebenza"</string>
+    <string name="all_apps_label" msgid="5015784846527570951">"Zonke izinhlelo zokusebenza"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Izaziso"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Thinta uphinde ubambe ukuze uhambise isinqamuleli."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Thepha kabili uphinde ubambe ukuze uhambise isinqamuleli noma usebenzise izenzo ezingokwezifiso."</string>
@@ -91,10 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Lolu uhlelo lokusebenza lwesistimu futhi alikwazi ukukhishwa."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Hlela igama"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Kukhutshaziwe <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, unezaziso ezingu-<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g></item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, unezaziso ezingu-<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g></item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{I-{app_name}, inesaziso esingu-#}one{I-{app_name}, inezaziso ezingu-#}other{I-{app_name}, inezaziso ezingu-#}}"</string>
     <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>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 00cf31c..08f0089 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -42,7 +42,9 @@
     <attr name="popupNotificationDotColor" format="color" />
 
     <attr name="folderDotColor" format="color" />
-    <attr name="folderFillColor" format="color" />
+    <attr name="folderPaginationColor" format="color" />
+    <attr name="folderPreviewColor" format="color" />
+    <attr name="folderBackgroundColor" format="color" />
     <attr name="folderIconRadius" format="float" />
     <attr name="folderIconBorderColor" format="color" />
     <attr name="folderTextColor" format="color" />
@@ -71,7 +73,7 @@
 
     <!-- BubbleTextView specific attributes. -->
     <declare-styleable name="FolderIconPreview">
-        <attr name="folderFillColor" />
+        <attr name="folderPreviewColor" />
         <attr name="folderIconBorderColor" />
         <attr name="folderDotColor" />
     </declare-styleable>
@@ -148,9 +150,11 @@
 
         <attr name="dbFile" format="string" />
         <attr name="defaultLayoutId" format="reference" />
+        <attr name="defaultSplitDisplayLayoutId" format="reference" />
         <attr name="demoModeLayoutId" format="reference" />
         <attr name="isScalable" format="boolean" />
         <attr name="devicePaddingId" format="reference" />
+        <attr name="gridEnabled" format="boolean" />
 
     </declare-styleable>
 
@@ -169,31 +173,77 @@
         <attr name="minWidthDps" format="float" />
         <attr name="minHeightDps" format="float" />
 
-        <!-- These min cell values are only used if GridDisplayOption#isScalable is true-->
+        <!-- These min cell values are only used if GridDisplayOption#isScalable is true -->
         <attr name="minCellHeightDps" format="float" />
         <attr name="minCellWidthDps" format="float" />
+        <!-- twoPanelPortraitMinCellWidthDps defaults to minCellHeightDps, if not specified -->
+        <attr name="twoPanelPortraitMinCellHeightDps" format="float" />
+        <!-- twoPanelPortraitMinCellHeightDps defaults to minCellWidthDps, if not specified -->
+        <attr name="twoPanelPortraitMinCellWidthDps" format="float" />
+        <!-- twoPanelLandscapeMinCellHeightDps defaults to minCellHeightDps, if not specified -->
+        <attr name="twoPanelLandscapeMinCellHeightDps" format="float" />
+        <!-- twoPanelLandscapeMinCellWidthDps defaults to minCellWidthDps, if not specified -->
+        <attr name="twoPanelLandscapeMinCellWidthDps" format="float" />
 
-        <attr name="borderSpacingDps" format="float" />
+        <!-- These border spaces are only used if GridDisplayOption#isScalable is true -->
+        <!-- space to be used horizontally and vertically -->
+        <attr name="borderSpaceDps" format="float" />
+        <!-- space to the right of the cell, defaults to borderSpaceDps if not specified -->
+        <attr name="borderSpaceHorizontalDps" format="float" />
+        <!-- space below the cell, defaults to borderSpaceDps if not specified -->
+        <attr name="borderSpaceVerticalDps" format="float" />
+        <!-- space to be used horizontally and vertically in two panels,
+        defaults to borderSpaceDps if not specified -->
+        <attr name="twoPanelPortraitBorderSpaceDps" format="float" />
+        <!-- space to the right of the cell in two panels, defaults to
+        twoPanelPortraitBorderSpaceDps if not specified -->
+        <attr name="twoPanelPortraitBorderSpaceHorizontalDps" format="float" />
+        <!-- space below the cell in two panels, defaults to twoPanelPortraitBorderSpaceDps
+        if not specified -->
+        <attr name="twoPanelPortraitBorderSpaceVerticalDps" format="float" />
+        <!-- space to be used horizontally and vertically in two panels,
+        defaults to borderSpaceDps if not specified -->
+        <attr name="twoPanelLandscapeBorderSpaceDps" format="float" />
+        <!-- space to the right of the cell in two panels, defaults to
+        twoPanelLandscapeBorderSpaceDps if not specified -->
+        <attr name="twoPanelLandscapeBorderSpaceHorizontalDps" format="float" />
+        <!-- space below the cell in two panels, defaults to twoPanelLandscapeBorderSpaceDps
+        if not specified -->
+        <attr name="twoPanelLandscapeBorderSpaceVerticalDps" format="float" />
 
-        <attr name="iconImageSize" format="float" />
-        <!-- landscapeIconSize defaults to iconSize, if not specified -->
-        <attr name="landscapeIconSize" format="float" />
-        <attr name="iconTextSize" format="float" />
-        <!-- landscapeIconTextSize defaults to iconTextSize, if not specified -->
-        <attr name="landscapeIconTextSize" format="float" />
-
-        <!-- If set, this display option is used to determine the default grid -->
-        <attr name="canBeDefault" format="boolean|integer" >
-            <!-- The profile can be default on split display devices -->
-            <flag name="split_display" value="0x2" />
-        </attr>
-
+        <!-- allAppsCellSpacingDps defaults to borderSpaceDps, if not specified -->
+        <attr name="allAppsCellSpacingDps" format="float" />
         <!-- The following values are only enabled if grid is supported. -->
         <!-- allAppsIconSize defaults to iconSize, if not specified -->
         <attr name="allAppsIconSize" format="float" />
         <!-- allAppsIconTextSize defaults to iconTextSize, if not specified -->
         <attr name="allAppsIconTextSize" format="float" />
 
+        <attr name="iconImageSize" format="float" />
+        <!-- landscapeIconSize defaults to iconImageSize, if not specified -->
+        <attr name="landscapeIconSize" format="float" />
+        <!-- twoPanelPortraitIconSize defaults to iconImageSize, if not specified -->
+        <attr name="twoPanelPortraitIconSize" format="float" />
+        <!-- twoPanelLandscapeIconSize defaults to iconImageSize, if not specified -->
+        <attr name="twoPanelLandscapeIconSize" format="float" />
+
+        <attr name="iconTextSize" format="float" />
+        <!-- landscapeIconTextSize defaults to iconTextSize, if not specified -->
+        <attr name="landscapeIconTextSize" format="float" />
+        <!-- twoPanelPortraitIconTextSize defaults to iconTextSize, if not specified -->
+        <attr name="twoPanelPortraitIconTextSize" format="float" />
+        <!-- twoPanelLandscapeIconTextSize defaults to iconTextSize, if not specified -->
+        <attr name="twoPanelLandscapeIconTextSize" format="float" />
+
+        <!-- If set, this display option is used to determine the default grid -->
+        <attr name="canBeDefault" format="boolean" />
+
+        <!-- Margin on left and right of the workspace when GridDisplayOption#isScalable is true -->
+        <attr name="horizontalMargin" format="float"/>
+        <!-- twoPanelLandscapeHorizontalMargin defaults to horizontalMargin if not specified -->
+        <attr name="twoPanelLandscapeHorizontalMargin" format="float"/>
+        <!-- twoPanelPortraitHorizontalMargin defaults to horizontalMargin if not specified -->
+        <attr name="twoPanelPortraitHorizontalMargin" format="float"/>
     </declare-styleable>
 
     <declare-styleable name="CellLayout">
@@ -231,4 +281,18 @@
     <declare-styleable name="WidgetsListRowHeader">
         <attr name="appIconSize" format="dimension" />
     </declare-styleable>
+
+    <declare-styleable name="WidgetSections">
+        <!-- Component name of an app widget provider. -->
+        <attr name="provider" format="string" />
+        <!-- If true, keep the app widget under its app listing in addition to the widget category
+             in the widget picker. Defaults to false if not specified. -->
+        <attr name="alsoKeepInApp" format="boolean" />
+        <!-- The category of an app widget provider. Defaults to -1 if not specified. -->
+        <attr name="category" format="integer" />
+        <!-- The title name of a widget category. -->
+        <attr name="sectionTitle" format="reference" />
+        <!-- The icon drawable of a widget category. -->
+        <attr name="sectionDrawable" format="reference" />
+    </declare-styleable>
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 1b68fb6..0b1b451 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -36,15 +36,6 @@
 
     <color name="icon_background">#E0E0E0</color> <!-- Gray 300 -->
 
-    <color name="gesture_tutorial_ripple_color">#A0C2F9</color> <!-- Light Blue -->
-    <color name="gesture_tutorial_fake_task_view_color">#6DA1FF</color> <!-- Light Blue -->
-    <color name="fake_wallpaper_color_dark_mode">#000000</color> <!-- Black -->
-    <color name="fake_wallpaper_color_light_mode">#f9f9f9</color> <!-- White -->
-    <!-- Must contrast fake_wallpaper_color_dark_mode and fake_wallpaper_color_light_mode -->
-    <color name="gesture_tutorial_fake_previous_task_view_color">#3C4043</color> <!-- Gray -->
-    <color name="gesture_tutorial_action_button_label_color">#FF000000</color>
-    <color name="gesture_tutorial_primary_color">#B7F29F</color> <!-- Light Green -->
-
     <color name="popup_color_primary_light">#FFF</color>
     <color name="popup_color_secondary_light">#F1F3F4</color>
     <color name="popup_color_tertiary_light">#E0E0E0</color> <!-- Gray 300 -->
@@ -72,7 +63,12 @@
     <color name="folder_background_light">#F9F9F9</color>
     <color name="folder_background_dark">#464746</color>
 
+    <color name="folder_preview_light">#F9F9F9</color>
+    <color name="folder_preview_dark">#464746</color>
+
     <color name="folder_dot_color">?attr/colorPrimary</color>
+    <color name="folder_pagination_color_light">#ff006c5f</color>
+    <color name="folder_pagination_color_dark">#ffbfebe3</color>
 
     <color name="text_color_primary_dark">#FFFFFFFF</color>
     <color name="text_color_secondary_dark">#FFFFFFFF</color>
diff --git a/res/values/config.xml b/res/values/config.xml
index 72959b2..6fdb4de 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -176,7 +176,7 @@
 
     <item name="staggered_damping_ratio" type="dimen" format="float">0.7</item>
     <item name="staggered_stiffness" type="dimen" format="float">150</item>
-    <dimen name="unlock_staggered_velocity_dp_per_s">4dp</dimen>
+    <dimen name="unlock_staggered_velocity_dp_per_s">2dp</dimen>
 
     <item name="hint_scale_damping_ratio" type="dimen" format="float">0.7</item>
     <item name="hint_scale_stiffness" type="dimen" format="float">200</item>
@@ -184,8 +184,8 @@
 
     <!-- Swipe up to home related -->
     <dimen name="swipe_up_fling_min_visible_change">18dp</dimen>
-    <dimen name="swipe_up_y_overshoot">10dp</dimen>
     <dimen name="swipe_up_max_workspace_trans_y">-60dp</dimen>
+    <dimen name="swipe_up_max_velocity">7.619dp</dimen>
 
     <array name="dynamic_resources">
         <item>@dimen/swipe_up_duration</item>
@@ -201,6 +201,7 @@
         <item>@dimen/swipe_up_launcher_alpha_max_progress</item>
         <item>@dimen/swipe_up_rect_2_y_stiffness_low_swipe_multiplier</item>
         <item>@dimen/swipe_up_low_swipe_duration_multiplier</item>
+        <item>@dimen/swipe_up_max_velocity</item>
 
         <item>@dimen/c1_a</item>
         <item>@dimen/c1_b</item>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a1e4cd9..4d137c8 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -29,8 +29,6 @@
     <dimen name="dynamic_grid_cell_layout_padding">5.5dp</dimen>
     <dimen name="dynamic_grid_cell_padding_x">8dp</dimen>
 
-    <dimen name="two_panel_home_side_padding">18dp</dimen>
-
     <!-- Hotseat -->
     <dimen name="dynamic_grid_hotseat_top_padding">8dp</dimen>
     <dimen name="dynamic_grid_hotseat_bottom_padding">2dp</dimen>
@@ -40,7 +38,6 @@
     <dimen name="dynamic_grid_hotseat_side_padding">0dp</dimen>
 
     <!-- Scalable Grid -->
-    <dimen name="scalable_grid_left_right_margin">22dp</dimen>
     <dimen name="scalable_grid_qsb_bottom_margin">42dp</dimen>
 
     <!-- Workspace page indicator -->
@@ -56,6 +53,7 @@
     <dimen name="widget_handle_margin">13dp</dimen>
     <dimen name="resize_frame_background_padding">24dp</dimen>
     <dimen name="resize_frame_margin">22dp</dimen>
+    <dimen name="resize_frame_invalid_drag_across_two_panel_opacity_margin">24dp</dimen>
 
     <!-- App widget reconfigure button -->
     <dimen name="widget_reconfigure_button_corner_radius">14dp</dimen>
@@ -105,6 +103,7 @@
     <dimen name="all_apps_tabs_vertical_padding">6dp</dimen>
     <dimen name="all_apps_divider_height">2dp</dimen>
     <dimen name="all_apps_divider_width">128dp</dimen>
+    <dimen name="all_apps_content_fade_in_offset">150dp</dimen>
 
     <dimen name="all_apps_tip_bottom_margin">8dp</dimen>
     <!-- The size of corner radius of the arrow in the arrow toast. -->
@@ -126,7 +125,6 @@
     <dimen name="work_card_button_height">52dp</dimen>
     <dimen name="work_fab_margin">16dp</dimen>
     <dimen name="work_profile_footer_padding">20dp</dimen>
-    <dimen name="work_profile_footer_text_size">16sp</dimen>
     <dimen name="work_edu_card_margin">16dp</dimen>
     <dimen name="work_edu_card_radius">28dp</dimen>
 
@@ -143,8 +141,7 @@
     <dimen name="widget_cell_font_size">14sp</dimen>
 
     <dimen name="widget_tabs_button_horizontal_padding">4dp</dimen>
-    <dimen name="widget_tabs_horizontal_margin">32dp</dimen>
-    <dimen name="widget_apps_header_pill_height">48dp</dimen>
+    <dimen name="widget_tabs_horizontal_padding">16dp</dimen>
     <dimen name="widget_apps_tabs_vertical_padding">6dp</dimen>
 
     <dimen name="recommended_widgets_table_vertical_padding">8dp</dimen>
@@ -178,8 +175,6 @@
     <dimen name="widget_picker_education_tip_max_width">308dp</dimen>
     <dimen name="widget_picker_education_tip_min_margin">4dp</dimen>
 
-    <dimen name="widget_picker_view_pager_top_padding">10dp</dimen>
-
     <!-- Padding applied to shortcut previews -->
     <dimen name="shortcut_preview_padding_left">0dp</dimen>
     <dimen name="shortcut_preview_padding_right">0dp</dimen>
@@ -317,16 +312,34 @@
 
 <!-- Taskbar related (placeholders to compile in Launcher3 without Quickstep) -->
     <dimen name="taskbar_size">0dp</dimen>
+    <dimen name="qsb_widget_height">0dp</dimen>
+    <dimen name="taskbar_icon_size">44dp</dimen>
+    <!-- Note that this applies to both sides of all icons, so visible space is double this. -->
+    <dimen name="taskbar_icon_spacing">8dp</dimen>
 
     <!-- Size of the maximum radius for the enforced rounded rectangles. -->
     <dimen name="enforced_rounded_corner_max_radius">16dp</dimen>
 
-<!-- Overview placeholder to compile in Launcer3 without Quickstep -->
+<!-- Overview placeholder to compile in Launcher3 without Quickstep -->
     <dimen name="task_thumbnail_icon_size">0dp</dimen>
-    <dimen name="task_thumbnail_icon_size_grid">0dp</dimen>
+    <dimen name="task_thumbnail_icon_drawable_size">0dp</dimen>
+    <dimen name="task_thumbnail_icon_drawable_size_grid">0dp</dimen>
     <dimen name="overview_task_margin">0dp</dimen>
-    <dimen name="overview_actions_bottom_margin_gesture">0dp</dimen>
-    <dimen name="overview_actions_bottom_margin_three_button">0dp</dimen>
+    <dimen name="overview_task_margin_focused">0dp</dimen>
+    <dimen name="overview_task_margin_grid">0dp</dimen>
+    <dimen name="overview_actions_margin_gesture">0dp</dimen>
+    <dimen name="overview_actions_top_margin_gesture_grid_portrait">0dp</dimen>
+    <dimen name="overview_actions_bottom_margin_gesture_grid_portrait">0dp</dimen>
+    <dimen name="overview_actions_top_margin_gesture_grid_landscape">0dp</dimen>
+    <dimen name="overview_actions_bottom_margin_gesture_grid_landscape">0dp</dimen>
+    <dimen name="overview_actions_margin_three_button">0dp</dimen>
+    <dimen name="overview_grid_row_spacing_portrait">0dp</dimen>
+    <dimen name="overview_grid_row_spacing_landscape">0dp</dimen>
+    <dimen name="recents_page_spacing">0dp</dimen>
+    <dimen name="recents_page_spacing_grid">0dp</dimen>
+    <dimen name="split_placeholder_size">110dp</dimen>
+    <dimen name="task_menu_width_grid">200dp</dimen>
+
 
 <!-- Workspace grid visualization parameters -->
     <dimen name="grid_visualization_rounding_radius">22dp</dimen>
diff --git a/res/values/id.xml b/res/values/id.xml
index 1709c59..ebc4075 100644
--- a/res/values/id.xml
+++ b/res/values/id.xml
@@ -17,7 +17,17 @@
 <resources>
     <item type="id" name="apps_list_view_work" />
     <item type="id" name="tag_widget_entry" />
+    <item type="id" name="view_type_widgets_space" />
     <item type="id" name="view_type_widgets_list" />
     <item type="id" name="view_type_widgets_header" />
     <item type="id" name="view_type_widgets_search_header" />
+
+    <!--  Do not change, must be kept in sync with sysui navbar button IDs for tests!  -->
+    <item type="id" name="home" />
+    <item type="id" name="recent_apps" />
+    <item type="id" name="back" />
+    <item type="id" name="ime_switcher" />
+    <item type="id" name="accessibility_button" />
+    <item type="id" name="rotate_suggestion" />
+    <!--  /Do not change, must be kept in sync with sysui navbar button IDs for tests!  -->
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d7a2d47..d7a1506 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
 /*
 * Copyright (C) 2008 The Android Open Source Project
 *
@@ -67,17 +66,15 @@
          button in a dialog. [CHAR_LIMIT=none] -->
     <string name="added_to_home_screen_accessibility_text"><xliff:g id="widget_name" example="Calendar month view">%1$s</xliff:g> widget added to home screen</string>
     <!-- Label for showing the number of widgets an app has in the full widgets picker.
-         [CHAR_LIMIT=25] -->
-    <plurals name="widgets_count">
-        <item quantity="one"><xliff:g id="widgets_count" example="1">%1$d</xliff:g> widget</item>
-        <item quantity="other"><xliff:g id="widgets_count" example="2">%1$d</xliff:g> widgets</item>
-    </plurals>
+         [CHAR_LIMIT=25][ICU SYNTAX] -->
+    <string name="widgets_count">
+        {count, plural, =1{# widget} other{# widgets}}
+    </string>
     <!-- Label for showing the number of shortcut an app has in the full widgets picker.
-         [CHAR_LIMIT=25] -->
-    <plurals name="shortcuts_count">
-        <item quantity="one"><xliff:g id="shortcuts_count" example="1">%1$d</xliff:g> shortcut</item>
-        <item quantity="other"><xliff:g id="shortcuts_count" example="2">%1$d</xliff:g> shortcuts</item>
-    </plurals>
+         [CHAR_LIMIT=25][ICU SYNTAX] -->
+    <string name="shortcuts_count">
+        {count, plural, =1{# shortcut} other{# shortcuts}}
+    </string>
     <!-- Label for showing both the number of widgets and shortcuts an app has in the full widgets
          picker. [CHAR_LIMIT=50] -->
     <string name="widgets_and_shortcuts_count"><xliff:g id="widgets_count" example="5 widgets">%1$s</xliff:g>, <xliff:g id="shortcuts_count" example="1 shortcut">%2$s</xliff:g></string>
@@ -134,6 +131,8 @@
     <string name="all_apps_search_market_message">Search for more apps</string>
     <!-- Label for an icon representing any generic app. [CHAR_LIMIT=50] -->
     <string name="label_application">App</string>
+    <!-- Label for the header text of the All Apps section in All Apps view, used to separate Predicted Apps and Actions section from All Apps section. [CHAR_LIMIT=50] -->
+    <string name="all_apps_label">All apps</string>
 
     <!-- Popup items -->
     <!-- Text to display as the header above notifications. [CHAR_LIMIT=30] -->
@@ -195,7 +194,7 @@
     <string name="msg_no_phone_permission"><xliff:g id="app_name" example="Launcher3">%1$s</xliff:g> is not allowed to make phone calls</string>
 
     <!-- Widgets: -->
-    <skip />    
+    <skip />
 
     <!-- Error text that lets a user know that the widget can't load. -->
     <string name="gadget_error_text">Can\'t load widget</string>
@@ -215,11 +214,13 @@
     <!-- Accessibility -->
     <!-- The format string for when an app is temporarily disabled. -->
     <string name="disabled_app_label">Disabled <xliff:g id="app_name" example="Messenger">%1$s</xliff:g></string>
-    <!-- The format string for when an app has a notification dot (meaning it has associated notifications). -->
-    <plurals name="dotted_app_label">
-        <item quantity="one"><xliff:g id="app_name" example="Messenger">%1$s</xliff:g>, has <xliff:g id="notification_count" example="1">%2$d</xliff:g> notification</item>
-        <item quantity="other"><xliff:g id="app_name" example="Messenger">%1$s</xliff:g>, has <xliff:g id="notification_count" example="3">%2$d</xliff:g> notifications</item>
-    </plurals>
+    <!-- The format string for when an app has a notification dot (meaning it has associated notifications). [ICU_FORMAT]-->
+    <string name="dotted_app_label">
+        {count, plural, offset:1
+            =1      {{app_name} has # notification}
+            other   {{app_name} has # notifications}
+        }
+    </string>
     <skip />
 
     <!-- The format string for default page scroll text [CHAR_LIMIT=none] -->
@@ -308,7 +309,7 @@
     <!-- Text announced by accessibility when the popup containing the list of widgets is closed. [CHAR_LIMIT=100] -->
     <string name="widgets_list_closed">Widgets list closed</string>
 
-<!-- Strings for accessibility actions -->
+    <!-- Strings for accessibility actions -->
     <!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] -->
     <string name="action_add_to_workspace">Add to Home screen</string>
 
diff --git a/res/values/styles.xml b/res/values/styles.xml
index b7661b9..818a032 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -50,7 +50,9 @@
         <item name="workspaceStatusBarScrim">@drawable/workspace_bg</item>
         <item name="widgetsTheme">@style/WidgetContainerTheme</item>
         <item name="folderDotColor">@color/folder_dot_color</item>
-        <item name="folderFillColor">@color/folder_background_light</item>
+        <item name="folderPaginationColor">@color/folder_pagination_color_light</item>
+        <item name="folderPreviewColor">@color/folder_preview_light</item>
+        <item name="folderBackgroundColor">@color/folder_background_light</item>
         <item name="folderIconBorderColor">?android:attr/colorPrimary</item>
         <item name="folderTextColor">@color/workspace_text_color_dark</item>
         <item name="isFolderDarkText">true</item>
@@ -108,7 +110,9 @@
         <item name="popupShadeThird">@color/popup_shade_third_dark</item>
         <item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
         <item name="folderDotColor">@color/folder_dot_color</item>
-        <item name="folderFillColor">@color/folder_background_dark</item>
+        <item name="folderPaginationColor">@color/folder_pagination_color_dark</item>
+        <item name="folderPreviewColor">@color/folder_preview_dark</item>
+        <item name="folderBackgroundColor">@color/folder_background_dark</item>
         <item name="folderIconBorderColor">?android:attr/colorPrimary</item>
         <item name="folderTextColor">@color/workspace_text_color_light</item>
         <item name="isFolderDarkText">false</item>
diff --git a/res/xml/default_workspace_5x5.xml b/res/xml/default_workspace_5x5.xml
index ccdde2c..b4ac8f6 100644
--- a/res/xml/default_workspace_5x5.xml
+++ b/res/xml/default_workspace_5x5.xml
@@ -94,4 +94,5 @@
         <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MARKET;end" />
         <favorite launcher:uri="market://details?id=com.android.launcher" />
     </resolve>
+
 </favorites>
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 256999c..e030f81 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -121,7 +121,7 @@
             launcher:minHeightDps="694"
             launcher:iconImageSize="56"
             launcher:iconTextSize="14.4"
-            launcher:canBeDefault="split_display" />
+            launcher:canBeDefault="true" />
 
         <display-option
             launcher:name="Shorter Stubby"
diff --git a/res/xml/device_profiles_split.xml b/res/xml/device_profiles_split.xml
new file mode 100644
index 0000000..2fad0c9
--- /dev/null
+++ b/res/xml/device_profiles_split.xml
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<profiles xmlns:launcher="http://schemas.android.com/apk/res-auto" >
+
+    <grid-option
+        launcher:name="3_by_3"
+        launcher:numRows="3"
+        launcher:numColumns="3"
+        launcher:numFolderRows="2"
+        launcher:numFolderColumns="3"
+        launcher:numHotseatIcons="3"
+        launcher:dbFile="launcher_3_by_3.db"
+        launcher:defaultLayoutId="@xml/default_workspace_3x3" >
+
+        <display-option
+            launcher:name="Super Short Stubby"
+            launcher:minWidthDps="255"
+            launcher:minHeightDps="300"
+            launcher:iconImageSize="48"
+            launcher:iconTextSize="13.0"
+            launcher:canBeDefault="true" />
+
+        <display-option
+            launcher:name="Shorter Stubby"
+            launcher:minWidthDps="255"
+            launcher:minHeightDps="400"
+            launcher:iconImageSize="48"
+            launcher:iconTextSize="13.0"
+            launcher:canBeDefault="true" />
+
+    </grid-option>
+
+    <grid-option
+        launcher:name="4_by_4"
+        launcher:numRows="4"
+        launcher:numColumns="4"
+        launcher:numFolderRows="3"
+        launcher:numFolderColumns="4"
+        launcher:numHotseatIcons="4"
+        launcher:dbFile="launcher_4_by_4.db"
+        launcher:defaultLayoutId="@xml/default_workspace_4x4" >
+
+        <display-option
+            launcher:name="Short Stubby"
+            launcher:minWidthDps="275"
+            launcher:minHeightDps="420"
+            launcher:iconImageSize="48"
+            launcher:iconTextSize="13.0"
+            launcher:canBeDefault="true" />
+
+        <display-option
+            launcher:name="Stubby"
+            launcher:minWidthDps="255"
+            launcher:minHeightDps="450"
+            launcher:iconImageSize="48"
+            launcher:iconTextSize="13.0"
+            launcher:canBeDefault="true" />
+
+        <display-option
+            launcher:name="Nexus S"
+            launcher:minWidthDps="296"
+            launcher:minHeightDps="491.33"
+            launcher:iconImageSize="48"
+            launcher:iconTextSize="13.0"
+            launcher:canBeDefault="true" />
+
+        <display-option
+            launcher:name="Nexus 4"
+            launcher:minWidthDps="359"
+            launcher:minHeightDps="567"
+            launcher:iconImageSize="54"
+            launcher:iconTextSize="13.0"
+            launcher:canBeDefault="true" />
+
+        <display-option
+            launcher:name="Nexus 5"
+            launcher:minWidthDps="335"
+            launcher:minHeightDps="567"
+            launcher:iconImageSize="54"
+            launcher:iconTextSize="13.0"
+            launcher:canBeDefault="true" />
+
+    </grid-option>
+
+    <grid-option
+        launcher:name="5_by_5"
+        launcher:numRows="5"
+        launcher:numColumns="5"
+        launcher:numFolderRows="4"
+        launcher:numFolderColumns="4"
+        launcher:numHotseatIcons="5"
+        launcher:numExtendedHotseatIcons="8"
+        launcher:dbFile="launcher.db"
+        launcher:defaultLayoutId="@xml/default_workspace_5x5" >
+
+        <display-option
+            launcher:name="Large Phone"
+            launcher:minWidthDps="406"
+            launcher:minHeightDps="694"
+            launcher:iconImageSize="56"
+            launcher:iconTextSize="14.4"
+            launcher:canBeDefault="true" />
+
+        <display-option
+            launcher:name="Large Phone Split Display"
+            launcher:minWidthDps="406"
+            launcher:minHeightDps="694"
+            launcher:iconImageSize="56"
+            launcher:iconTextSize="14.4"
+            launcher:canBeDefault="true" />
+
+        <display-option
+            launcher:name="Shorter Stubby"
+            launcher:minWidthDps="255"
+            launcher:minHeightDps="400"
+            launcher:iconImageSize="48"
+            launcher:iconTextSize="13.0"
+            launcher:canBeDefault="true" />
+
+    </grid-option>
+
+</profiles>
\ No newline at end of file
diff --git a/res/xml/size_limits_80x104.xml b/res/xml/size_limits_80x104.xml
deleted file mode 100644
index 4178664..0000000
--- a/res/xml/size_limits_80x104.xml
+++ /dev/null
@@ -1,114 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-
-<device-paddings xmlns:launcher="http://schemas.android.com/apk/res-auto" >
-
-    <device-padding
-        launcher:maxEmptySpace="88dp">
-        <workspaceTopPadding
-            launcher:a="0"
-            launcher:b="0"/>
-        <workspaceBottomPadding
-            launcher:a="0.52"
-            launcher:b="0"/>
-        <hotseatBottomPadding
-            launcher:a="0.48"
-            launcher:b="0"/>
-    </device-padding>
-
-    <device-padding
-        launcher:maxEmptySpace="100dp">
-        <workspaceTopPadding
-            launcher:a="0"
-            launcher:b="9dp"/>
-        <workspaceBottomPadding
-            launcher:a="0.40"
-            launcher:b="0"
-            launcher:c="9dp"/>
-        <hotseatBottomPadding
-            launcher:a="0.60"
-            launcher:b="0"
-            launcher:c="9dp"/>
-    </device-padding>
-
-    <device-padding
-        launcher:maxEmptySpace="103dp">
-        <workspaceTopPadding
-            launcher:a="0"
-            launcher:b="26dp"/>
-        <workspaceBottomPadding
-            launcher:a="0"
-            launcher:b="20dp"/>
-        <hotseatBottomPadding
-            launcher:a="1"
-            launcher:b="0"
-            launcher:c="46dp"/>
-    </device-padding>
-
-    <device-padding
-        launcher:maxEmptySpace="107dp">
-        <workspaceTopPadding
-            launcher:a="0"
-            launcher:b="9dp"/>
-        <workspaceBottomPadding
-            launcher:a="0"
-            launcher:b="34dp"/>
-        <hotseatBottomPadding
-            launcher:a="1"
-            launcher:b="0"
-            launcher:c="43dp"/>
-    </device-padding>
-
-    <device-padding
-        launcher:maxEmptySpace="120dp">
-        <workspaceTopPadding
-            launcher:a="0"
-            launcher:b="16dp"/>
-        <workspaceBottomPadding
-            launcher:a="1"
-            launcher:c="72dp"/>
-        <hotseatBottomPadding
-            launcher:a="0"
-            launcher:b="56dp"/>
-    </device-padding>
-
-    <device-padding
-        launcher:maxEmptySpace="135dp">
-        <workspaceTopPadding
-            launcher:a="0"
-            launcher:b="39dp"/>
-        <workspaceBottomPadding
-            launcher:a="1"
-            launcher:c="95dp"/>
-        <hotseatBottomPadding
-            launcher:a="0"
-            launcher:b="56dp"/>
-    </device-padding>
-
-    <device-padding
-        launcher:maxEmptySpace="9999dp">
-        <workspaceTopPadding
-            launcher:a="0.40"
-            launcher:c="36dp"/>
-        <workspaceBottomPadding
-            launcher:a="0.60"
-            launcher:c="36dp"/>
-        <hotseatBottomPadding
-            launcher:a="0"
-            launcher:b="36dp"/>
-    </device-padding>
-</device-paddings>
\ No newline at end of file
diff --git a/res/drawable/widgets_bottom_sheet_background.xml b/res/xml/widget_sections.xml
similarity index 62%
rename from res/drawable/widgets_bottom_sheet_background.xml
rename to res/xml/widget_sections.xml
index b877546..d755de6 100644
--- a/res/drawable/widgets_bottom_sheet_background.xml
+++ b/res/xml/widget_sections.xml
@@ -14,13 +14,12 @@
      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/surface" />
-    <corners
-        android:topLeftRadius="@dimen/default_dialog_corner_radius"
-        android:topRightRadius="@dimen/default_dialog_corner_radius"
-        android:bottomLeftRadius="0dp"
-        android:bottomRightRadius="0dp"
-        />
-</shape>
\ No newline at end of file
+
+<widget-sections xmlns:launcher="http://schemas.android.com/apk/res-auto">
+    <section
+        launcher:category="0"
+        launcher:sectionDrawable="@drawable/ic_conversations_widget_category"
+        launcher:sectionTitle="@string/widget_category_conversations">
+        <widget launcher:provider="com.android.systemui/.people.widget.PeopleSpaceWidgetProvider" />
+    </section>
+</widget-sections>
\ No newline at end of file
diff --git a/robolectric_tests/Android.bp b/robolectric_tests/Android.bp
deleted file mode 100644
index 9ed26ff..0000000
--- a/robolectric_tests/Android.bp
+++ /dev/null
@@ -1,63 +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.
-
-//
-// Launcher Robolectric test target.
-//
-//        "robolectric_android-all-stub", not needed, we write our own stubs
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "packages_apps_Launcher3_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["packages_apps_Launcher3_license"],
-}
-
-filegroup {
-    name: "launcher3-robolectric-resources",
-    path: "resources",
-    srcs: ["resources/*"],
-}
-
-filegroup {
-    name: "launcher3-robolectric-src",
-    srcs: ["src/**/*.java"],
-}
-
-android_robolectric_test {
-    name: "LauncherRoboTests",
-    srcs: [
-        ":launcher3-robolectric-src",
-        ":launcher3-test-src-common",
-    ],
-    java_resources: [":launcher3-robolectric-resources"],
-    static_libs: [
-        "truth-prebuilt",
-        "androidx.test.espresso.contrib",
-        "androidx.test.espresso.core",
-        "androidx.test.espresso.intents",
-        "androidx.test.ext.junit",
-        "androidx.test.runner",
-        "androidx.test.rules",
-        "mockito-robolectric-prebuilt",
-        "SystemUISharedLib",
-    ],
-    robolectric_prebuilt_version: "4.5.1",
-    instrumentation_for: "Launcher3",
-
-    test_options: {
-        timeout: 36000,
-    },
-}
diff --git a/robolectric_tests/res/values/overlayable_icons_test.xml b/robolectric_tests/res/values/overlayable_icons_test.xml
deleted file mode 100644
index 5144e52..0000000
--- a/robolectric_tests/res/values/overlayable_icons_test.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
-   Copyright (C) 2019 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
--->
-<resources>
-    <!-- overlayable_icons references all of the drawables in this package
-         that are being overlayed by resource overlays. If you remove/rename
-         any of these resources, you must also change the resource overlay icons.-->
-    <array name="overlayable_icons">
-        <item>@drawable/ic_corp</item>
-        <item>@drawable/ic_drag_handle</item>
-        <item>@drawable/ic_hourglass_top</item>
-        <item>@drawable/ic_info_no_shadow</item>
-        <item>@drawable/ic_install_no_shadow</item>
-        <item>@drawable/ic_palette</item>
-        <item>@drawable/ic_pin</item>
-        <item>@drawable/ic_remove_no_shadow</item>
-        <item>@drawable/ic_setting</item>
-        <item>@drawable/ic_smartspace_preferences</item>
-        <item>@drawable/ic_split_screen</item>
-        <item>@drawable/ic_uninstall_no_shadow</item>
-        <item>@drawable/ic_warning</item>
-        <item>@drawable/ic_widget</item>
-    </array>
-</resources>
diff --git a/robolectric_tests/resources/robolectric.properties b/robolectric_tests/resources/robolectric.properties
deleted file mode 100644
index abb6968..0000000
--- a/robolectric_tests/resources/robolectric.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-sdk=30
-
-shadows= \
-    com.android.launcher3.shadows.LShadowAppPredictionManager \
-    com.android.launcher3.shadows.LShadowAppWidgetManager \
-    com.android.launcher3.shadows.LShadowBackupManager \
-    com.android.launcher3.shadows.LShadowDisplay \
-    com.android.launcher3.shadows.LShadowLauncherApps \
-    com.android.launcher3.shadows.ShadowDeviceFlag \
-    com.android.launcher3.shadows.ShadowLooperExecutor \
-    com.android.launcher3.shadows.ShadowMainThreadInitializedObject \
-    com.android.launcher3.shadows.ShadowOverrides \
-    com.android.launcher3.shadows.ShadowSurfaceTransactionApplier \
-
-application=com.android.launcher3.util.LauncherTestApplication
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java b/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java
deleted file mode 100644
index 56ce215..0000000
--- a/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-package com.android.launcher3.model;
-
-
-import static android.database.DatabaseUtils.queryNumEntries;
-
-import static com.android.launcher3.LauncherSettings.Favorites.BACKUP_TABLE_NAME;
-import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
-import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
-import static com.android.launcher3.util.LauncherModelHelper.APP_ICON;
-import static com.android.launcher3.util.LauncherModelHelper.DESKTOP;
-import static com.android.launcher3.util.LauncherModelHelper.NO__ICON;
-import static com.android.launcher3.util.LauncherModelHelper.SHORTCUT;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Point;
-
-import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.LauncherSettings.Settings;
-import com.android.launcher3.util.LauncherModelHelper;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-/**
- * Unit tests for {@link GridBackupTable}
- */
-@RunWith(RobolectricTestRunner.class)
-public class GridBackupTableTest {
-
-    private static final int BACKUP_ITEM_COUNT = 12;
-
-    private LauncherModelHelper mModelHelper;
-    private Context mContext;
-    private SQLiteDatabase mDb;
-
-    @Before
-    public void setUp() {
-        mModelHelper = new LauncherModelHelper();
-        mContext = RuntimeEnvironment.application;
-        mDb = mModelHelper.provider.getDb();
-
-        setupGridData();
-    }
-
-    private void setupGridData() {
-        mModelHelper.createGrid(new int[][][]{{
-                { APP_ICON, APP_ICON, SHORTCUT, SHORTCUT},
-                { SHORTCUT, SHORTCUT, NO__ICON, NO__ICON},
-                { NO__ICON, NO__ICON, SHORTCUT, SHORTCUT},
-                { APP_ICON, SHORTCUT, SHORTCUT, APP_ICON},
-        }});
-        assertEquals(BACKUP_ITEM_COUNT, queryNumEntries(mDb, TABLE_NAME));
-    }
-
-    @Test
-    public void backupTableCreated() {
-        GridBackupTable backupTable = new GridBackupTable(mContext, mDb, 4, 4, 4);
-        assertFalse(backupTable.backupOrRestoreAsNeeded());
-        Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
-
-        assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
-
-        // One extra entry for properties
-        assertEquals(BACKUP_ITEM_COUNT + 1, queryNumEntries(mDb, BACKUP_TABLE_NAME));
-    }
-
-    @Test
-    public void backupTableRestored() {
-        assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
-        Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
-
-        // Delete entries
-        mDb.delete(TABLE_NAME, null, null);
-        assertEquals(0, queryNumEntries(mDb, TABLE_NAME));
-
-        GridBackupTable backupTable = new GridBackupTable(mContext, mDb, 3, 3, 3);
-        assertTrue(backupTable.backupOrRestoreAsNeeded());
-
-        // Items have been restored
-        assertEquals(BACKUP_ITEM_COUNT, queryNumEntries(mDb, TABLE_NAME));
-
-        Point outSize = new Point();
-        assertEquals(4, backupTable.getRestoreHotseatAndGridSize(outSize));
-        assertEquals(4, outSize.x);
-        assertEquals(4, outSize.y);
-    }
-
-    @Test
-    public void backupTableRemovedOnAdd() {
-        assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
-        Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
-
-        assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
-
-        mModelHelper.addItem(1, 2, DESKTOP, 1, 1);
-        assertFalse(tableExists(mDb, BACKUP_TABLE_NAME));
-    }
-
-    @Test
-    public void backupTableRemovedOnDelete() {
-        assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
-        Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
-
-        assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
-
-        mContext.getContentResolver().delete(Favorites.CONTENT_URI, null, null);
-        assertFalse(tableExists(mDb, BACKUP_TABLE_NAME));
-    }
-
-    @Test
-    public void backupTableRetainedOnUpdate() {
-        assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
-        Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
-
-        assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
-
-        ContentValues values = new ContentValues();
-        values.put(Favorites.RANK, 4);
-        // Something was updated
-        assertTrue(mContext.getContentResolver()
-                .update(Favorites.CONTENT_URI, values, null, null) > 0);
-
-        // Backup table remains
-        assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
deleted file mode 100644
index d544a0b..0000000
--- a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
+++ /dev/null
@@ -1,373 +0,0 @@
-package com.android.launcher3.model;
-
-import static com.android.launcher3.model.GridSizeMigrationTask.getWorkspaceScreenIds;
-import static com.android.launcher3.util.LauncherModelHelper.APP_ICON;
-import static com.android.launcher3.util.LauncherModelHelper.HOTSEAT;
-import static com.android.launcher3.util.LauncherModelHelper.SHORTCUT;
-import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Point;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.model.GridSizeMigrationTask.MultiStepMigrationTask;
-import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.LauncherModelHelper;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-import java.util.HashSet;
-import java.util.LinkedList;
-
-/**
- * Unit tests for {@link GridSizeMigrationTask}
- */
-@RunWith(RobolectricTestRunner.class)
-public class GridSizeMigrationTaskTest {
-
-    private LauncherModelHelper mModelHelper;
-    private Context mContext;
-    private SQLiteDatabase mDb;
-
-    private HashSet<String> mValidPackages;
-    private InvariantDeviceProfile mIdp;
-
-    @Before
-    public void setUp() {
-        mModelHelper = new LauncherModelHelper();
-        mContext = RuntimeEnvironment.application;
-        mDb = mModelHelper.provider.getDb();
-
-        mValidPackages = new HashSet<>();
-        mValidPackages.add(TEST_PACKAGE);
-        mIdp = InvariantDeviceProfile.INSTANCE.get(mContext);
-    }
-
-    @Test
-    public void testHotseatMigration_apps_dropped() throws Exception {
-        int[] hotseatItems = {
-                mModelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0),
-                mModelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0),
-                -1,
-                mModelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0),
-                mModelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0),
-        };
-
-        mIdp.numDatabaseHotseatIcons = 3;
-        new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, 5, 3)
-                .migrateHotseat();
-        // First item is dropped as it has the least weight.
-        verifyHotseat(hotseatItems[1], hotseatItems[3], hotseatItems[4]);
-    }
-
-    @Test
-    public void testHotseatMigration_shortcuts_dropped() throws Exception {
-        int[] hotseatItems = {
-                mModelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0),
-                mModelHelper.addItem(30, 1, HOTSEAT, 0, 0),
-                -1,
-                mModelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0),
-                mModelHelper.addItem(10, 4, HOTSEAT, 0, 0),
-        };
-
-        mIdp.numDatabaseHotseatIcons = 3;
-        new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, 5, 3)
-                .migrateHotseat();
-        // First item is dropped as it has the least weight.
-        verifyHotseat(hotseatItems[1], hotseatItems[3], hotseatItems[4]);
-    }
-
-    private void verifyHotseat(int... sortedIds) {
-        int screenId = 0;
-        int total = 0;
-
-        for (int id : sortedIds) {
-            Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
-                    new String[]{LauncherSettings.Favorites._ID},
-                    "container=-101 and screen=" + screenId, null, null, null);
-
-            if (id == -1) {
-                assertEquals(0, c.getCount());
-            } else {
-                assertEquals(1, c.getCount());
-                c.moveToNext();
-                assertEquals(id, c.getLong(0));
-                total ++;
-            }
-            c.close();
-
-            screenId++;
-        }
-
-        // Verify that not other entry exist in the DB.
-        Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
-                new String[]{LauncherSettings.Favorites._ID},
-                "container=-101", null, null, null);
-        assertEquals(total, c.getCount());
-        c.close();
-    }
-
-    @Test
-    public void testWorkspace_empty_row_column_removed() throws Exception {
-        int[][][] ids = mModelHelper.createGrid(new int[][][]{{
-                {  0,  0, -1,  1},
-                {  3,  1, -1,  4},
-                { -1, -1, -1, -1},
-                {  5,  2, -1,  6},
-        }});
-
-        new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
-                new Point(4, 4), new Point(3, 3)).migrateWorkspace();
-
-        // Column 2 and row 2 got removed.
-        verifyWorkspace(new int[][][] {{
-                {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
-                {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
-                {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
-        }});
-    }
-
-    @Test
-    public void testWorkspace_new_screen_created() throws Exception {
-        int[][][] ids = mModelHelper.createGrid(new int[][][]{{
-                {  0,  0,  0,  1},
-                {  3,  1,  0,  4},
-                { -1, -1, -1, -1},
-                {  5,  2, -1,  6},
-        }});
-
-        new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
-                new Point(4, 4), new Point(3, 3)).migrateWorkspace();
-
-        // Items in the second column get moved to new screen
-        verifyWorkspace(new int[][][] {{
-                {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
-                {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
-                {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
-        }, {
-                {ids[0][0][2], ids[0][1][2], -1},
-        }});
-    }
-
-    @Test
-    public void testWorkspace_items_merged_in_next_screen() throws Exception {
-        int[][][] ids = mModelHelper.createGrid(new int[][][]{{
-                {  0,  0,  0,  1},
-                {  3,  1,  0,  4},
-                { -1, -1, -1, -1},
-                {  5,  2, -1,  6},
-        },{
-                {  0,  0, -1,  1},
-                {  3,  1, -1,  4},
-        }});
-
-        new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
-                new Point(4, 4), new Point(3, 3)).migrateWorkspace();
-
-        // Items in the second column of the first screen should get placed on the 3rd
-        // row of the second screen
-        verifyWorkspace(new int[][][] {{
-                {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
-                {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
-                {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
-        }, {
-                {ids[1][0][0], ids[1][0][1], ids[1][0][3]},
-                {ids[1][1][0], ids[1][1][1], ids[1][1][3]},
-                {ids[0][0][2], ids[0][1][2], -1},
-        }});
-    }
-
-    @Test
-    public void testWorkspace_items_not_merged_in_next_screen() throws Exception {
-        // First screen has 2 mItems that need to be moved, but second screen has only one
-        // empty space after migration (top-left corner)
-        int[][][] ids = mModelHelper.createGrid(new int[][][]{{
-                {  0,  0,  0,  1},
-                {  3,  1,  0,  4},
-                { -1, -1, -1, -1},
-                {  5,  2, -1,  6},
-        },{
-                { -1,  0, -1,  1},
-                {  3,  1, -1,  4},
-                { -1, -1, -1, -1},
-                {  5,  2, -1,  6},
-        }});
-
-        new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
-                new Point(4, 4), new Point(3, 3)).migrateWorkspace();
-
-        // Items in the second column of the first screen should get placed on a new screen.
-        verifyWorkspace(new int[][][] {{
-                {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
-                {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
-                {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
-        }, {
-                {          -1, ids[1][0][1], ids[1][0][3]},
-                {ids[1][1][0], ids[1][1][1], ids[1][1][3]},
-                {ids[1][3][0], ids[1][3][1], ids[1][3][3]},
-        }, {
-                {ids[0][0][2], ids[0][1][2], -1},
-        }});
-    }
-
-    @Test
-    public void testWorkspace_first_row_blocked() throws Exception {
-        if (!FeatureFlags.QSB_ON_FIRST_SCREEN) {
-            return;
-        }
-        // The first screen has one item on the 4th column which needs moving, as the first row
-        // will be kept empty.
-        int[][][] ids = mModelHelper.createGrid(new int[][][]{{
-                { -1, -1, -1, -1},
-                {  3,  1,  7,  0},
-                {  8,  7,  7, -1},
-                {  5,  2,  7, -1},
-        }}, 0);
-
-        new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
-                new Point(4, 4), new Point(3, 4)).migrateWorkspace();
-
-        // Items in the second column of the first screen should get placed on a new screen.
-        verifyWorkspace(new int[][][] {{
-                {          -1,           -1,           -1},
-                {ids[0][1][0], ids[0][1][1], ids[0][1][2]},
-                {ids[0][2][0], ids[0][2][1], ids[0][2][2]},
-                {ids[0][3][0], ids[0][3][1], ids[0][3][2]},
-        }, {
-                {ids[0][1][3]},
-        }});
-    }
-
-    @Test
-    public void testWorkspace_items_moved_to_empty_first_row() throws Exception {
-        if (!FeatureFlags.QSB_ON_FIRST_SCREEN) {
-            return;
-        }
-        // Items will get moved to the next screen to keep the first screen empty.
-        int[][][] ids = mModelHelper.createGrid(new int[][][]{{
-                { -1, -1, -1, -1},
-                {  0,  1,  0,  0},
-                {  8,  7,  7, -1},
-                {  5,  6,  7, -1},
-        }}, 0);
-
-        new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
-                new Point(4, 4), new Point(3, 3)).migrateWorkspace();
-
-        // Items in the second column of the first screen should get placed on a new screen.
-        verifyWorkspace(new int[][][] {{
-                {          -1,           -1,           -1},
-                {ids[0][2][0], ids[0][2][1], ids[0][2][2]},
-                {ids[0][3][0], ids[0][3][1], ids[0][3][2]},
-        }, {
-                {ids[0][1][1], ids[0][1][0], ids[0][1][2]},
-                {ids[0][1][3]},
-        }});
-    }
-
-    /**
-     * Verifies that the workspace mItems are arranged in the provided order.
-     * @param ids A 3d array where the first dimension represents the screen, and the rest two
-     *            represent the workspace grid.
-     */
-    private void verifyWorkspace(int[][][] ids) {
-        IntArray allScreens = getWorkspaceScreenIds(mDb, LauncherSettings.Favorites.TABLE_NAME);
-        assertEquals(ids.length, allScreens.size());
-        int total = 0;
-
-        for (int i = 0; i < ids.length; i++) {
-            int screenId = allScreens.get(i);
-            for (int y = 0; y < ids[i].length; y++) {
-                for (int x = 0; x < ids[i][y].length; x++) {
-                    int id = ids[i][y][x];
-
-                    Cursor c = mContext.getContentResolver().query(
-                            LauncherSettings.Favorites.CONTENT_URI,
-                            new String[]{LauncherSettings.Favorites._ID},
-                            "container=-100 and screen=" + screenId +
-                                    " and cellX=" + x + " and cellY=" + y, null, null, null);
-                    if (id == -1) {
-                        assertEquals(0, c.getCount());
-                    } else {
-                        assertEquals(1, c.getCount());
-                        c.moveToNext();
-                        assertEquals(String.format("Failed to verify item at %d %d, %d", i, y, x),
-                                id, c.getLong(0));
-                        total++;
-                    }
-                    c.close();
-                }
-            }
-        }
-
-        // Verify that not other entry exist in the DB.
-        Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
-                new String[]{LauncherSettings.Favorites._ID},
-                "container=-100", null, null, null);
-        assertEquals(total, c.getCount());
-        c.close();
-    }
-
-    @Test
-    public void testMultiStepMigration_small_to_large() throws Exception {
-        MultiStepMigrationTaskVerifier verifier = new MultiStepMigrationTaskVerifier();
-        verifier.migrate(new Point(3, 3), new Point(5, 5));
-        verifier.assertCompleted();
-    }
-
-    @Test
-    public void testMultiStepMigration_large_to_small() throws Exception {
-        MultiStepMigrationTaskVerifier verifier = new MultiStepMigrationTaskVerifier(
-                5, 5, 4, 4,
-                4, 4, 3, 4
-        );
-        verifier.migrate(new Point(5, 5), new Point(3, 4));
-        verifier.assertCompleted();
-    }
-
-    @Test
-    public void testMultiStepMigration_zig_zag() throws Exception {
-        MultiStepMigrationTaskVerifier verifier = new MultiStepMigrationTaskVerifier(
-                5, 7, 4, 7,
-                4, 7, 3, 7
-        );
-        verifier.migrate(new Point(5, 5), new Point(3, 7));
-        verifier.assertCompleted();
-    }
-
-    private static class MultiStepMigrationTaskVerifier extends MultiStepMigrationTask {
-
-        private final LinkedList<Point> mPoints;
-
-        public MultiStepMigrationTaskVerifier(int... points) {
-            super(null, null, null, false);
-
-            mPoints = new LinkedList<>();
-            for (int i = 0; i < points.length; i += 2) {
-                mPoints.add(new Point(points[i], points[i + 1]));
-            }
-        }
-
-        @Override
-        protected boolean runStepTask(Point sourceSize, Point nextSize) throws Exception {
-            assertEquals(sourceSize, mPoints.poll());
-            assertEquals(nextSize, mPoints.poll());
-            return false;
-        }
-
-        public void assertCompleted() {
-            assertTrue(mPoints.isEmpty());
-        }
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java b/robolectric_tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
deleted file mode 100644
index a2abfd5..0000000
--- a/robolectric_tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Copyright (C) 2020 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.model;
-
-import static com.android.launcher3.util.Executors.createAndStartNewLooper;
-import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.spy;
-import static org.robolectric.Shadows.shadowOf;
-
-import android.os.Process;
-
-import com.android.launcher3.PagedView;
-import com.android.launcher3.model.BgDataModel.Callbacks;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.shadows.ShadowLooperExecutor;
-import com.android.launcher3.util.Executors;
-import com.android.launcher3.util.LauncherLayoutBuilder;
-import com.android.launcher3.util.LauncherModelHelper;
-import com.android.launcher3.util.LooperExecutor;
-import com.android.launcher3.util.ViewOnDrawExecutor;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.LooperMode;
-import org.robolectric.annotation.LooperMode.Mode;
-import org.robolectric.shadow.api.Shadow;
-import org.robolectric.shadows.ShadowPackageManager;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * Tests to verify multiple callbacks in Loader
- */
-@RunWith(RobolectricTestRunner.class)
-@LooperMode(Mode.PAUSED)
-public class ModelMultiCallbacksTest {
-
-    private LauncherModelHelper mModelHelper;
-
-    private ShadowPackageManager mSpm;
-    private LooperExecutor mTempMainExecutor;
-
-    @Before
-    public void setUp() throws Exception {
-        mModelHelper = new LauncherModelHelper();
-        mModelHelper.installApp(TEST_PACKAGE);
-
-        mSpm = shadowOf(RuntimeEnvironment.application.getPackageManager());
-
-        // Since robolectric tests run on main thread, we run the loader-UI calls on a temp thread,
-        // so that we can wait appropriately for the loader to complete.
-        mTempMainExecutor = new LooperExecutor(createAndStartNewLooper("tempMain"));
-        ShadowLooperExecutor sle = Shadow.extract(Executors.MAIN_EXECUTOR);
-        sle.setHandler(mTempMainExecutor.getHandler());
-    }
-
-    @Test
-    public void testTwoCallbacks_loadedTogether() throws Exception {
-        setupWorkspacePages(3);
-
-        MyCallbacks cb1 = spy(MyCallbacks.class);
-        mModelHelper.getModel().addCallbacksAndLoad(cb1);
-
-        waitForLoaderAndTempMainThread();
-        cb1.verifySynchronouslyBound(3);
-
-        // Add a new callback
-        cb1.reset();
-        MyCallbacks cb2 = spy(MyCallbacks.class);
-        cb2.mPageToBindSync = 2;
-        mModelHelper.getModel().addCallbacksAndLoad(cb2);
-
-        waitForLoaderAndTempMainThread();
-        cb1.verifySynchronouslyBound(3);
-        cb2.verifySynchronouslyBound(3);
-
-        // Remove callbacks
-        cb1.reset();
-        cb2.reset();
-
-        // No effect on callbacks when removing an callback
-        mModelHelper.getModel().removeCallbacks(cb2);
-        waitForLoaderAndTempMainThread();
-        assertNull(cb1.mDeferredExecutor);
-        assertNull(cb2.mDeferredExecutor);
-
-        // Reloading only loads registered callbacks
-        mModelHelper.getModel().startLoader();
-        waitForLoaderAndTempMainThread();
-        cb1.verifySynchronouslyBound(3);
-        assertNull(cb2.mDeferredExecutor);
-    }
-
-    @Test
-    public void testTwoCallbacks_receiveUpdates() throws Exception {
-        setupWorkspacePages(1);
-
-        MyCallbacks cb1 = spy(MyCallbacks.class);
-        MyCallbacks cb2 = spy(MyCallbacks.class);
-        mModelHelper.getModel().addCallbacksAndLoad(cb1);
-        mModelHelper.getModel().addCallbacksAndLoad(cb2);
-        waitForLoaderAndTempMainThread();
-
-        cb1.verifyApps(TEST_PACKAGE);
-        cb2.verifyApps(TEST_PACKAGE);
-
-        // Install package 1
-        String pkg1 = "com.test.pkg1";
-        mModelHelper.installApp(pkg1);
-        mModelHelper.getModel().onPackageAdded(pkg1, Process.myUserHandle());
-        waitForLoaderAndTempMainThread();
-        cb1.verifyApps(TEST_PACKAGE, pkg1);
-        cb2.verifyApps(TEST_PACKAGE, pkg1);
-
-        // Install package 2
-        String pkg2 = "com.test.pkg2";
-        mModelHelper.installApp(pkg2);
-        mModelHelper.getModel().onPackageAdded(pkg2, Process.myUserHandle());
-        waitForLoaderAndTempMainThread();
-        cb1.verifyApps(TEST_PACKAGE, pkg1, pkg2);
-        cb2.verifyApps(TEST_PACKAGE, pkg1, pkg2);
-
-        // Uninstall package 2
-        mSpm.removePackage(pkg1);
-        mModelHelper.getModel().onPackageRemoved(pkg1, Process.myUserHandle());
-        waitForLoaderAndTempMainThread();
-        cb1.verifyApps(TEST_PACKAGE, pkg2);
-        cb2.verifyApps(TEST_PACKAGE, pkg2);
-
-        // Unregister a callback and verify updates no longer received
-        mModelHelper.getModel().removeCallbacks(cb2);
-        mSpm.removePackage(pkg2);
-        mModelHelper.getModel().onPackageRemoved(pkg2, Process.myUserHandle());
-        waitForLoaderAndTempMainThread();
-        cb1.verifyApps(TEST_PACKAGE);
-        cb2.verifyApps(TEST_PACKAGE, pkg2);
-    }
-
-    private void waitForLoaderAndTempMainThread() throws Exception {
-        Executors.MODEL_EXECUTOR.submit(() -> { }).get();
-        mTempMainExecutor.submit(() -> { }).get();
-    }
-
-    private void setupWorkspacePages(int pageCount) throws Exception {
-        // Create a layout with 3 pages
-        LauncherLayoutBuilder builder = new LauncherLayoutBuilder();
-        for (int i = 0; i < pageCount; i++) {
-            builder.atWorkspace(1, 1, i).putApp(TEST_PACKAGE, TEST_PACKAGE);
-        }
-        mModelHelper.setupDefaultLayoutProvider(builder);
-    }
-
-    private abstract static class MyCallbacks implements Callbacks {
-
-        final List<ItemInfo> mItems = new ArrayList<>();
-        int mPageToBindSync = 0;
-        int mPageBoundSync = PagedView.INVALID_PAGE;
-        ViewOnDrawExecutor mDeferredExecutor;
-        AppInfo[] mAppInfos;
-
-        MyCallbacks() { }
-
-        @Override
-        public void onPageBoundSynchronously(int page) {
-            mPageBoundSync = page;
-        }
-
-        @Override
-        public void executeOnNextDraw(ViewOnDrawExecutor executor) {
-            mDeferredExecutor = executor;
-        }
-
-        @Override
-        public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons) {
-            mItems.addAll(shortcuts);
-        }
-
-        @Override
-        public void bindAllApplications(AppInfo[] apps, int flags) {
-            mAppInfos = apps;
-        }
-
-        @Override
-        public int getPageToBindSynchronously() {
-            return mPageToBindSync;
-        }
-
-        public void reset() {
-            mItems.clear();
-            mPageBoundSync = PagedView.INVALID_PAGE;
-            mDeferredExecutor = null;
-            mAppInfos = null;
-        }
-
-        public void verifySynchronouslyBound(int totalItems) {
-            // Verify that the requested page is bound synchronously
-            assertEquals(mPageBoundSync, mPageToBindSync);
-            assertEquals(mItems.size(), 1);
-            assertEquals(mItems.get(0).screenId, mPageBoundSync);
-            assertNotNull(mDeferredExecutor);
-
-            // Verify that all other pages are bound properly
-            mDeferredExecutor.runAllTasks();
-            assertEquals(mItems.size(), totalItems);
-        }
-
-        public void verifyApps(String... apps) {
-            assertEquals(apps.length, mAppInfos.length);
-            assertEquals(Arrays.stream(mAppInfos)
-                    .map(ai -> ai.getTargetComponent().getPackageName())
-                    .collect(Collectors.toSet()),
-                    new HashSet<>(Arrays.asList(apps)));
-        }
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/secondarydisplay/SDWorkModeTest.java b/robolectric_tests/src/com/android/launcher3/secondarydisplay/SDWorkModeTest.java
deleted file mode 100644
index e3694ae..0000000
--- a/robolectric_tests/src/com/android/launcher3/secondarydisplay/SDWorkModeTest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2020 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.secondarydisplay;
-
-import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
-import static com.android.launcher3.util.LauncherUIHelper.doLayout;
-import static com.android.launcher3.util.Preconditions.assertNotNull;
-
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.os.UserManager;
-import android.provider.Settings;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.allapps.AllAppsPagedView;
-import com.android.launcher3.allapps.AllAppsRecyclerView;
-import com.android.launcher3.util.LauncherLayoutBuilder;
-import com.android.launcher3.util.LauncherModelHelper;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.LooperMode;
-import org.robolectric.annotation.LooperMode.Mode;
-import org.robolectric.shadow.api.Shadow;
-import org.robolectric.shadows.ShadowUserManager;
-
-/**
- * Tests for {@link SecondaryDisplayLauncher} with work profile
- */
-@RunWith(RobolectricTestRunner.class)
-@LooperMode(Mode.PAUSED)
-public class SDWorkModeTest {
-
-    private static final int SYSTEM_USER = 0;
-    private static final int FLAG_SYSTEM = 0x00000800;
-    private static final int WORK_PROFILE_ID = 10;
-    private static final int FLAG_PROFILE = 0x00001000;
-
-    private Context mTargetContext;
-    private InvariantDeviceProfile mIdp;
-    private LauncherModelHelper mModelHelper;
-
-    @Before
-    public void setup() throws Exception {
-        mModelHelper = new LauncherModelHelper();
-        mTargetContext = RuntimeEnvironment.application;
-        mIdp = InvariantDeviceProfile.INSTANCE.get(mTargetContext);
-        Settings.Global.putFloat(mTargetContext.getContentResolver(),
-                Settings.Global.WINDOW_ANIMATION_SCALE, 0);
-
-        mModelHelper.installApp(TEST_PACKAGE);
-    }
-
-    @Test
-    public void testAllAppsList_noWorkProfile() throws Exception {
-        SecondaryDisplayLauncher launcher = loadLauncher();
-        launcher.showAppDrawer(true);
-        doLayout(launcher);
-
-        verifyRecyclerViewCount(launcher.getAppsView().getActiveRecyclerView());
-    }
-
-    @Test
-    public void testAllAppsList_workProfile() throws Exception {
-        ShadowUserManager sum = Shadow.extract(mTargetContext.getSystemService(UserManager.class));
-        sum.addUser(SYSTEM_USER, "me", FLAG_SYSTEM);
-        sum.addProfile(SYSTEM_USER, WORK_PROFILE_ID, "work", FLAG_PROFILE);
-
-        SecondaryDisplayLauncher launcher = loadLauncher();
-        launcher.showAppDrawer(true);
-        doLayout(launcher);
-
-        AllAppsRecyclerView rv1 = launcher.getAppsView().getActiveRecyclerView();
-        verifyRecyclerViewCount(rv1);
-
-        assertNotNull(launcher.getAppsView().getWorkModeSwitch());
-        assertTrue(launcher.getAppsView().getRecyclerViewContainer() instanceof AllAppsPagedView);
-
-        AllAppsPagedView pagedView =
-                (AllAppsPagedView) launcher.getAppsView().getRecyclerViewContainer();
-        pagedView.snapToPageImmediately(1);
-        doLayout(launcher);
-
-        AllAppsRecyclerView rv2 = launcher.getAppsView().getActiveRecyclerView();
-        verifyRecyclerViewCount(rv2);
-        assertNotSame(rv1, rv2);
-    }
-
-    private SecondaryDisplayLauncher loadLauncher() throws Exception {
-        // Install 100 apps
-        for (int i = 0; i < 100; i++) {
-            mModelHelper.installApp(TEST_PACKAGE + i);
-        }
-        mModelHelper.setupDefaultLayoutProvider(new LauncherLayoutBuilder()).loadModelSync();
-        SecondaryDisplayLauncher launcher =
-                Robolectric.buildActivity(SecondaryDisplayLauncher.class).setup().get();
-        doLayout(launcher);
-        return launcher;
-    }
-
-    private void verifyRecyclerViewCount(AllAppsRecyclerView rv) {
-        int childCount = rv.getChildCount();
-        assertTrue(childCount > 0);
-        assertTrue(childCount < 100);
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowAppPredictionManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowAppPredictionManager.java
deleted file mode 100644
index ae051f7..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowAppPredictionManager.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2020 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.shadows;
-
-import static org.mockito.Mockito.mock;
-
-import android.app.prediction.AppPredictionContext;
-import android.app.prediction.AppPredictionManager;
-import android.app.prediction.AppPredictor;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-
-/**
- * Shadow for {@link AppPredictionManager} which create mock predictors
- */
-@Implements(value = AppPredictionManager.class)
-public class LShadowAppPredictionManager {
-
-    @Implementation
-    public AppPredictor createAppPredictionSession(AppPredictionContext predictionContext) {
-        return mock(AppPredictor.class);
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowAppWidgetManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowAppWidgetManager.java
deleted file mode 100644
index 696ffd0..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowAppWidgetManager.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.shadows;
-
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
-import android.os.Process;
-import android.os.UserHandle;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowAppWidgetManager;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * Extension of {@link ShadowAppWidgetManager} with missing shadow methods
- */
-@Implements(value = AppWidgetManager.class)
-public class LShadowAppWidgetManager extends ShadowAppWidgetManager {
-
-    @Override
-    protected List<AppWidgetProviderInfo> getInstalledProviders() {
-        return getInstalledProvidersForProfile(null);
-    }
-
-    @Implementation
-    public List<AppWidgetProviderInfo> getInstalledProvidersForProfile(UserHandle profile) {
-        UserHandle user = profile == null ? Process.myUserHandle() : profile;
-        return super.getInstalledProviders().stream().filter(
-                info -> user.equals(info.getProfile())).collect(Collectors.toList());
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowBackupManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowBackupManager.java
deleted file mode 100644
index eae0101..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowBackupManager.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2020 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.shadows;
-
-import android.app.backup.BackupManager;
-import android.os.UserHandle;
-import android.util.LongSparseArray;
-
-import androidx.annotation.Nullable;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowBackupManager;
-
-/**
- * Extension of {@link ShadowBackupManager} with missing shadow methods
- */
-@Implements(value = BackupManager.class)
-public class LShadowBackupManager extends ShadowBackupManager {
-
-    private LongSparseArray<UserHandle> mProfileMapping = new LongSparseArray<>();
-
-    public void addProfile(long userSerial, UserHandle userHandle) {
-        mProfileMapping.put(userSerial, userHandle);
-    }
-
-    @Implementation
-    @Nullable
-    public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) {
-        return mProfileMapping.get(ancestralSerialNumber);
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowDisplay.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowDisplay.java
deleted file mode 100644
index 3813fa1..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowDisplay.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2020 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.shadows;
-
-import static org.robolectric.shadow.api.Shadow.directlyOn;
-
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.view.Display;
-
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.RealObject;
-import org.robolectric.shadows.ShadowDisplay;
-
-/**
- * Extension of {@link ShadowDisplay} with missing shadow methods
- */
-@Implements(value = Display.class)
-public class LShadowDisplay extends ShadowDisplay {
-
-    private final Rect mInsets = new Rect();
-
-    @RealObject Display realObject;
-
-    /**
-     * Sets the insets for the display
-     */
-    public void setInsets(Rect insets) {
-        mInsets.set(insets);
-    }
-
-    @Override
-    protected void getCurrentSizeRange(Point outSmallestSize, Point outLargestSize) {
-        directlyOn(realObject, Display.class).getCurrentSizeRange(outSmallestSize, outLargestSize);
-        outSmallestSize.x -= mInsets.left + mInsets.right;
-        outLargestSize.x -= mInsets.left + mInsets.right;
-
-        outSmallestSize.y -= mInsets.top + mInsets.bottom;
-        outLargestSize.y -= mInsets.top + mInsets.bottom;
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java
deleted file mode 100644
index 6a6f0fb..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.shadows;
-
-import static org.robolectric.util.ReflectionHelpers.ClassParameter;
-import static org.robolectric.util.ReflectionHelpers.callConstructor;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.LauncherApps;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ShortcutInfo;
-import android.os.UserHandle;
-import android.util.ArraySet;
-
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.PackageUserKey;
-
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowLauncherApps;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.Executor;
-import java.util.stream.Collectors;
-
-/**
- * Extension of {@link ShadowLauncherApps} with missing shadow methods
- */
-@Implements(value = LauncherApps.class)
-public class LShadowLauncherApps extends ShadowLauncherApps {
-
-    public final ArraySet<PackageUserKey> disabledApps = new ArraySet<>();
-    public final ArraySet<ComponentKey> disabledActivities = new ArraySet<>();
-
-    @Implementation
-    @Override
-    protected List<ShortcutInfo> getShortcuts(LauncherApps.ShortcutQuery query, UserHandle user) {
-        try {
-            return super.getShortcuts(query, user);
-        } catch (UnsupportedOperationException e) {
-            return Collections.emptyList();
-        }
-    }
-
-    @Implementation
-    protected boolean isPackageEnabled(String packageName, UserHandle user) {
-        return !disabledApps.contains(new PackageUserKey(packageName, user));
-    }
-
-    @Implementation
-    protected boolean isActivityEnabled(ComponentName component, UserHandle user) {
-        return !disabledActivities.contains(new ComponentKey(component, user));
-    }
-
-    @Implementation
-    protected LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
-        ResolveInfo ri = RuntimeEnvironment.application.getPackageManager()
-                .resolveActivity(intent, 0);
-        return ri == null ? null : getLauncherActivityInfo(ri.activityInfo, user);
-    }
-
-    public LauncherActivityInfo getLauncherActivityInfo(
-            ActivityInfo activityInfo, UserHandle user) {
-        return callConstructor(LauncherActivityInfo.class,
-                ClassParameter.from(Context.class, RuntimeEnvironment.application),
-                ClassParameter.from(ActivityInfo.class, activityInfo),
-                ClassParameter.from(UserHandle.class, user));
-    }
-
-    @Implementation
-    public ApplicationInfo getApplicationInfo(String packageName, int flags, UserHandle user)
-            throws PackageManager.NameNotFoundException {
-        return RuntimeEnvironment.application.getPackageManager()
-                .getApplicationInfo(packageName, flags);
-    }
-
-    @Implementation
-    public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
-        Intent intent = new Intent(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_LAUNCHER)
-                .setPackage(packageName);
-        return RuntimeEnvironment.application.getPackageManager().queryIntentActivities(intent, 0)
-                .stream()
-                .map(ri -> getLauncherActivityInfo(ri.activityInfo, user))
-                .collect(Collectors.toList());
-    }
-
-    @Implementation
-    public boolean hasShortcutHostPermission() {
-        return true;
-    }
-
-    @Implementation
-    public List<PackageInstaller.SessionInfo> getAllPackageInstallerSessions() {
-        return RuntimeEnvironment.application.getPackageManager().getPackageInstaller()
-                .getAllSessions();
-    }
-
-    @Implementation
-    public void registerPackageInstallerSessionCallback(
-            Executor executor, PackageInstaller.SessionCallback callback) {
-    }
-
-    @Override
-    protected List<LauncherActivityInfo> getShortcutConfigActivityList(String packageName,
-            UserHandle user) {
-        Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setPackage(packageName);
-        return RuntimeEnvironment.application.getPackageManager().queryIntentActivities(intent, 0)
-                .stream()
-                .map(ri -> getLauncherActivityInfo(ri.activityInfo, user))
-                .collect(Collectors.toList());
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/ShadowDeviceFlag.java b/robolectric_tests/src/com/android/launcher3/shadows/ShadowDeviceFlag.java
deleted file mode 100644
index b58e4b7..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/ShadowDeviceFlag.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.shadows;
-
-import android.content.Context;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.uioverrides.DeviceFlag;
-import com.android.launcher3.util.LooperExecutor;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.RealObject;
-import org.robolectric.shadow.api.Shadow;
-
-/**
- * Shadow for {@link LooperExecutor} to provide reset functionality for static executors.
- */
-@Implements(value = DeviceFlag.class, isInAndroidSdk = false)
-public class ShadowDeviceFlag {
-
-    @RealObject private DeviceFlag mRealObject;
-    @Nullable private Boolean mValue;
-
-    /**
-     * Mock change listener as it uses internal system classes not available to robolectric
-     */
-    @Implementation
-    protected void addChangeListener(Context context, Runnable r) { }
-
-    @Implementation
-    protected static boolean getDeviceValue(String key, boolean defaultValue) {
-        return defaultValue;
-    }
-
-    @Implementation
-    public boolean get() {
-        if (mValue != null) {
-            return mValue;
-        }
-        return Shadow.directlyOn(mRealObject, DeviceFlag.class, "get");
-    }
-
-    public void setValue(boolean value) {
-        mValue = new Boolean(value);
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/ShadowLooperExecutor.java b/robolectric_tests/src/com/android/launcher3/shadows/ShadowLooperExecutor.java
deleted file mode 100644
index 57eda7e..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/ShadowLooperExecutor.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.shadows;
-
-import static com.android.launcher3.util.Executors.createAndStartNewLooper;
-
-import static org.robolectric.shadow.api.Shadow.directlyOn;
-import static org.robolectric.util.ReflectionHelpers.setField;
-
-import android.os.Handler;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.util.LooperExecutor;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.RealObject;
-
-/**
- * Shadow for {@link LooperExecutor} to provide reset functionality for static executors.
- */
-@Implements(value = LooperExecutor.class, isInAndroidSdk = false)
-public class ShadowLooperExecutor {
-
-    @RealObject private LooperExecutor mRealExecutor;
-
-    private Handler mOverriddenHandler;
-
-    @Implementation
-    protected Handler getHandler() {
-        if (mOverriddenHandler != null) {
-            return mOverriddenHandler;
-        }
-        Handler handler = directlyOn(mRealExecutor, LooperExecutor.class, "getHandler");
-        Thread thread = handler.getLooper().getThread();
-        if (!thread.isAlive()) {
-            // Robolectric destroys all loopers at the end of every test. Since Launcher maintains
-            // some static threads, they need to be reinitialized in case they were destroyed.
-            setField(mRealExecutor, "mHandler",
-                    new Handler(createAndStartNewLooper(thread.getName())));
-        }
-        return directlyOn(mRealExecutor, LooperExecutor.class, "getHandler");
-    }
-
-    public void setHandler(@Nullable Handler handler) {
-        mOverriddenHandler = handler;
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/ShadowMainThreadInitializedObject.java b/robolectric_tests/src/com/android/launcher3/shadows/ShadowMainThreadInitializedObject.java
deleted file mode 100644
index 6e2ccf8..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/ShadowMainThreadInitializedObject.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.shadows;
-
-import static org.robolectric.shadow.api.Shadow.invokeConstructor;
-import static org.robolectric.util.ReflectionHelpers.ClassParameter.from;
-
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.MainThreadInitializedObject.ObjectProvider;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.RealObject;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Set;
-import java.util.WeakHashMap;
-
-/**
- * Shadow for {@link MainThreadInitializedObject} to provide reset functionality for static sObjects
- */
-@Implements(value = MainThreadInitializedObject.class, isInAndroidSdk = false)
-public class ShadowMainThreadInitializedObject {
-
-    // Keep reference to all created MainThreadInitializedObject so they can be cleared after test
-    private static Set<MainThreadInitializedObject> sObjects =
-            Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>()));
-
-    @RealObject private MainThreadInitializedObject mRealObject;
-
-    @Implementation
-    protected void __constructor__(ObjectProvider provider) {
-        invokeConstructor(MainThreadInitializedObject.class, mRealObject,
-                from(ObjectProvider.class, provider));
-        sObjects.add(mRealObject);
-    }
-
-    /**
-     * Resets all the initialized sObjects to be null
-     */
-    public static void resetInitializedObjects() {
-        for (MainThreadInitializedObject object : new ArrayList<>(sObjects)) {
-            object.initializeForTesting(null);
-        }
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/ShadowOverrides.java b/robolectric_tests/src/com/android/launcher3/shadows/ShadowOverrides.java
deleted file mode 100644
index 131f691..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/ShadowOverrides.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2020 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.shadows;
-
-import android.content.Context;
-
-import com.android.launcher3.util.MainThreadInitializedObject.ObjectProvider;
-import com.android.launcher3.util.ResourceBasedOverride;
-import com.android.launcher3.util.ResourceBasedOverride.Overrides;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadow.api.Shadow;
-import org.robolectric.util.ReflectionHelpers.ClassParameter;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Shadow for {@link Overrides} to provide custom overrides for test
- */
-@Implements(value = Overrides.class, isInAndroidSdk = false)
-public class ShadowOverrides {
-
-    private static Map<Class, ObjectProvider> sProviderMap = new HashMap<>();
-
-    @Implementation
-    public static <T extends ResourceBasedOverride> T getObject(
-            Class<T> clazz, Context context, int resId) {
-        ObjectProvider<T> provider = sProviderMap.get(clazz);
-        if (provider != null) {
-            return provider.get(context);
-        }
-        return Shadow.directlyOn(Overrides.class, "getObject",
-                ClassParameter.from(Class.class, clazz),
-                ClassParameter.from(Context.class, context),
-                ClassParameter.from(int.class, resId));
-    }
-
-    public static <T> void setProvider(Class<T> clazz, ObjectProvider<T> provider) {
-        sProviderMap.put(clazz, provider);
-    }
-
-    public static void clearProvider() {
-        sProviderMap.clear();
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/ShadowSurfaceTransactionApplier.java b/robolectric_tests/src/com/android/launcher3/shadows/ShadowSurfaceTransactionApplier.java
deleted file mode 100644
index a9f2f27..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/ShadowSurfaceTransactionApplier.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2020 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.shadows;
-
-import static org.robolectric.shadow.api.Shadow.invokeConstructor;
-import static org.robolectric.util.ReflectionHelpers.ClassParameter.from;
-
-import android.view.View;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.RealObject;
-
-/**
- * Shadow for SurfaceTransactionApplier to override default functionality
- */
-@Implements(className = "com.android.quickstep.util.SurfaceTransactionApplier",
-        isInAndroidSdk = false)
-public class ShadowSurfaceTransactionApplier {
-
-    @RealObject
-    private Object mRealObject;
-
-    @Implementation
-    protected void __constructor__(View view) {
-        invokeConstructor(mRealObject.getClass(), mRealObject, from(View.class, null));
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/testing/TestActivity.java b/robolectric_tests/src/com/android/launcher3/testing/TestActivity.java
deleted file mode 100644
index 17d0ac1..0000000
--- a/robolectric_tests/src/com/android/launcher3/testing/TestActivity.java
+++ /dev/null
@@ -1,46 +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.testing;
-
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.views.ActivityContext;
-import com.android.launcher3.views.BaseDragLayer;
-
-/** An empty activity for {@link android.app.Fragment}s, {@link android.view.View}s testing. */
-public class TestActivity extends BaseActivity implements ActivityContext {
-
-    private DeviceProfile mDeviceProfile;
-
-    @Override
-    public BaseDragLayer getDragLayer() {
-        return new BaseDragLayer(this, /* attrs= */ null, /* alphaChannelCount= */ 1) {
-            @Override
-            public void recreateControllers() {
-                // Do nothing.
-            }
-        };
-    }
-
-    @Override
-    public DeviceProfile getDeviceProfile() {
-        return mDeviceProfile;
-    }
-
-    public void setDeviceProfile(DeviceProfile deviceProfile) {
-        mDeviceProfile = deviceProfile;
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
deleted file mode 100644
index ea75548..0000000
--- a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2020 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.ui;
-
-import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
-import static com.android.launcher3.util.LauncherUIHelper.buildAndBindLauncher;
-import static com.android.launcher3.util.LauncherUIHelper.doLayout;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-
-import android.content.Context;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.MotionEvent.PointerProperties;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.folder.Folder;
-import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.folder.FolderPagedView;
-import com.android.launcher3.util.LauncherLayoutBuilder;
-import com.android.launcher3.util.LauncherLayoutBuilder.FolderBuilder;
-import com.android.launcher3.util.LauncherModelHelper;
-import com.android.launcher3.widget.picker.WidgetsFullSheet;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.LooperMode;
-import org.robolectric.annotation.LooperMode.Mode;
-import org.robolectric.shadows.ShadowLooper;
-
-/**
- * Tests scroll behavior at various Launcher UI components
- */
-@RunWith(RobolectricTestRunner.class)
-@LooperMode(Mode.PAUSED)
-@org.junit.Ignore
-public class LauncherUIScrollTest {
-
-    private Context mTargetContext;
-    private InvariantDeviceProfile mIdp;
-    private LauncherModelHelper mModelHelper;
-
-    private LauncherLayoutBuilder mLayoutBuilder;
-
-    @Before
-    public void setup() throws Exception {
-        mModelHelper = new LauncherModelHelper();
-        mTargetContext = RuntimeEnvironment.application;
-        mIdp = InvariantDeviceProfile.INSTANCE.get(mTargetContext);
-
-        Settings.Global.putFloat(mTargetContext.getContentResolver(),
-                Settings.Global.WINDOW_ANIMATION_SCALE, 0);
-
-        mModelHelper.installApp(TEST_PACKAGE);
-        // LayoutBuilder with 3 workspace pages
-        mLayoutBuilder = new LauncherLayoutBuilder()
-                .atWorkspace(0,  mIdp.numRows - 1, 0).putApp(TEST_PACKAGE, TEST_PACKAGE)
-                .atWorkspace(0,  mIdp.numRows - 1, 1).putApp(TEST_PACKAGE, TEST_PACKAGE)
-                .atWorkspace(0,  mIdp.numRows - 1, 2).putApp(TEST_PACKAGE, TEST_PACKAGE);
-    }
-
-    @Test
-    public void testWorkspacePagesBound() throws Exception {
-        // Verify that the workspace if bound synchronously
-        Launcher launcher = loadLauncher();
-        assertEquals(3, launcher.getWorkspace().getPageCount());
-        assertEquals(0, launcher.getWorkspace().getCurrentPage());
-
-        launcher.dispatchGenericMotionEvent(createScrollEvent(-1));
-        assertNotEquals("Workspace was not scrolled",
-                0, launcher.getWorkspace().getNextPage());
-    }
-
-    @Test
-    public void testAllAppsScroll() throws Exception {
-        // Install 100 apps
-        for (int i = 0; i < 100; i++) {
-            mModelHelper.installApp(TEST_PACKAGE + i);
-        }
-
-        // Bind and open all-apps
-        Launcher launcher = loadLauncher();
-        launcher.getStateManager().goToState(LauncherState.ALL_APPS, false);
-        doLayout(launcher);
-
-        int currentScroll = launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
-        launcher.dispatchGenericMotionEvent(createScrollEvent(-1));
-        int newScroll = launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
-
-        assertNotEquals("All Apps was not scrolled", currentScroll, newScroll);
-        assertEquals("Workspace was scrolled", 0, launcher.getWorkspace().getNextPage());
-    }
-
-    @Test
-    public void testWidgetsListScroll() throws Exception {
-        // Install 100 widgets
-        for (int i = 0; i < 100; i++) {
-            mModelHelper.installCustomShortcut(TEST_PACKAGE + i, "shortcutProvider");
-        }
-
-        // Bind and open widgets
-        Launcher launcher = loadLauncher();
-        WidgetsFullSheet widgets = WidgetsFullSheet.show(launcher, false);
-        doLayout(launcher);
-
-        int currentScroll = widgets.getRecyclerView().getCurrentScrollY();
-        launcher.dispatchGenericMotionEvent(createScrollEvent(-1));
-        int newScroll = widgets.getRecyclerView().getCurrentScrollY();
-        assertNotEquals("Widgets was not scrolled", currentScroll, newScroll);
-        assertEquals("Workspace was scrolled", 0, launcher.getWorkspace().getNextPage());
-    }
-
-    @Test
-    public void testFolderPageScroll() throws Exception {
-        // Add a folder with multiple icons
-        FolderBuilder fb = mLayoutBuilder.atWorkspace(mIdp.numColumns / 2, mIdp.numRows / 2, 0)
-                .putFolder(0);
-        for (int i = 0; i < 100; i++) {
-            fb.addApp(TEST_PACKAGE, TEST_PACKAGE);
-        }
-
-        // Bind and open folder
-        Launcher launcher = loadLauncher();
-        doLayout(launcher);
-        launcher.getWorkspace().getFirstMatch((i, v) -> v instanceof FolderIcon).performClick();
-        ShadowLooper.idleMainLooper();
-        doLayout(launcher);
-        FolderPagedView folderPages = Folder.getOpen(launcher).getContent();
-
-        assertEquals(0, folderPages.getNextPage());
-        launcher.dispatchGenericMotionEvent(createScrollEvent(-1));
-        assertNotEquals("Folder page was not scrolled", 0, folderPages.getNextPage());
-        assertEquals("Workspace was scrolled", 0, launcher.getWorkspace().getNextPage());
-    }
-
-    private Launcher loadLauncher() throws Exception {
-        mModelHelper.setupDefaultLayoutProvider(mLayoutBuilder).loadModelSync();
-        return buildAndBindLauncher();
-    }
-
-    private static MotionEvent createScrollEvent(int scroll) {
-        DeviceProfile dp = InvariantDeviceProfile.INSTANCE
-                .get(RuntimeEnvironment.application).supportedProfiles.get(0);
-
-        final PointerProperties[] pointerProperties = new PointerProperties[1];
-        pointerProperties[0] = new PointerProperties();
-        pointerProperties[0].id = 0;
-        final MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[1];
-        coords[0] = new MotionEvent.PointerCoords();
-        coords[0].setAxisValue(MotionEvent.AXIS_VSCROLL, scroll);
-        coords[0].x = dp.widthPx / 2;
-        coords[0].y = dp.heightPx / 2;
-
-        final long time = SystemClock.uptimeMillis();
-        return MotionEvent.obtain(time, time, MotionEvent.ACTION_SCROLL, 1,
-                pointerProperties, coords, 0, 0, 1.0f, 1.0f, 0, 0,
-                InputDevice.SOURCE_CLASS_POINTER, 0);
-    }
-
-}
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java b/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java
deleted file mode 100644
index efac150..0000000
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2020 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 org.mockito.Mockito.mock;
-
-import android.app.Application;
-
-import com.android.launcher3.shadows.ShadowMainThreadInitializedObject;
-import com.android.launcher3.shadows.ShadowOverrides;
-import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
-
-import org.robolectric.TestLifecycleApplication;
-import org.robolectric.shadows.ShadowLog;
-
-import java.lang.reflect.Method;
-
-public class LauncherTestApplication extends Application implements TestLifecycleApplication {
-
-    @Override
-    public void beforeTest(Method method) {
-        ShadowLog.stream = System.out;
-
-        // Disable plugins
-        PluginManagerWrapper.INSTANCE.initializeForTesting(mock(PluginManagerWrapper.class));
-    }
-
-    @Override
-    public void prepareTest(Object test) { }
-
-    @Override
-    public void afterTest(Method method) {
-        ShadowLog.stream = null;
-        ShadowMainThreadInitializedObject.resetInitializedObjects();
-        ShadowOverrides.clearProvider();
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherUIHelper.java b/robolectric_tests/src/com/android/launcher3/util/LauncherUIHelper.java
deleted file mode 100644
index fdddab4..0000000
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherUIHelper.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2020 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 android.view.View.MeasureSpec.EXACTLY;
-import static android.view.View.MeasureSpec.makeMeasureSpec;
-
-import static com.android.launcher3.Utilities.createHomeIntent;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ResolveInfo;
-import android.graphics.Point;
-import android.view.View;
-import android.view.WindowManager;
-
-import com.android.launcher3.Launcher;
-
-import org.robolectric.Robolectric;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.android.controller.ActivityController;
-import org.robolectric.shadows.ShadowLooper;
-import org.robolectric.util.ReflectionHelpers;
-
-import java.util.List;
-
-/**
- * Utility class to help manage Launcher UI and related objects for test.
- */
-public class LauncherUIHelper {
-
-    /**
-     * Returns the class name for the Launcher activity as defined in the manifest
-     */
-    public static String getLauncherClassName() {
-        Context context = RuntimeEnvironment.application;
-        Intent homeIntent = createHomeIntent().setPackage(context.getPackageName());
-
-        List<ResolveInfo> launchers = context.getPackageManager()
-                .queryIntentActivities(homeIntent, 0);
-        if (launchers.size() != 1) {
-            return null;
-        }
-        return launchers.get(0).activityInfo.name;
-    }
-
-    /**
-     * Returns an activity controller for Launcher activity defined in the manifest
-     */
-    public static <T extends Launcher> ActivityController<T> buildLauncher() {
-        try {
-            Class<T> tClass = (Class<T>) Class.forName(getLauncherClassName());
-            return Robolectric.buildActivity(tClass);
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /**
-     * Creates and binds a Launcher activity defined in the manifest.
-     * Note that the model must be bound before calling this
-     */
-    public static <T extends Launcher> T buildAndBindLauncher() {
-        ActivityController<T> controller = buildLauncher();
-
-        T launcher = controller.setup().get();
-        doLayout(launcher);
-        ViewOnDrawExecutor executor = ReflectionHelpers.getField(launcher, "mPendingExecutor");
-        if (executor != null) {
-            executor.runAllTasks();
-        }
-        return launcher;
-    }
-
-    /**
-     * Performs a measure and layout pass for the given activity
-     */
-    public static void doLayout(Activity activity) {
-        Point size = new Point();
-        RuntimeEnvironment.application.getSystemService(WindowManager.class)
-                .getDefaultDisplay().getSize(size);
-        View view = activity.getWindow().getDecorView();
-        view.measure(makeMeasureSpec(size.x, EXACTLY), makeMeasureSpec(size.y, EXACTLY));
-        view.layout(0, 0, size.x, size.y);
-        ShadowLooper.idleMainLooper();
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/CachingWidgetPreviewLoaderTest.java b/robolectric_tests/src/com/android/launcher3/widget/CachingWidgetPreviewLoaderTest.java
deleted file mode 100644
index 1090d1e..0000000
--- a/robolectric_tests/src/com/android/launcher3/widget/CachingWidgetPreviewLoaderTest.java
+++ /dev/null
@@ -1,409 +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.widget;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.graphics.Bitmap;
-import android.os.CancellationSignal;
-import android.os.UserHandle;
-import android.util.Size;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.testing.TestActivity;
-import com.android.launcher3.widget.WidgetPreviewLoader.WidgetPreviewLoadedCallback;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-
-import java.util.Arrays;
-import java.util.Collections;
-
-@RunWith(RobolectricTestRunner.class)
-public class CachingWidgetPreviewLoaderTest {
-    private final Size SIZE_10_10 = new Size(10, 10);
-    private final Size SIZE_20_20 = new Size(20, 20);
-    private static final String TEST_PACKAGE = "com.example.test";
-    private final ComponentName TEST_PROVIDER =
-            new ComponentName(TEST_PACKAGE, ".WidgetProvider");
-    private final ComponentName TEST_PROVIDER2 =
-            new ComponentName(TEST_PACKAGE, ".WidgetProvider2");
-    private final Bitmap BITMAP = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
-    private final Bitmap BITMAP2 = Bitmap.createBitmap(20, 20, Bitmap.Config.ARGB_8888);
-
-
-    @Mock private CancellationSignal mCancellationSignal;
-    @Mock private WidgetPreviewLoader mDelegate;
-    @Mock private IconCache mIconCache;
-    @Mock private DeviceProfile mDeviceProfile;
-    @Mock private LauncherAppWidgetProviderInfo mProviderInfo;
-    @Mock private LauncherAppWidgetProviderInfo mProviderInfo2;
-    @Mock private WidgetPreviewLoadedCallback mPreviewLoadedCallback;
-    @Mock private WidgetPreviewLoadedCallback mPreviewLoadedCallback2;
-    @Captor private ArgumentCaptor<WidgetPreviewLoadedCallback> mCallbackCaptor;
-
-    private TestActivity mTestActivity;
-    private CachingWidgetPreviewLoader mLoader;
-    private WidgetItem mWidgetItem;
-    private WidgetItem mWidgetItem2;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mLoader = new CachingWidgetPreviewLoader(mDelegate);
-
-        mTestActivity = Robolectric.buildActivity(TestActivity.class).setup().get();
-        mTestActivity.setDeviceProfile(mDeviceProfile);
-
-        when(mDelegate.loadPreview(any(), any(), any(), any())).thenReturn(mCancellationSignal);
-
-        mProviderInfo.provider = TEST_PROVIDER;
-        when(mProviderInfo.getProfile()).thenReturn(new UserHandle(0));
-
-        mProviderInfo2.provider = TEST_PROVIDER2;
-        when(mProviderInfo2.getProfile()).thenReturn(new UserHandle(0));
-
-        InvariantDeviceProfile testProfile = new InvariantDeviceProfile();
-        testProfile.numRows = 5;
-        testProfile.numColumns = 5;
-
-        mWidgetItem = new WidgetItem(mProviderInfo, testProfile, mIconCache);
-        mWidgetItem2 = new WidgetItem(mProviderInfo2, testProfile, mIconCache);
-    }
-
-    @Test
-    public void getPreview_notInCache_shouldReturnNull() {
-        assertThat(mLoader.getPreview(mWidgetItem, SIZE_10_10)).isNull();
-    }
-
-    @Test
-    public void getPreview_notInCache_shouldNotCallDelegate() {
-        mLoader.getPreview(mWidgetItem, SIZE_10_10);
-
-        verifyZeroInteractions(mDelegate);
-    }
-
-    @Test
-    public void getPreview_inCache_shouldReturnCachedBitmap() {
-        loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
-
-        assertThat(mLoader.getPreview(mWidgetItem, SIZE_10_10)).isEqualTo(BITMAP);
-    }
-
-    @Test
-    public void getPreview_otherSizeInCache_shouldReturnNull() {
-        loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
-
-        assertThat(mLoader.getPreview(mWidgetItem, SIZE_20_20)).isNull();
-    }
-
-    @Test
-    public void getPreview_otherItemInCache_shouldReturnNull() {
-        loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
-
-        assertThat(mLoader.getPreview(mWidgetItem2, SIZE_10_10)).isNull();
-    }
-
-    @Test
-    public void getPreview_shouldStoreMultipleSizesPerItem() {
-        loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
-        loadPreviewIntoCache(mWidgetItem, SIZE_20_20, BITMAP2);
-
-        assertThat(mLoader.getPreview(mWidgetItem, SIZE_10_10)).isEqualTo(BITMAP);
-        assertThat(mLoader.getPreview(mWidgetItem, SIZE_20_20)).isEqualTo(BITMAP2);
-    }
-
-    @Test
-    public void loadPreview_notInCache_shouldStartLoading() {
-        mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
-
-        verify(mDelegate).loadPreview(eq(mTestActivity), eq(mWidgetItem), eq(SIZE_10_10), any());
-        verifyZeroInteractions(mPreviewLoadedCallback);
-    }
-
-    @Test
-    public void loadPreview_thenLoaded_shouldCallBack() {
-        mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
-        verify(mDelegate).loadPreview(any(), any(), any(), mCallbackCaptor.capture());
-        WidgetPreviewLoadedCallback loaderCallback = mCallbackCaptor.getValue();
-
-        loaderCallback.onPreviewLoaded(BITMAP);
-
-        verify(mPreviewLoadedCallback).onPreviewLoaded(BITMAP);
-    }
-
-    @Test
-    public void loadPreview_thenCancelled_shouldCancelDelegateRequest() {
-        CancellationSignal cancellationSignal =
-                mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
-
-        cancellationSignal.cancel();
-
-        verify(mCancellationSignal).cancel();
-        verifyZeroInteractions(mPreviewLoadedCallback);
-        assertThat(mLoader.getPreview(mWidgetItem, SIZE_10_10)).isNull();
-    }
-
-    @Test
-    public void loadPreview_thenCancelled_otherCallListening_shouldNotCancelDelegateRequest() {
-        CancellationSignal cancellationSignal1 =
-                mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
-        mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback2);
-
-        cancellationSignal1.cancel();
-
-        verifyZeroInteractions(mCancellationSignal);
-    }
-
-    @Test
-    public void loadPreview_thenCancelled_otherCallListening_loaded_shouldCallBackToNonCancelled() {
-        CancellationSignal cancellationSignal1 =
-                mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
-        mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback2);
-        verify(mDelegate).loadPreview(any(), any(), any(), mCallbackCaptor.capture());
-        WidgetPreviewLoadedCallback loaderCallback = mCallbackCaptor.getValue();
-
-        cancellationSignal1.cancel();
-        loaderCallback.onPreviewLoaded(BITMAP);
-
-        verifyZeroInteractions(mPreviewLoadedCallback);
-        verify(mPreviewLoadedCallback2).onPreviewLoaded(BITMAP);
-        assertThat(mLoader.getPreview(mWidgetItem, SIZE_10_10)).isEqualTo(BITMAP);
-    }
-
-    @Test
-    public void loadPreview_thenCancelled_bothCallsCancelled_shouldCancelDelegateRequest() {
-        CancellationSignal cancellationSignal1 =
-                mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
-        CancellationSignal cancellationSignal2 =
-                mLoader.loadPreview(
-                        mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback2);
-
-        cancellationSignal1.cancel();
-        cancellationSignal2.cancel();
-
-        verify(mCancellationSignal).cancel();
-        verifyZeroInteractions(mPreviewLoadedCallback);
-        verifyZeroInteractions(mPreviewLoadedCallback2);
-        assertThat(mLoader.getPreview(mWidgetItem, SIZE_10_10)).isNull();
-    }
-
-    @Test
-    public void loadPreview_multipleCallbacks_shouldOnlyCallDelegateOnce() {
-        mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
-        mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback2);
-
-        verify(mDelegate).loadPreview(any(), any(), any(), any());
-    }
-
-    @Test
-    public void loadPreview_multipleCallbacks_shouldForwardResultToEachCallback() {
-        mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
-        mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback2);
-
-        verify(mDelegate).loadPreview(any(), any(), any(), mCallbackCaptor.capture());
-        WidgetPreviewLoadedCallback loaderCallback = mCallbackCaptor.getValue();
-
-        loaderCallback.onPreviewLoaded(BITMAP);
-
-        verify(mPreviewLoadedCallback).onPreviewLoaded(BITMAP);
-        verify(mPreviewLoadedCallback2).onPreviewLoaded(BITMAP);
-    }
-
-    @Test
-    public void loadPreview_inCache_shouldCallBackImmediately() {
-        loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
-        reset(mDelegate);
-
-        mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
-
-        verify(mPreviewLoadedCallback).onPreviewLoaded(BITMAP);
-        verifyZeroInteractions(mDelegate);
-    }
-
-    @Test
-    public void loadPreview_thenLoaded_thenCancelled_shouldNotRemovePreviewFromCache() {
-        CancellationSignal cancellationSignal =
-                mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
-        verify(mDelegate).loadPreview(any(), any(), any(), mCallbackCaptor.capture());
-        WidgetPreviewLoadedCallback loaderCallback = mCallbackCaptor.getValue();
-        loaderCallback.onPreviewLoaded(BITMAP);
-
-        cancellationSignal.cancel();
-
-        assertThat(mLoader.getPreview(mWidgetItem, SIZE_10_10)).isEqualTo(BITMAP);
-    }
-
-    @Test
-    public void isPreviewLoaded_notLoaded_shouldReturnFalse() {
-        assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
-    }
-
-    @Test
-    public void isPreviewLoaded_otherSizeLoaded_shouldReturnFalse() {
-        loadPreviewIntoCache(mWidgetItem, SIZE_20_20, BITMAP);
-
-        assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
-    }
-
-    @Test
-    public void isPreviewLoaded_otherItemLoaded_shouldReturnFalse() {
-        loadPreviewIntoCache(mWidgetItem2, SIZE_10_10, BITMAP);
-
-        assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
-    }
-
-    @Test
-    public void isPreviewLoaded_loaded_shouldReturnTrue() {
-        loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
-
-        assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isTrue();
-    }
-
-    @Test
-    public void clearPreviews_notInCache_shouldBeNoOp() {
-        mLoader.clearPreviews(Collections.singletonList(mWidgetItem));
-
-        assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
-    }
-
-    @Test
-    public void clearPreviews_inCache_shouldRemovePreview() {
-        loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
-
-        mLoader.clearPreviews(Collections.singletonList(mWidgetItem));
-
-        assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
-    }
-
-    @Test
-    public void clearPreviews_inCache_multipleSizes_shouldRemoveAllSizes() {
-        loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
-        loadPreviewIntoCache(mWidgetItem, SIZE_20_20, BITMAP);
-
-        mLoader.clearPreviews(Collections.singletonList(mWidgetItem));
-
-        assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
-        assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_20_20)).isFalse();
-    }
-
-    @Test
-    public void clearPreviews_inCache_otherItems_shouldOnlyRemoveSpecifiedItems() {
-        loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
-        loadPreviewIntoCache(mWidgetItem2, SIZE_10_10, BITMAP);
-
-        mLoader.clearPreviews(Collections.singletonList(mWidgetItem));
-
-        assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
-        assertThat(mLoader.isPreviewLoaded(mWidgetItem2, SIZE_10_10)).isTrue();
-    }
-
-    @Test
-    public void clearPreviews_inCache_otherItems_shouldRemoveAllSpecifiedItems() {
-        loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
-        loadPreviewIntoCache(mWidgetItem2, SIZE_10_10, BITMAP);
-
-        mLoader.clearPreviews(Arrays.asList(mWidgetItem, mWidgetItem2));
-
-        assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
-        assertThat(mLoader.isPreviewLoaded(mWidgetItem2, SIZE_10_10)).isFalse();
-    }
-
-    @Test
-    public void clearPreviews_loading_shouldCancelLoad() {
-        mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
-
-        mLoader.clearPreviews(Collections.singletonList(mWidgetItem));
-
-        verify(mCancellationSignal).cancel();
-    }
-
-    @Test
-    public void clearAll_cacheEmpty_shouldBeNoOp() {
-        mLoader.clearAll();
-
-        assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
-    }
-
-    @Test
-    public void clearAll_inCache_shouldRemovePreview() {
-        loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
-
-        mLoader.clearAll();
-
-        assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
-    }
-
-    @Test
-    public void clearAll_inCache_multipleSizes_shouldRemoveAllSizes() {
-        loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
-        loadPreviewIntoCache(mWidgetItem, SIZE_20_20, BITMAP);
-
-        mLoader.clearAll();
-
-        assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
-        assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_20_20)).isFalse();
-    }
-
-    @Test
-    public void clearAll_inCache_multipleItems_shouldRemoveAll() {
-        loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
-        loadPreviewIntoCache(mWidgetItem, SIZE_20_20, BITMAP);
-        loadPreviewIntoCache(mWidgetItem2, SIZE_20_20, BITMAP);
-
-        mLoader.clearAll();
-
-        assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
-        assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_20_20)).isFalse();
-        assertThat(mLoader.isPreviewLoaded(mWidgetItem2, SIZE_20_20)).isFalse();
-    }
-
-    @Test
-    public void clearAll_loading_shouldCancelLoad() {
-        mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
-
-        mLoader.clearAll();
-
-        verify(mCancellationSignal).cancel();
-    }
-
-    private void loadPreviewIntoCache(WidgetItem widgetItem, Size size, Bitmap bitmap) {
-        reset(mDelegate);
-        mLoader.loadPreview(mTestActivity, widgetItem, size, ignored -> {});
-        verify(mDelegate).loadPreview(any(), any(), any(), mCallbackCaptor.capture());
-        WidgetPreviewLoadedCallback loaderCallback = mCallbackCaptor.getValue();
-
-        loaderCallback.onPreviewLoaded(bitmap);
-    }
-}
diff --git a/robolectric_tests/unstaged/SettingsCacheTest.java b/robolectric_tests/unstaged/SettingsCacheTest.java
deleted file mode 100644
index fbf4c63..0000000
--- a/robolectric_tests/unstaged/SettingsCacheTest.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.launcher3.util;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.net.Uri;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-import java.util.Collections;
-
-@RunWith(RobolectricTestRunner.class)
-public class SettingsCacheTest {
-
-    public static final Uri KEY_SYSTEM_URI_TEST1 = Uri.parse("content://settings/system/test1");
-    public static final Uri KEY_SYSTEM_URI_TEST2 = Uri.parse("content://settings/system/test2");;
-
-    private SettingsCache.OnChangeListener mChangeListener;
-    private SettingsCache mSettingsCache;
-
-    @Before
-    public void setup() {
-        mChangeListener = mock(SettingsCache.OnChangeListener.class);
-        Context targetContext = RuntimeEnvironment.application;
-        mSettingsCache = SettingsCache.INSTANCE.get(targetContext);
-        mSettingsCache.register(KEY_SYSTEM_URI_TEST1, mChangeListener);
-    }
-
-    @Test
-    public void listenerCalledOnChange() {
-        mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST1);
-        verify(mChangeListener, times(1)).onSettingsChanged(true);
-    }
-
-    @Test
-    public void getValueRespectsDefaultValue() {
-        // Case of key not found
-        boolean val = mSettingsCache.getValue(KEY_SYSTEM_URI_TEST1, 0);
-        assertFalse(val);
-    }
-
-    @Test
-    public void getValueHitsCache() {
-        mSettingsCache.setKeyCache(Collections.singletonMap(KEY_SYSTEM_URI_TEST1, true));
-        boolean val = mSettingsCache.getValue(KEY_SYSTEM_URI_TEST1, 0);
-        assertTrue(val);
-    }
-
-    @Test
-    public void getValueUpdatedCache() {
-        // First ensure there's nothing in cache
-        boolean val = mSettingsCache.getValue(KEY_SYSTEM_URI_TEST1, 0);
-        assertFalse(val);
-
-        mSettingsCache.setKeyCache(Collections.singletonMap(KEY_SYSTEM_URI_TEST1, true));
-        val = mSettingsCache.getValue(KEY_SYSTEM_URI_TEST1, 0);
-        assertTrue(val);
-    }
-
-    @Test
-    public void multipleListenersSingleKey() {
-        SettingsCache.OnChangeListener secondListener = mock(SettingsCache.OnChangeListener.class);
-        mSettingsCache.register(KEY_SYSTEM_URI_TEST1, secondListener);
-
-        mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST1);
-        verify(mChangeListener, times(1)).onSettingsChanged(true);
-        verify(secondListener, times(1)).onSettingsChanged(true);
-    }
-
-    @Test
-    public void singleListenerMultipleKeys() {
-        SettingsCache.OnChangeListener secondListener = mock(SettingsCache.OnChangeListener.class);
-        mSettingsCache.register(KEY_SYSTEM_URI_TEST2, secondListener);
-
-        mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST1);
-        mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST2);
-        verify(mChangeListener, times(1)).onSettingsChanged(true);
-        verify(secondListener, times(1)).onSettingsChanged(true);
-    }
-
-    @Test
-    public void sameListenerMultipleKeys() {
-        SettingsCache.OnChangeListener secondListener = mock(SettingsCache.OnChangeListener.class);
-        mSettingsCache.register(KEY_SYSTEM_URI_TEST2, mChangeListener);
-
-        mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST1);
-        mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST2);
-        verify(mChangeListener, times(2)).onSettingsChanged(true);
-        verify(secondListener, times(0)).onSettingsChanged(true);
-    }
-}
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 4979b40..e3cfb59 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -64,7 +64,8 @@
             TYPE_OPTIONS_POPUP,
             TYPE_ICON_SURFACE,
             TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP,
-            TYPE_WIDGETS_EDUCATION_DIALOG
+            TYPE_WIDGETS_EDUCATION_DIALOG,
+            TYPE_TASKBAR_EDUCATION_DIALOG
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface FloatingViewType {}
@@ -87,18 +88,20 @@
 
     public static final int TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP = 1 << 14;
     public static final int TYPE_WIDGETS_EDUCATION_DIALOG = 1 << 15;
+    public static final int TYPE_TASKBAR_EDUCATION_DIALOG = 1 << 16;
 
     public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
             | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
             | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU
             | TYPE_OPTIONS_POPUP | TYPE_SNACKBAR | TYPE_LISTENER | TYPE_ALL_APPS_EDU
             | TYPE_ICON_SURFACE | TYPE_DRAG_DROP_POPUP | TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP
-            | TYPE_WIDGETS_EDUCATION_DIALOG;
+            | TYPE_WIDGETS_EDUCATION_DIALOG | TYPE_TASKBAR_EDUCATION_DIALOG;
 
     // Type of popups which should be kept open during launcher rebind
     public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
             | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
-            | TYPE_ALL_APPS_EDU | TYPE_ICON_SURFACE | TYPE_WIDGETS_EDUCATION_DIALOG;
+            | TYPE_ALL_APPS_EDU | TYPE_ICON_SURFACE | TYPE_WIDGETS_EDUCATION_DIALOG
+            | TYPE_TASKBAR_EDUCATION_DIALOG;
 
     // Usually we show the back button when a floating view is open. Instead, hide for these types.
     public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
@@ -192,10 +195,24 @@
     }
 
     /**
-     * Returns a view matching FloatingViewType
+     * Returns a view matching FloatingViewType and {@link #isOpen()} == true.
      */
     public static <T extends AbstractFloatingView> T getOpenView(
             ActivityContext activity, @FloatingViewType int type) {
+        return getView(activity, type, true /* mustBeOpen */);
+    }
+
+    /**
+     * Returns a view matching FloatingViewType, and {@link #isOpen()} may be false (if animating
+     * closed).
+     */
+    public static <T extends AbstractFloatingView> T getAnyView(
+            ActivityContext activity, @FloatingViewType int type) {
+        return getView(activity, type, false /* mustBeOpen */);
+    }
+
+    private static <T extends AbstractFloatingView> T getView(
+            ActivityContext activity, @FloatingViewType int type, boolean mustBeOpen) {
         BaseDragLayer dragLayer = activity.getDragLayer();
         if (dragLayer == null) return null;
         // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer,
@@ -204,7 +221,7 @@
             View child = dragLayer.getChildAt(i);
             if (child instanceof AbstractFloatingView) {
                 AbstractFloatingView view = (AbstractFloatingView) child;
-                if (view.isOfType(type) && view.isOpen()) {
+                if (view.isOfType(type) && (!mustBeOpen || view.isOpen())) {
                     return (T) view;
                 }
             }
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index ee71146..300f22b 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -2,6 +2,7 @@
 
 import static android.appwidget.AppWidgetHostView.getDefaultPaddingForWidget;
 
+import static com.android.launcher3.CellLayout.SPRING_LOADED_PROGRESS;
 import static com.android.launcher3.LauncherAnimUtils.LAYOUT_HEIGHT;
 import static com.android.launcher3.LauncherAnimUtils.LAYOUT_WIDTH;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RESIZE_COMPLETED;
@@ -9,6 +10,8 @@
 import static com.android.launcher3.views.BaseDragLayer.LAYOUT_X;
 import static com.android.launcher3.views.BaseDragLayer.LAYOUT_Y;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
@@ -29,6 +32,7 @@
 
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
 import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.keyboard.ViewGroupFocusHelper;
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.InstanceIdSequence;
 import com.android.launcher3.model.data.ItemInfo;
@@ -49,12 +53,14 @@
     private static final String KEY_RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN =
             "launcher.reconfigurable_widget_education_tip_seen";
     private static final Rect sTmpRect = new Rect();
+    private static final Rect sTmpRect2 = new Rect();
 
     private static final int HANDLE_COUNT = 4;
     private static final int INDEX_LEFT = 0;
     private static final int INDEX_TOP = 1;
     private static final int INDEX_RIGHT = 2;
     private static final int INDEX_BOTTOM = 3;
+    private static final float MIN_OPACITY_FOR_CELL_LAYOUT_DURING_INVALID_RESIZE = 0.5f;
 
     private final Launcher mLauncher;
     private final DragViewStateAnnouncer mStateAnnouncer;
@@ -103,6 +109,16 @@
 
     private final InstanceId logInstanceId = new InstanceIdSequence().newInstanceId();
 
+    private final ViewGroupFocusHelper mDragLayerRelativeCoordinateHelper;
+
+    /**
+     * In the two panel UI, it is not possible to resize a widget to cross its host
+     * {@link CellLayout}'s sibling. When this happens, we gradually reduce the opacity of the
+     * sibling {@link CellLayout} from 1f to
+     * {@link #MIN_OPACITY_FOR_CELL_LAYOUT_DURING_INVALID_RESIZE}.
+     */
+    private final float mDragAcrossTwoPanelOpacityMargin;
+
     private boolean mLeftBorderActive;
     private boolean mRightBorderActive;
     private boolean mTopBorderActive;
@@ -149,6 +165,10 @@
         for (int i = 0; i < HANDLE_COUNT; i++) {
             mSystemGestureExclusionRects.add(new Rect());
         }
+
+        mDragAcrossTwoPanelOpacityMargin = mLauncher.getResources().getDimensionPixelSize(
+                R.dimen.resize_frame_invalid_drag_across_two_panel_opacity_margin);
+        mDragLayerRelativeCoordinateHelper = new ViewGroupFocusHelper(mLauncher.getDragLayer());
     }
 
     @Override
@@ -195,7 +215,7 @@
 
         dl.addView(frame);
         frame.mIsOpen = true;
-        frame.snapToWidget(false);
+        frame.post(() -> frame.snapToWidget(false));
     }
 
     private void setupForWidget(LauncherAppWidgetHostView widgetView, CellLayout cellLayout,
@@ -221,13 +241,15 @@
         // Only show resize handles for the directions in which resizing is possible.
         InvariantDeviceProfile idp = LauncherAppState.getIDP(cellLayout.getContext());
         mVerticalResizeActive = (info.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0
-                && mMinVSpan < idp.numRows && mMaxVSpan > 1;
+                && mMinVSpan < idp.numRows && mMaxVSpan > 1
+                && mMinVSpan < mMaxVSpan;
         if (!mVerticalResizeActive) {
             mDragHandles[INDEX_TOP].setVisibility(GONE);
             mDragHandles[INDEX_BOTTOM].setVisibility(GONE);
         }
         mHorizontalResizeActive = (info.resizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0
-                && mMinHSpan < idp.numColumns && mMaxHSpan > 1;
+                && mMinHSpan < idp.numColumns && mMaxHSpan > 1
+                && mMinHSpan < mMaxHSpan;
         if (!mHorizontalResizeActive) {
             mDragHandles[INDEX_LEFT].setVisibility(GONE);
             mDragHandles[INDEX_RIGHT].setVisibility(GONE);
@@ -359,6 +381,37 @@
             lp.y = sTmpRect.top;
         }
 
+        // Handle invalid resize across CellLayouts in the two panel UI.
+        if (mCellLayout.getParent() instanceof Workspace) {
+            Workspace workspace = (Workspace) mCellLayout.getParent();
+            CellLayout pairedCellLayout = workspace.getScreenPair(mCellLayout);
+            if (pairedCellLayout != null) {
+                Rect focusedCellLayoutBound = sTmpRect;
+                mDragLayerRelativeCoordinateHelper.viewToRect(mCellLayout, focusedCellLayoutBound);
+                Rect resizeFrameBound = sTmpRect2;
+                findViewById(R.id.widget_resize_frame).getGlobalVisibleRect(resizeFrameBound);
+                float progress = 1f;
+                if (workspace.indexOfChild(pairedCellLayout) < workspace.indexOfChild(mCellLayout)
+                        && mDeltaX < 0
+                        && resizeFrameBound.left < focusedCellLayoutBound.left) {
+                    // Resize from right to left.
+                    progress = (mDragAcrossTwoPanelOpacityMargin + mDeltaX)
+                            / mDragAcrossTwoPanelOpacityMargin;
+                } else if (workspace.indexOfChild(pairedCellLayout)
+                                > workspace.indexOfChild(mCellLayout)
+                        && mDeltaX > 0
+                        && resizeFrameBound.right > focusedCellLayoutBound.right) {
+                    // Resize from left to right.
+                    progress = (mDragAcrossTwoPanelOpacityMargin - mDeltaX)
+                            / mDragAcrossTwoPanelOpacityMargin;
+                }
+                float alpha = Math.max(MIN_OPACITY_FOR_CELL_LAYOUT_DURING_INVALID_RESIZE, progress);
+                float springLoadedProgress = Math.min(1f, 1f - progress);
+                updateInvalidResizeEffect(mCellLayout, pairedCellLayout, alpha,
+                        springLoadedProgress);
+            }
+        }
+
         requestLayout();
     }
 
@@ -371,8 +424,8 @@
      */
     private void resizeWidgetIfNeeded(boolean onDismiss) {
         DeviceProfile dp = mLauncher.getDeviceProfile();
-        float xThreshold = mCellLayout.getCellWidth() + dp.cellLayoutBorderSpacingPx;
-        float yThreshold = mCellLayout.getCellHeight() + dp.cellLayoutBorderSpacingPx;
+        float xThreshold = mCellLayout.getCellWidth() + dp.cellLayoutBorderSpacePx.x;
+        float yThreshold = mCellLayout.getCellHeight() + dp.cellLayoutBorderSpacePx.y;
 
         int hSpanInc = getSpanIncrement((mDeltaX + mDeltaXAddOn) / xThreshold - mRunningHInc);
         int vSpanInc = getSpanIncrement((mDeltaY + mDeltaYAddOn) / yThreshold - mRunningVInc);
@@ -457,8 +510,8 @@
 
     private void onTouchUp() {
         DeviceProfile dp = mLauncher.getDeviceProfile();
-        int xThreshold = mCellLayout.getCellWidth() + dp.cellLayoutBorderSpacingPx;
-        int yThreshold = mCellLayout.getCellHeight() + dp.cellLayoutBorderSpacingPx;
+        int xThreshold = mCellLayout.getCellWidth() + dp.cellLayoutBorderSpacePx.x;
+        int yThreshold = mCellLayout.getCellHeight() + dp.cellLayoutBorderSpacePx.y;
 
         mDeltaXAddOn = mRunningHInc * xThreshold;
         mDeltaYAddOn = mRunningVInc * yThreshold;
@@ -515,13 +568,24 @@
         }
 
         final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
+        final CellLayout pairedCellLayout;
+        if (mCellLayout.getParent() instanceof Workspace) {
+            Workspace workspace = (Workspace) mCellLayout.getParent();
+            pairedCellLayout = workspace.getScreenPair(mCellLayout);
+        } else {
+            pairedCellLayout = null;
+        }
         if (!animate) {
             lp.width = newWidth;
             lp.height = newHeight;
             lp.x = newX;
             lp.y = newY;
             for (int i = 0; i < HANDLE_COUNT; i++) {
-                mDragHandles[i].setAlpha(1.0f);
+                mDragHandles[i].setAlpha(1f);
+            }
+            if (pairedCellLayout != null) {
+                updateInvalidResizeEffect(mCellLayout, pairedCellLayout, /* alpha= */ 1f,
+                        /* springLoadedProgress= */ 0f);
             }
             requestLayout();
         } else {
@@ -538,6 +602,10 @@
                 set.play(mFirstFrameAnimatorHelper.addTo(
                         ObjectAnimator.ofFloat(mDragHandles[i], ALPHA, 1f)));
             }
+            if (pairedCellLayout != null) {
+                updateInvalidResizeEffect(mCellLayout, pairedCellLayout, /* alpha= */ 1f,
+                        /* springLoadedProgress= */ 0f, /* animatorSet= */ set);
+            }
             set.setDuration(SNAP_DURATION);
             set.start();
         }
@@ -624,6 +692,52 @@
         }
     }
 
+    private void updateInvalidResizeEffect(CellLayout cellLayout, CellLayout pairedCellLayout,
+            float alpha, float springLoadedProgress) {
+        updateInvalidResizeEffect(cellLayout, pairedCellLayout, alpha,
+                springLoadedProgress, /* animatorSet= */ null);
+    }
+
+    private void updateInvalidResizeEffect(CellLayout cellLayout, CellLayout pairedCellLayout,
+            float alpha, float springLoadedProgress, @Nullable AnimatorSet animatorSet) {
+        int childCount = pairedCellLayout.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = pairedCellLayout.getChildAt(i);
+            if (animatorSet != null) {
+                animatorSet.play(
+                        mFirstFrameAnimatorHelper.addTo(
+                                ObjectAnimator.ofFloat(child, ALPHA, alpha)));
+            } else {
+                child.setAlpha(alpha);
+            }
+        }
+        if (animatorSet != null) {
+            animatorSet.play(mFirstFrameAnimatorHelper.addTo(
+                    ObjectAnimator.ofFloat(cellLayout, SPRING_LOADED_PROGRESS,
+                            springLoadedProgress)));
+            animatorSet.play(mFirstFrameAnimatorHelper.addTo(
+                    ObjectAnimator.ofFloat(pairedCellLayout, SPRING_LOADED_PROGRESS,
+                            springLoadedProgress)));
+        } else {
+            cellLayout.setSpringLoadedProgress(springLoadedProgress);
+            pairedCellLayout.setSpringLoadedProgress(springLoadedProgress);
+        }
+
+        boolean shouldShowCellLayoutBorder = springLoadedProgress > 0f;
+        if (animatorSet != null) {
+            animatorSet.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animator) {
+                    cellLayout.setIsDragOverlapping(shouldShowCellLayoutBorder);
+                    pairedCellLayout.setIsDragOverlapping(shouldShowCellLayoutBorder);
+                }
+            });
+        } else {
+            cellLayout.setIsDragOverlapping(shouldShowCellLayoutBorder);
+            pairedCellLayout.setIsDragOverlapping(shouldShowCellLayoutBorder);
+        }
+    }
+
     @Override
     protected boolean isOfType(int type) {
         return (type & TYPE_WIDGET_RESIZE_FRAME) != 0;
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 9778b61..ec96c6d 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -145,6 +145,7 @@
     /**
      * Returns {@link StatsLogManager} for user event logging.
      */
+    @Override
     public StatsLogManager getStatsLogManager() {
         if (mStatsLogManager == null) {
             mStatsLogManager = StatsLogManager.newInstance(this);
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 2c76e52..dd56ca3 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -41,7 +41,6 @@
 import android.view.ActionMode;
 import android.view.Display;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.WindowInsets.Type;
 import android.view.WindowMetrics;
 import android.widget.Toast;
@@ -55,6 +54,7 @@
 import com.android.launcher3.allapps.search.SearchAdapterProvider;
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.InstanceIdSequence;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.touch.ItemClickHandler;
@@ -165,12 +165,6 @@
         // no-op
     }
 
-    public Rect getViewBounds(View v) {
-        int[] pos = new int[2];
-        v.getLocationOnScreen(pos);
-        return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
-    }
-
     @NonNull
     public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
         int left = 0, top = 0;
@@ -205,7 +199,7 @@
         // Prepare intent
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         if (v != null) {
-            intent.setSourceBounds(getViewBounds(v));
+            intent.setSourceBounds(Utilities.getViewBounds(v));
         }
         try {
             boolean isShortcut = (item instanceof WorkspaceItemInfo)
@@ -224,7 +218,7 @@
             }
             if (item != null) {
                 InstanceId instanceId = new InstanceIdSequence().newInstanceId();
-                logAppLaunch(item, instanceId);
+                logAppLaunch(getStatsLogManager(), item, instanceId);
             }
             return true;
         } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
@@ -234,8 +228,12 @@
         return false;
     }
 
-    protected void logAppLaunch(ItemInfo info, InstanceId instanceId) {
-        getStatsLogManager().logger().withItemInfo(info).withInstanceId(instanceId)
+    /**
+     * Creates and logs a new app launch event.
+     */
+    public void logAppLaunch(StatsLogManager statsLogManager, ItemInfo info,
+            InstanceId instanceId) {
+        statsLogManager.logger().withItemInfo(info).withInstanceId(instanceId)
                 .log(LAUNCHER_APP_LAUNCH_TAP);
     }
 
@@ -311,7 +309,8 @@
         }
     }
 
-    public OnClickListener getItemOnClickListener() {
+    @Override
+    public View.OnClickListener getItemOnClickListener() {
         return ItemClickHandler.INSTANCE;
     }
 
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index ddbd425..35c257f 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.config.FeatureFlags.ENABLE_ICON_LABEL_AUTO_SCALING;
 import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
 
@@ -32,6 +33,8 @@
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.icu.text.MessageFormat;
+import android.text.TextPaint;
 import android.text.TextUtils.TruncateAt;
 import android.util.AttributeSet;
 import android.util.Property;
@@ -65,9 +68,12 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.BubbleTextHolder;
 import com.android.launcher3.views.IconLabelDotView;
 
 import java.text.NumberFormat;
+import java.util.HashMap;
+import java.util.Locale;
 
 /**
  * TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan
@@ -84,12 +90,19 @@
     private static final int DISPLAY_SEARCH_RESULT = 6;
     private static final int DISPLAY_SEARCH_RESULT_SMALL = 7;
 
+    private static final float MIN_LETTER_SPACING = -0.05f;
+    private static final int MAX_SEARCH_LOOP_COUNT = 20;
+
     private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
     private static final float HIGHLIGHT_SCALE = 1.16f;
 
     private final PointF mTranslationForReorderBounce = new PointF(0, 0);
     private final PointF mTranslationForReorderPreview = new PointF(0, 0);
 
+    private float mTranslationXForTaskbarAlignmentAnimation = 0f;
+
+    private final PointF mTranslationForMoveFromCenterAnimation = new PointF(0, 0);
+
     private float mScaleForReorderBounce = 1f;
 
     private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY
@@ -155,6 +168,7 @@
     private HandlerRunnable mIconLoadRequest;
 
     private boolean mEnableIconUpdateAnimation = false;
+    private BubbleTextHolder mBubbleTextHolder;
 
     public BubbleTextView(Context context) {
         this(context, null, 0);
@@ -254,9 +268,27 @@
 
     @UiThread
     public void applyFromWorkspaceItem(WorkspaceItemInfo info) {
+        applyFromWorkspaceItem(info, /* animate = */ false, /* staggerIndex = */ 0);
+    }
+
+    @UiThread
+    public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean animate, int staggerIndex) {
         applyFromWorkspaceItem(info, false);
     }
 
+    /**
+     * Returns whether the newInfo differs from the current getTag().
+     */
+    public boolean shouldAnimateIconChange(WorkspaceItemInfo newInfo) {
+        WorkspaceItemInfo oldInfo = getTag() instanceof WorkspaceItemInfo
+                ? (WorkspaceItemInfo) getTag()
+                : null;
+        boolean changedIcons = oldInfo != null && oldInfo.getTargetComponent() != null
+                && newInfo.getTargetComponent() != null
+                && !oldInfo.getTargetComponent().equals(newInfo.getTargetComponent());
+        return changedIcons && isShown();
+    }
+
     @Override
     public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
         if (delegate instanceof LauncherAccessibilityDelegate) {
@@ -272,7 +304,7 @@
     @UiThread
     public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean promiseStateChanged) {
         applyIconAndLabel(info);
-        setTag(info);
+        setItemInfo(info);
         applyLoadingState(promiseStateChanged);
         applyDotState(info, false /* animate */);
         setDownloadStateContentDescription(info, info.getProgressLevel());
@@ -283,7 +315,8 @@
         applyIconAndLabel(info);
 
         // We don't need to check the info since it's not a WorkspaceItemInfo
-        super.setTag(info);
+        setItemInfo(info);
+
 
         // Verify high res immediately
         verifyHighRes();
@@ -302,7 +335,7 @@
     public void applyFromItemInfoWithIcon(ItemInfoWithIcon info) {
         applyIconAndLabel(info);
         // We don't need to check the info since it's not a WorkspaceItemInfo
-        super.setTag(info);
+        setItemInfo(info);
 
         // Verify high res immediately
         verifyHighRes();
@@ -310,18 +343,22 @@
         setDownloadStateContentDescription(info, info.getProgressLevel());
     }
 
-    /**
-     * Apply label and tag using a {@link SearchActionItemInfo}
-     */
-    @UiThread
-    public void applyFromSearchActionItemInfo(SearchActionItemInfo searchActionItemInfo) {
-        applyIconAndLabel(searchActionItemInfo);
-        setTag(searchActionItemInfo);
+    private void setItemInfo(ItemInfoWithIcon itemInfo) {
+        setTag(itemInfo);
+        if (mBubbleTextHolder != null) {
+            mBubbleTextHolder.onItemInfoUpdated(itemInfo);
+        }
+    }
+
+    public void setBubbleTextHolder(
+            BubbleTextHolder bubbleTextHolder) {
+        mBubbleTextHolder = bubbleTextHolder;
     }
 
     @UiThread
     protected void applyIconAndLabel(ItemInfoWithIcon info) {
-        boolean useTheme = mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER;
+        boolean useTheme = mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER
+                || mDisplay == DISPLAY_TASKBAR;
         FastBitmapDrawable iconDrawable = info.newIcon(getContext(), useTheme);
         mDotParams.color = IconPalette.getMutedColor(iconDrawable.getIconColor(), 0.54f);
 
@@ -388,6 +425,10 @@
      * Returns true if the touch down at the provided position be ignored
      */
     protected boolean shouldIgnoreTouchDown(float x, float y) {
+        if (mDisplay == DISPLAY_TASKBAR) {
+            // Allow touching within padding on taskbar, given icon sizes are smaller.
+            return false;
+        }
         return y < getPaddingTop()
                 || x < getPaddingLeft()
                 || y > getHeight() - getPaddingBottom()
@@ -407,7 +448,7 @@
         }
     }
 
-    void clearPressedBackground() {
+    public void clearPressedBackground() {
         setPressed(false);
         setStayPressed(false);
     }
@@ -424,6 +465,75 @@
         return result;
     }
 
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        checkForEllipsis();
+    }
+
+    @Override
+    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
+        super.onTextChanged(text, start, lengthBefore, lengthAfter);
+        checkForEllipsis();
+    }
+
+    private void checkForEllipsis() {
+        if (!ENABLE_ICON_LABEL_AUTO_SCALING.get()) {
+            return;
+        }
+        float width = getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight();
+        if (width <= 0) {
+            return;
+        }
+        setLetterSpacing(0);
+
+        String text = getText().toString();
+        TextPaint paint = getPaint();
+        if (paint.measureText(text) < width) {
+            return;
+        }
+
+        float spacing = findBestSpacingValue(paint, text, width, MIN_LETTER_SPACING);
+        // Reset the paint value so that the call to TextView does appropriate diff.
+        paint.setLetterSpacing(0);
+        setLetterSpacing(spacing);
+    }
+
+    /**
+     * Find the appropriate text spacing to display the provided text
+     * @param paint the paint used by the text view
+     * @param text the text to display
+     * @param allowedWidthPx available space to render the text
+     * @param minSpacingEm minimum spacing allowed between characters
+     * @return the final textSpacing value
+     *
+     * @see #setLetterSpacing(float)
+     */
+    private float findBestSpacingValue(TextPaint paint, String text, float allowedWidthPx,
+            float minSpacingEm) {
+        paint.setLetterSpacing(minSpacingEm);
+        if (paint.measureText(text) > allowedWidthPx) {
+            // If there is no result at high limit, we can do anything more
+            return minSpacingEm;
+        }
+
+        float lowLimit = 0;
+        float highLimit = minSpacingEm;
+
+        for (int i = 0; i < MAX_SEARCH_LOOP_COUNT; i++) {
+            float value = (lowLimit + highLimit) / 2;
+            paint.setLetterSpacing(value);
+            if (paint.measureText(text) < allowedWidthPx) {
+                highLimit = value;
+            } else {
+                lowLimit = value;
+            }
+        }
+
+        // At the end error on the higher side
+        return highLimit;
+    }
+
     @SuppressWarnings("wrongcall")
     protected void drawWithoutDot(Canvas canvas) {
         super.onDraw(canvas);
@@ -668,8 +778,8 @@
                             itemInfo.contentDescription));
                 } else if (hasDot()) {
                     int count = mDotInfo.getNotificationCount();
-                    setContentDescription(getContext().getResources().getQuantityString(
-                            R.plurals.dotted_app_label, count, itemInfo.contentDescription, count));
+                    setContentDescription(
+                            getAppLabelPluralString(itemInfo.contentDescription.toString(), count));
                 } else {
                     setContentDescription(itemInfo.contentDescription);
                 }
@@ -769,7 +879,7 @@
             } else if (info instanceof PackageItemInfo) {
                 applyFromItemInfoWithIcon((PackageItemInfo) info);
             } else if (info instanceof SearchActionItemInfo) {
-                applyFromSearchActionItemInfo((SearchActionItemInfo) info);
+                applyFromItemInfoWithIcon((SearchActionItemInfo) info);
             }
 
             mDisableRelayout = false;
@@ -799,8 +909,11 @@
     }
 
     private void updateTranslation() {
-        super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x);
-        super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y);
+        super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x
+                + mTranslationForMoveFromCenterAnimation.x
+                + mTranslationXForTaskbarAlignmentAnimation);
+        super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y
+                + mTranslationForMoveFromCenterAnimation.y);
     }
 
     public void setReorderBounceOffset(float x, float y) {
@@ -833,6 +946,29 @@
         return mScaleForReorderBounce;
     }
 
+    /**
+     * Sets translation values for move from center animation
+     */
+    public void setTranslationForMoveFromCenterAnimation(float x, float y) {
+        mTranslationForMoveFromCenterAnimation.set(x, y);
+        updateTranslation();
+    }
+
+    /**
+     * Sets translationX for taskbar to launcher alignment animation
+     */
+    public void setTranslationXForTaskbarAlignmentAnimation(float translationX) {
+        mTranslationXForTaskbarAlignmentAnimation = translationX;
+        updateTranslation();
+    }
+
+    /**
+     * Returns translationX value for taskbar to launcher alignment animation
+     */
+    public float getTranslationXForTaskbarAlignmentAnimation() {
+        return mTranslationXForTaskbarAlignmentAnimation;
+    }
+
     public View getView() {
         return this;
     }
@@ -853,8 +989,9 @@
         switch (display) {
             case DISPLAY_ALL_APPS:
                 return grid.allAppsIconSizePx;
-            case DISPLAY_WORKSPACE:
             case DISPLAY_FOLDER:
+                return grid.folderChildIconSizePx;
+            case DISPLAY_WORKSPACE:
             default:
                 return grid.iconSizePx;
         }
@@ -884,4 +1021,14 @@
             setCompoundDrawables(null, newIcon, null, null);
         }
     }
+
+    private String getAppLabelPluralString(String appName, int notificationCount) {
+        MessageFormat icuCountFormat = new MessageFormat(
+                getResources().getString(R.string.dotted_app_label),
+                Locale.getDefault());
+        HashMap<String, Object> args = new HashMap();
+        args.put("app_name", appName);
+        args.put("count", notificationCount);
+        return icuCountFormat.format(args);
+    }
 }
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index bc4c982..477dcf8 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -215,12 +215,8 @@
         }
         final DragLayer dragLayer = mLauncher.getDragLayer();
         final DragView dragView = d.dragView;
-        final Rect from = new Rect();
-        dragLayer.getViewRectRelativeToSelf(d.dragView, from);
-
         final Rect to = getIconRect(d);
-        final float scale = (float) to.width() / from.width();
-        dragView.disableColorExtraction();
+        final float scale = (float) to.width() / dragView.getMeasuredWidth();
         dragView.detachContentView(/* reattachToPreviousParent= */ true);
         mDropTargetBar.deferOnDragEnd();
 
@@ -228,14 +224,11 @@
             completeDrop(d);
             mDropTargetBar.onDragEnd();
             mLauncher.getStateManager().goToState(NORMAL);
-            // Only re-enable updates once the workspace is back to normal, which will be after the
-            // current frame.
-            post(dragView::resumeColorExtraction);
         };
 
-        dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f,
+        dragLayer.animateView(d.dragView, to, scale, 0.1f, 0.1f,
                 DRAG_VIEW_DROP_DURATION,
-                Interpolators.DEACCEL_2, Interpolators.LINEAR, onAnimationEndRunnable,
+                Interpolators.DEACCEL_2, onAnimationEndRunnable,
                 DragLayer.ANIMATION_END_DISAPPEAR, null);
     }
 
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index a6adfc4..02eb1de 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -18,7 +18,6 @@
 
 import static android.animation.ValueAnimator.areAnimatorsEnabled;
 
-import static com.android.launcher3.Utilities.getBoundsForViewInDragLayer;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
 
 import android.animation.Animator;
@@ -92,7 +91,7 @@
     private int mFixedCellWidth;
     private int mFixedCellHeight;
     @ViewDebug.ExportedProperty(category = "launcher")
-    private final int mBorderSpacing;
+    private final Point mBorderSpace;
 
     @ViewDebug.ExportedProperty(category = "launcher")
     private int mCountX;
@@ -237,9 +236,9 @@
         mActivity = ActivityContext.lookupContext(context);
         DeviceProfile deviceProfile = mActivity.getDeviceProfile();
 
-        mBorderSpacing = mContainerType == FOLDER
-                ? deviceProfile.folderCellLayoutBorderSpacingPx
-                : deviceProfile.cellLayoutBorderSpacingPx;
+        mBorderSpace = mContainerType == FOLDER
+                ? new Point(deviceProfile.folderCellLayoutBorderSpacePx)
+                : new Point(deviceProfile.cellLayoutBorderSpacePx);
 
         mCellWidth = mCellHeight = -1;
         mFixedCellWidth = mFixedCellHeight = -1;
@@ -309,7 +308,7 @@
 
         mShortcutsAndWidgets = new ShortcutAndWidgetContainer(context, mContainerType);
         mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY,
-                mBorderSpacing);
+                mBorderSpace);
         addView(mShortcutsAndWidgets);
     }
 
@@ -369,7 +368,7 @@
         mFixedCellWidth = mCellWidth = width;
         mFixedCellHeight = mCellHeight = height;
         mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY,
-                mBorderSpacing);
+                mBorderSpace);
     }
 
     public void setGridSize(int x, int y) {
@@ -379,7 +378,7 @@
         mTmpOccupied = new GridOccupancy(mCountX, mCountY);
         mTempRectStack.clear();
         mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY,
-                mBorderSpacing);
+                mBorderSpace);
         requestLayout();
     }
 
@@ -542,9 +541,9 @@
         if (mVisualizeCells) {
             for (int i = 0; i < mCountX; i++) {
                 for (int j = 0; j < mCountY; j++) {
-                    int transX = i * mCellWidth + (i * mBorderSpacing) + getPaddingLeft()
+                    int transX = i * mCellWidth + (i * mBorderSpace.x) + getPaddingLeft()
                             + paddingX;
-                    int transY = j * mCellHeight + (j * mBorderSpacing) + getPaddingTop()
+                    int transY = j * mCellHeight + (j * mBorderSpace.y) + getPaddingTop()
                             + paddingY;
 
                     mVisualizeGridRect.offsetTo(transX, transY);
@@ -568,12 +567,12 @@
 
                 // TODO b/194414754 clean this up, reconcile with cellToRect
                 mVisualizeGridRect.set(paddingX, paddingY,
-                        mCellWidth * spanX + mBorderSpacing * (spanX - 1) - paddingX,
-                        mCellHeight * spanY + mBorderSpacing * (spanY - 1) - paddingY);
+                        mCellWidth * spanX + mBorderSpace.x * (spanX - 1) - paddingX,
+                        mCellHeight * spanY + mBorderSpace.y * (spanY - 1) - paddingY);
 
-                int transX = x * mCellWidth + (x * mBorderSpacing)
+                int transX = x * mCellWidth + (x * mBorderSpace.x)
                         + getPaddingLeft() + paddingX;
-                int transY = y * mCellHeight + (y * mBorderSpacing)
+                int transY = y * mCellHeight + (y * mBorderSpace.y)
                         + getPaddingTop() + paddingY;
 
                 mVisualizeGridRect.offsetTo(transX, transY);
@@ -859,15 +858,15 @@
         int childHeightSize = heightSize - (getPaddingTop() + getPaddingBottom());
 
         if (mFixedCellWidth < 0 || mFixedCellHeight < 0) {
-            int cw = DeviceProfile.calculateCellWidth(childWidthSize, mBorderSpacing,
+            int cw = DeviceProfile.calculateCellWidth(childWidthSize, mBorderSpace.x,
                     mCountX);
-            int ch = DeviceProfile.calculateCellHeight(childHeightSize, mBorderSpacing,
+            int ch = DeviceProfile.calculateCellHeight(childHeightSize, mBorderSpace.y,
                     mCountY);
             if (cw != mCellWidth || ch != mCellHeight) {
                 mCellWidth = cw;
                 mCellHeight = ch;
                 mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY,
-                        mBorderSpacing);
+                        mBorderSpace);
             }
         }
 
@@ -921,7 +920,7 @@
      */
     public int getUnusedHorizontalSpace() {
         return getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth)
-                - ((mCountX - 1) * mBorderSpacing);
+                - ((mCountX - 1) * mBorderSpace.x);
     }
 
     @Override
@@ -1071,18 +1070,12 @@
         // Apply local extracted color if the DragView is an AppWidgetHostViewDrawable.
         View view = dragObject.dragView.getContentView();
         if (view instanceof LauncherAppWidgetHostView) {
-            Launcher launcher = Launcher.getLauncher(dragObject.dragView.getContext());
+            Launcher launcher = Launcher.getLauncher(getContext());
             Workspace workspace = launcher.getWorkspace();
             int screenId = workspace.getIdForScreen(this);
-            int pageId = workspace.getPageIndexForScreenId(screenId);
             cellToRect(targetCell[0], targetCell[1], spanX, spanY, mTempRect);
 
-            // Now get the rect in drag layer coordinates.
-            getBoundsForViewInDragLayer(launcher.getDragLayer(), this, mTempRect, true,
-                    mTmpFloatArray, mTempRectF);
-            Utilities.setRect(mTempRectF, mTempRect);
-
-            ((LauncherAppWidgetHostView) view).handleDrag(mTempRect, pageId);
+            ((LauncherAppWidgetHostView) view).handleDrag(mTempRect, this, screenId);
         }
     }
 
@@ -2148,7 +2141,7 @@
         mShakeAnimators.clear();
     }
 
-    private void commitTempPlacement() {
+    private void commitTempPlacement(View dragView) {
         mTmpOccupied.copyTo(mOccupied);
 
         int screenId = Launcher.cast(mActivity).getWorkspace().getIdForScreen(this);
@@ -2166,7 +2159,7 @@
             ItemInfo info = (ItemInfo) child.getTag();
             // We do a null check here because the item info can be null in the case of the
             // AllApps button in the hotseat.
-            if (info != null) {
+            if (info != null && child != dragView) {
                 final boolean requiresDbUpdate = (info.cellX != lp.tmpCellX
                         || info.cellY != lp.tmpCellY || info.spanX != lp.cellHSpan
                         || info.spanY != lp.cellVSpan);
@@ -2328,7 +2321,7 @@
             animateItemsToSolution(swapSolution, dragView, commit);
 
             if (commit) {
-                commitTempPlacement();
+                commitTempPlacement(null);
                 completeAndClearReorderPreviewAnimations();
                 setItemPlacementDirty(false);
             } else {
@@ -2422,7 +2415,8 @@
 
                 if (!DESTRUCTIVE_REORDER &&
                         (mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL)) {
-                    commitTempPlacement();
+                    // Since the temp solution didn't update dragView, don't commit it either
+                    commitTempPlacement(dragView);
                     completeAndClearReorderPreviewAnimations();
                     setItemPlacementDirty(false);
                 } else {
@@ -2598,11 +2592,11 @@
                 + (int) Math.ceil(getUnusedHorizontalSpace() / 2f);
         final int vStartPadding = getPaddingTop();
 
-        int x = hStartPadding + (cellX * mBorderSpacing) + (cellX * cellWidth);
-        int y = vStartPadding + (cellY * mBorderSpacing) + (cellY * cellHeight);
+        int x = hStartPadding + (cellX * mBorderSpace.x) + (cellX * cellWidth);
+        int y = vStartPadding + (cellY * mBorderSpace.y) + (cellY * cellHeight);
 
-        int width = cellHSpan * cellWidth + ((cellHSpan - 1) * mBorderSpacing);
-        int height = cellVSpan * cellHeight + ((cellVSpan - 1) * mBorderSpacing);
+        int width = cellHSpan * cellWidth + ((cellHSpan - 1) * mBorderSpace.x);
+        int height = cellVSpan * cellHeight + ((cellVSpan - 1) * mBorderSpace.y);
 
         resultRect.set(x, y, x + width, y + height);
     }
@@ -2621,12 +2615,12 @@
 
     public int getDesiredWidth() {
         return getPaddingLeft() + getPaddingRight() + (mCountX * mCellWidth)
-                + ((mCountX - 1) * mBorderSpacing);
+                + ((mCountX - 1) * mBorderSpace.x);
     }
 
     public int getDesiredHeight()  {
         return getPaddingTop() + getPaddingBottom() + (mCountY * mCellHeight)
-                + ((mCountY - 1) * mBorderSpacing);
+                + ((mCountY - 1) * mBorderSpace.y);
     }
 
     public boolean isOccupied(int x, int y) {
@@ -2742,20 +2736,20 @@
         }
 
         public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount,
-                int rowCount, int borderSpacing, @Nullable Rect inset) {
+                int rowCount, Point borderSpace, @Nullable Rect inset) {
             setup(cellWidth, cellHeight, invertHorizontally, colCount, rowCount, 1.0f, 1.0f,
-                    borderSpacing, inset);
+                    borderSpace, inset);
         }
 
         /**
-         * Use this method, as opposed to {@link #setup(int, int, boolean, int, int, int, Rect)},
+         * Use this method, as opposed to {@link #setup(int, int, boolean, int, int, Point, Rect)},
          * if the view needs to be scaled.
          *
          * ie. In multi-window mode, we setup widgets so that they are measured and laid out
          * using their full/invariant device profile sizes.
          */
         public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount,
-                int rowCount, float cellScaleX, float cellScaleY, int borderSpacing,
+                int rowCount, float cellScaleX, float cellScaleY, Point borderSpace,
                 @Nullable Rect inset) {
             if (isLockedToGrid) {
                 final int myCellHSpan = cellHSpan;
@@ -2767,16 +2761,16 @@
                     myCellX = colCount - myCellX - cellHSpan;
                 }
 
-                int hBorderSpacing = (myCellHSpan - 1) * borderSpacing;
-                int vBorderSpacing = (myCellVSpan - 1) * borderSpacing;
+                int hBorderSpacing = (myCellHSpan - 1) * borderSpace.x;
+                int vBorderSpacing = (myCellVSpan - 1) * borderSpace.y;
 
                 float myCellWidth = ((myCellHSpan * cellWidth) + hBorderSpacing) / cellScaleX;
                 float myCellHeight = ((myCellVSpan * cellHeight) + vBorderSpacing) / cellScaleY;
 
                 width = Math.round(myCellWidth) - leftMargin - rightMargin;
                 height = Math.round(myCellHeight) - topMargin - bottomMargin;
-                x = leftMargin + (myCellX * cellWidth) + (myCellX * borderSpacing);
-                y = topMargin + (myCellY * cellHeight) + (myCellY * borderSpacing);
+                x = leftMargin + (myCellX * cellWidth) + (myCellX * borderSpace.x);
+                y = topMargin + (myCellY * cellHeight) + (myCellY * borderSpace.y);
 
                 if (inset != null) {
                     x -= inset.left;
@@ -2878,7 +2872,7 @@
                 directionVector, null, false, configuration).isSolution) {
             if (commitConfig) {
                 copySolutionToTempState(configuration, null);
-                commitTempPlacement();
+                commitTempPlacement(null);
                 // undo marking cells occupied since there is actually nothing being placed yet.
                 mOccupied.markCells(0, mCountY - 1, mCountX, 1, false);
             }
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 80ec192..477964a 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -25,6 +25,7 @@
 import android.util.AttributeSet;
 import android.view.View;
 
+import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.logging.StatsLogManager;
@@ -33,6 +34,7 @@
 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.IntSet;
 import com.android.launcher3.views.Snackbar;
 
 public class DeleteDropTarget extends ButtonDropTarget {
@@ -127,11 +129,21 @@
     public void completeDrop(DragObject d) {
         ItemInfo item = d.dragInfo;
         if (canRemove(item)) {
-            int itemPage = mLauncher.getWorkspace().getCurrentPage();
+            ItemInfo pageItem = item;
+            if (item.container <= 0) {
+                View v = mLauncher.getWorkspace().getHomescreenIconByItemId(item.container);
+                if (v != null) {
+                    pageItem = (ItemInfo) v.getTag();
+                }
+            }
+            IntSet pageIds = pageItem.container == Favorites.CONTAINER_DESKTOP
+                    ? IntSet.wrap(pageItem.screenId)
+                    : mLauncher.getWorkspace().getCurrentPageScreenIds();
+
             onAccessibilityDrop(null, item);
             ModelWriter modelWriter = mLauncher.getModelWriter();
             Runnable onUndoClicked = () -> {
-                mLauncher.setPageToBindSynchronously(itemPage);
+                mLauncher.setPagesToBindSynchronously(pageIds);
                 modelWriter.abortDelete();
                 mLauncher.getStatsLogManager().logger().log(LAUNCHER_UNDO);
             };
diff --git a/src/com/android/launcher3/DevicePaddings.java b/src/com/android/launcher3/DevicePaddings.java
index 7c387b1..08fb47b 100644
--- a/src/com/android/launcher3/DevicePaddings.java
+++ b/src/com/android/launcher3/DevicePaddings.java
@@ -36,12 +36,12 @@
  * The unused or "extra" height is allocated to three different variable heights:
  * - The space above the workspace
  * - The space between the workspace and hotseat
- * - The espace below the hotseat
+ * - The space below the hotseat
  */
 public class DevicePaddings {
 
-    private static final String DEVICE_PADDING = "device-paddings";
-    private static final String DEVICE_PADDINGS = "device-padding";
+    private static final String DEVICE_PADDINGS = "device-paddings";
+    private static final String DEVICE_PADDING = "device-padding";
 
     private static final String WORKSPACE_TOP_PADDING = "workspaceTopPadding";
     private static final String WORKSPACE_BOTTOM_PADDING = "workspaceBottomPadding";
@@ -58,13 +58,13 @@
             int type;
             while (((type = parser.next()) != XmlPullParser.END_TAG ||
                     parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
-                if ((type == XmlPullParser.START_TAG) && DEVICE_PADDING.equals(parser.getName())) {
+                if ((type == XmlPullParser.START_TAG) && DEVICE_PADDINGS.equals(parser.getName())) {
                     final int displayDepth = parser.getDepth();
                     while (((type = parser.next()) != XmlPullParser.END_TAG ||
                             parser.getDepth() > displayDepth)
                             && type != XmlPullParser.END_DOCUMENT) {
                         if ((type == XmlPullParser.START_TAG)
-                                && DEVICE_PADDINGS.equals(parser.getName())) {
+                                && DEVICE_PADDING.equals(parser.getName())) {
                             TypedArray a = context.obtainStyledAttributes(
                                     Xml.asAttributeSet(parser), R.styleable.DevicePadding);
                             int maxWidthPx = a.getDimensionPixelSize(
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 81eda10..0b60b32 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -17,7 +17,6 @@
 package com.android.launcher3;
 
 import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
 import static com.android.launcher3.ResourceUtils.pxFromDp;
 import static com.android.launcher3.Utilities.dpiFromPx;
@@ -33,11 +32,8 @@
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
 import android.util.DisplayMetrics;
 import android.view.Surface;
-import android.view.WindowInsets;
-import android.view.WindowManager;
 
 import com.android.launcher3.CellLayout.ContainerType;
 import com.android.launcher3.DevicePaddings.DevicePadding;
@@ -45,6 +41,7 @@
 import com.android.launcher3.icons.DotRenderer;
 import com.android.launcher3.icons.GraphicsUtils;
 import com.android.launcher3.icons.IconNormalizer;
+import com.android.launcher3.uioverrides.ApiWrapper;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.WindowBounds;
@@ -55,6 +52,8 @@
 public class DeviceProfile {
 
     private static final int DEFAULT_DOT_SIZE = 100;
+    // Ratio of empty space, qsb should take up to appear visually centered.
+    private static final float QSB_CENTER_FACTOR = .325f;
 
     public final InvariantDeviceProfile inv;
     private final Info mInfo;
@@ -81,6 +80,7 @@
     public final float aspectRatio;
 
     public final boolean isScalableGrid;
+    private final int mTypeIndex;
 
     /**
      * The maximum amount of left/right workspace padding as a percentage of the screen width.
@@ -98,10 +98,10 @@
     private static final int PORTRAIT_TABLET_LEFT_RIGHT_PADDING_MULTIPLIER = 4;
 
     // Workspace
-    public final int desiredWorkspaceLeftRightOriginalPx;
-    public int desiredWorkspaceLeftRightMarginPx;
-    public final int cellLayoutBorderSpacingOriginalPx;
-    public int cellLayoutBorderSpacingPx;
+    public final int desiredWorkspaceHorizontalMarginOriginalPx;
+    public int desiredWorkspaceHorizontalMarginPx;
+    public Point cellLayoutBorderSpaceOriginalPx;
+    public Point cellLayoutBorderSpacePx;
     public final int cellLayoutPaddingLeftRightPx;
     public final int cellLayoutBottomPaddingPx;
     public final int edgeMarginPx;
@@ -138,7 +138,8 @@
     public int folderIconOffsetYPx;
 
     // Folder content
-    public int folderCellLayoutBorderSpacingPx;
+    public Point folderCellLayoutBorderSpacePx;
+    public int folderCellLayoutBorderSpaceOriginalPx;
     public int folderContentPaddingLeftRight;
     public int folderContentPaddingTop;
 
@@ -163,25 +164,35 @@
     // Start is the side next to the nav bar, end is the side next to the workspace
     public final int hotseatBarSidePaddingStartPx;
     public final int hotseatBarSidePaddingEndPx;
+    public final int hotseatQsbHeight;
 
     public final float qsbBottomMarginOriginalPx;
     public int qsbBottomMarginPx;
 
     // All apps
+    public Point allAppsCellSpacePx;
     public int allAppsOpenVerticalTranslate;
     public int allAppsCellHeightPx;
     public int allAppsCellWidthPx;
     public int allAppsIconSizePx;
     public int allAppsIconDrawablePaddingPx;
+    public int allAppsLeftRightPadding;
     public final int numShownAllAppsColumns;
     public float allAppsIconTextSizePx;
 
     // Overview
+    public final boolean overviewShowAsGrid;
     public int overviewTaskMarginPx;
+    public int overviewTaskMarginGridPx;
     public int overviewTaskIconSizePx;
+    public int overviewTaskIconDrawableSizePx;
+    public int overviewTaskIconDrawableSizeGridPx;
     public int overviewTaskThumbnailTopMarginPx;
     public final int overviewActionsMarginThreeButtonPx;
-    public final int overviewActionsMarginGesturePx;
+    public final int overviewActionsTopMarginGesturePx;
+    public final int overviewActionsBottomMarginGesturePx;
+    public int overviewPageSpacing;
+    public int overviewRowSpacing;
 
     // Widgets
     public final PointF appWidgetScale = new PointF(1.0f, 1.0f);
@@ -202,15 +213,16 @@
     public DotRenderer mDotRendererWorkSpace;
     public DotRenderer mDotRendererAllApps;
 
-     // Taskbar
+    // Taskbar
     public boolean isTaskbarPresent;
+    // Whether Taskbar will inset the bottom of apps by taskbarSize.
+    public boolean isTaskbarPresentInApps;
     public int taskbarSize;
-    // How much of the bottom inset is due to Taskbar rather than other system elements.
-    public int nonOverlappingTaskbarInset;
 
     // DragController
     public int flingToDeleteThresholdVelocity;
 
+    /** TODO: Once we fully migrate to staged split, remove "isMultiWindowMode" */
     DeviceProfile(Context context, InvariantDeviceProfile inv, Info info, WindowBounds windowBounds,
             boolean isMultiWindowMode, boolean transposeLayoutWithOrientation,
             boolean useTwoPanels) {
@@ -228,7 +240,7 @@
         widthPx = windowBounds.bounds.width();
         heightPx = windowBounds.bounds.height();
         availableWidthPx = windowBounds.availableSize.x;
-        int nonFinalAvailableHeightPx = windowBounds.availableSize.y;
+        availableHeightPx = windowBounds.availableSize.y;
 
         mInfo = info;
         // If the device's pixel density was scaled (usually via settings for A11y), use the
@@ -238,7 +250,8 @@
         // Tablet UI does not support emulated landscape.
         isTablet = allowRotation && info.isTablet(windowBounds);
         isPhone = !isTablet;
-        isTwoPanels = isTablet && useTwoPanels;
+        isTwoPanels = isTablet && useTwoPanels
+                && (isLandscape || FeatureFlags.ENABLE_TWO_PANEL_HOME_IN_PORTRAIT.get());
 
         aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
         boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
@@ -250,31 +263,31 @@
         mMetrics = context.getResources().getDisplayMetrics();
         final Resources res = context.getResources();
 
-        isTaskbarPresent = isTablet && FeatureFlags.ENABLE_TASKBAR.get();
-        if (isTaskbarPresent) {
-            // Taskbar will be added later, but provides bottom insets that we should subtract
-            // from availableHeightPx.
-            taskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_size);
-            WindowInsets windowInsets =
-                    context.createWindowContext(
-                            context.getSystemService(DisplayManager.class).getDisplay(mInfo.id),
-                            TYPE_APPLICATION, null)
-                    .getSystemService(WindowManager.class)
-                    .getCurrentWindowMetrics().getWindowInsets();
-            nonOverlappingTaskbarInset = taskbarSize - windowInsets.getSystemWindowInsetBottom();
-            if (nonOverlappingTaskbarInset > 0) {
-                nonFinalAvailableHeightPx -= nonOverlappingTaskbarInset;
+        if (isTwoPanels) {
+            if (isLandscape) {
+                mTypeIndex = InvariantDeviceProfile.INDEX_TWO_PANEL_LANDSCAPE;
+            } else {
+                mTypeIndex = InvariantDeviceProfile.INDEX_TWO_PANEL_PORTRAIT;
+            }
+        } else {
+            if (isLandscape) {
+                mTypeIndex = InvariantDeviceProfile.INDEX_LANDSCAPE;
+            } else {
+                mTypeIndex = InvariantDeviceProfile.INDEX_DEFAULT;
             }
         }
-        availableHeightPx = nonFinalAvailableHeightPx;
+
+        hotseatQsbHeight = res.getDimensionPixelSize(R.dimen.qsb_widget_height);
+        isTaskbarPresent = isTablet && ApiWrapper.TASKBAR_DRAWN_IN_PROCESS
+                && FeatureFlags.ENABLE_TASKBAR.get();
+        if (isTaskbarPresent) {
+            taskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_size);
+        }
 
         edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
 
-        desiredWorkspaceLeftRightMarginPx = isVerticalBarLayout() ? 0 : isScalableGrid
-                ? res.getDimensionPixelSize(R.dimen.scalable_grid_left_right_margin)
-                : res.getDimensionPixelSize(R.dimen.dynamic_grid_left_right_margin);
-        desiredWorkspaceLeftRightOriginalPx = desiredWorkspaceLeftRightMarginPx;
-
+        desiredWorkspaceHorizontalMarginPx = getHorizontalMarginPx(inv, res);
+        desiredWorkspaceHorizontalMarginOriginalPx = desiredWorkspaceHorizontalMarginPx;
 
         allAppsOpenVerticalTranslate = res.getDimensionPixelSize(
                 R.dimen.all_apps_open_vertical_translate);
@@ -284,9 +297,14 @@
                 res.getDimensionPixelSize(R.dimen.folder_content_padding_left_right);
         folderContentPaddingTop = res.getDimensionPixelSize(R.dimen.folder_content_padding_top);
 
-        setCellLayoutBorderSpacing(pxFromDp(inv.borderSpacing, mMetrics, 1f));
-        cellLayoutBorderSpacingOriginalPx = cellLayoutBorderSpacingPx;
-        folderCellLayoutBorderSpacingPx = cellLayoutBorderSpacingPx;
+        cellLayoutBorderSpacePx = getCellLayoutBorderSpace(inv);
+        allAppsCellSpacePx = new Point(
+                pxFromDp(inv.borderSpaces[InvariantDeviceProfile.INDEX_ALL_APPS].x, mMetrics, 1f),
+                pxFromDp(inv.borderSpaces[InvariantDeviceProfile.INDEX_ALL_APPS].y, mMetrics, 1f));
+        cellLayoutBorderSpaceOriginalPx = new Point(cellLayoutBorderSpacePx);
+        folderCellLayoutBorderSpaceOriginalPx = pxFromDp(inv.folderBorderSpace, mMetrics, 1f);
+        folderCellLayoutBorderSpacePx = new Point(folderCellLayoutBorderSpaceOriginalPx,
+                folderCellLayoutBorderSpaceOriginalPx);
 
         int cellLayoutPaddingLeftRightMultiplier = !isVerticalBarLayout() && isTablet
                 ? PORTRAIT_TABLET_LEFT_RIGHT_PADDING_MULTIPLIER : 1;
@@ -295,8 +313,7 @@
                 : res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding);
 
         if (isTwoPanels) {
-            cellLayoutPaddingLeftRightPx =
-                    res.getDimensionPixelSize(R.dimen.two_panel_home_side_padding);
+            cellLayoutPaddingLeftRightPx = 0;
             cellLayoutBottomPaddingPx = 0;
         } else if (isLandscape) {
             cellLayoutPaddingLeftRightPx = 0;
@@ -339,22 +356,49 @@
         hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0;
         hotseatExtraVerticalSize =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size);
-        updateHotseatIconSize(pxFromDp(inv.iconSize, mMetrics, 1f));
+        updateHotseatIconSize(
+                pxFromDp(inv.iconSize[InvariantDeviceProfile.INDEX_DEFAULT], mMetrics, 1f));
 
         qsbBottomMarginOriginalPx = isScalableGrid
                 ? res.getDimensionPixelSize(R.dimen.scalable_grid_qsb_bottom_margin)
                 : 0;
 
-        overviewTaskMarginPx = res.getDimensionPixelSize(R.dimen.overview_task_margin);
-        overviewTaskIconSizePx =
-                isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get() ? res.getDimensionPixelSize(
-                        R.dimen.task_thumbnail_icon_size_grid) : res.getDimensionPixelSize(
-                        R.dimen.task_thumbnail_icon_size);
+        overviewShowAsGrid = isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get();
+        overviewTaskMarginPx = overviewShowAsGrid
+                ? res.getDimensionPixelSize(R.dimen.overview_task_margin_focused)
+                : res.getDimensionPixelSize(R.dimen.overview_task_margin);
+        overviewTaskMarginGridPx = res.getDimensionPixelSize(R.dimen.overview_task_margin_grid);
+        overviewTaskIconSizePx = res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_size);
+        overviewTaskIconDrawableSizePx =
+                res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_drawable_size);
+        overviewTaskIconDrawableSizeGridPx =
+                res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_drawable_size_grid);
         overviewTaskThumbnailTopMarginPx = overviewTaskIconSizePx + overviewTaskMarginPx * 2;
-        overviewActionsMarginGesturePx = res.getDimensionPixelSize(
-                R.dimen.overview_actions_bottom_margin_gesture);
+        if (overviewShowAsGrid) {
+            if (isLandscape) {
+                overviewActionsTopMarginGesturePx = res.getDimensionPixelSize(
+                        R.dimen.overview_actions_top_margin_gesture_grid_landscape);
+                overviewActionsBottomMarginGesturePx = res.getDimensionPixelSize(
+                        R.dimen.overview_actions_bottom_margin_gesture_grid_landscape);
+            } else {
+                overviewActionsTopMarginGesturePx = res.getDimensionPixelSize(
+                        R.dimen.overview_actions_top_margin_gesture_grid_portrait);
+                overviewActionsBottomMarginGesturePx = res.getDimensionPixelSize(
+                        R.dimen.overview_actions_bottom_margin_gesture_grid_portrait);
+            }
+        } else {
+            overviewActionsTopMarginGesturePx = res.getDimensionPixelSize(
+                    R.dimen.overview_actions_margin_gesture);
+            overviewActionsBottomMarginGesturePx = overviewActionsTopMarginGesturePx;
+        }
         overviewActionsMarginThreeButtonPx = res.getDimensionPixelSize(
-                R.dimen.overview_actions_bottom_margin_three_button);
+                R.dimen.overview_actions_margin_three_button);
+        overviewPageSpacing = overviewShowAsGrid
+                ? res.getDimensionPixelSize(R.dimen.recents_page_spacing_grid)
+                : res.getDimensionPixelSize(R.dimen.recents_page_spacing);
+        overviewRowSpacing = isLandscape
+                ? res.getDimensionPixelSize(R.dimen.overview_grid_row_spacing_landscape)
+                : res.getDimensionPixelSize(R.dimen.overview_grid_row_spacing_portrait);
 
         // Calculate all of the remaining variables.
         extraSpace = updateAvailableDimensions(res);
@@ -420,6 +464,16 @@
                 new DotRenderer(allAppsIconSizePx, dotPath, DEFAULT_DOT_SIZE);
     }
 
+    private int getHorizontalMarginPx(InvariantDeviceProfile idp, Resources res) {
+        if (isVerticalBarLayout()) {
+            return 0;
+        }
+
+        return isScalableGrid
+                ? pxFromDp(idp.horizontalMargin[mTypeIndex], mMetrics)
+                : res.getDimensionPixelSize(R.dimen.dynamic_grid_left_right_margin);
+    }
+
     private void updateHotseatIconSize(int hotseatIconSizePx) {
         // Ensure there is enough space for folder icons, which have a slightly larger radius.
         hotseatCellHeightPx = (int) Math.ceil(hotseatIconSizePx * ICON_OVERLAP_FACTOR);
@@ -433,8 +487,24 @@
         }
     }
 
-    private void setCellLayoutBorderSpacing(int borderSpacing) {
-        cellLayoutBorderSpacingPx = isScalableGrid ? borderSpacing : 0;
+    private Point getCellLayoutBorderSpace(InvariantDeviceProfile idp) {
+        if (!isScalableGrid) {
+            return new Point(0, 0);
+        }
+
+        int horizontalSpacePx = pxFromDp(idp.borderSpaces[mTypeIndex].x, mMetrics);
+        int verticalSpacePx = pxFromDp(idp.borderSpaces[mTypeIndex].y, mMetrics);
+
+        return new Point(horizontalSpacePx, verticalSpacePx);
+    }
+
+    private Point getCellLayoutBorderSpaceScaled(InvariantDeviceProfile idp, float scale) {
+        Point original = getCellLayoutBorderSpace(idp);
+        return new Point((int) (original.x * scale), (int) (original.y * scale));
+    }
+
+    public Info getDisplayInfo() {
+        return mInfo;
     }
 
     /**
@@ -447,10 +517,10 @@
         // Check all sides to ensure that the widget won't overlap into another cell, or into
         // status bar.
         return workspaceTopPadding > widgetPadding.top
-                && cellLayoutBorderSpacingPx > widgetPadding.left
-                && cellLayoutBorderSpacingPx > widgetPadding.top
-                && cellLayoutBorderSpacingPx > widgetPadding.right
-                && cellLayoutBorderSpacingPx > widgetPadding.bottom;
+                && cellLayoutBorderSpacePx.x > widgetPadding.left
+                && cellLayoutBorderSpacePx.y > widgetPadding.top
+                && cellLayoutBorderSpacePx.x > widgetPadding.right
+                && cellLayoutBorderSpacePx.y > widgetPadding.bottom;
     }
 
     public Builder toBuilder(Context context) {
@@ -517,6 +587,17 @@
                 + textHeight + (topBottomPadding * 2);
     }
 
+    private void updateAllAppsWidth() {
+        if (isTwoPanels) {
+            int usedWidth = (allAppsCellWidthPx * numShownAllAppsColumns)
+                    + (allAppsCellSpacePx.x * (numShownAllAppsColumns + 1));
+            allAppsLeftRightPadding = Math.max(1, (availableWidthPx - usedWidth) / 2);
+        } else {
+            allAppsLeftRightPadding =
+                    desiredWorkspaceHorizontalMarginPx + cellLayoutPaddingLeftRightPx;
+        }
+    }
+
     /**
      * Returns the amount of extra (or unused) vertical space.
      */
@@ -537,9 +618,10 @@
             // We scale to fit the cellWidth and cellHeight in the available space.
             // The benefit of scalable grids is that we can get consistent aspect ratios between
             // devices.
-            float usedWidth = (cellWidthPx * inv.numColumns)
-                    + (cellLayoutBorderSpacingPx * (inv.numColumns - 1))
-                    + (desiredWorkspaceLeftRightMarginPx * 2);
+            int numColumns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns;
+            float usedWidth = (cellWidthPx * numColumns)
+                    + (cellLayoutBorderSpacePx.x * (numColumns - 1))
+                    + (desiredWorkspaceHorizontalMarginPx * 2);
             // We do not subtract padding here, as we also scale the workspace padding if needed.
             scaleX = availableWidthPx / usedWidth;
             shouldScale = true;
@@ -556,7 +638,7 @@
     }
 
     private int getCellLayoutHeight() {
-        return (cellHeightPx * inv.numRows) + (cellLayoutBorderSpacingPx * (inv.numRows - 1));
+        return (cellHeightPx * inv.numRows) + (cellLayoutBorderSpacePx.y * (inv.numRows - 1));
     }
 
     /**
@@ -569,24 +651,25 @@
         iconScale = Math.min(1f, scale);
         cellScaleToFit = scale;
 
-
         // Workspace
         final boolean isVerticalLayout = isVerticalBarLayout();
-        float invIconSizeDp = isLandscape ? inv.landscapeIconSize : inv.iconSize;
+        float invIconSizeDp = inv.iconSize[mTypeIndex];
+        float invIconTextSizeSp = inv.iconTextSize[mTypeIndex];
+
         iconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics, iconScale));
-        float invIconTextSizeSp = isLandscape ? inv.landscapeIconTextSize : inv.iconTextSize;
         iconTextSizePx = (int) (pxFromSp(invIconTextSizeSp, mMetrics) * iconScale);
         iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * iconScale);
 
-        setCellLayoutBorderSpacing((int) (cellLayoutBorderSpacingOriginalPx * scale));
+        cellLayoutBorderSpacePx = getCellLayoutBorderSpaceScaled(inv, scale);
 
         if (isScalableGrid) {
-            cellWidthPx = pxFromDp(inv.minCellWidth, mMetrics, scale);
-            cellHeightPx = pxFromDp(inv.minCellHeight, mMetrics, scale);
+            cellWidthPx = pxFromDp(inv.minCellSize[mTypeIndex].x, mMetrics, scale);
+            cellHeightPx = pxFromDp(inv.minCellSize[mTypeIndex].y, mMetrics, scale);
             int cellContentHeight = iconSizePx + iconDrawablePaddingPx
                     + Utilities.calculateTextHeight(iconTextSizePx);
             cellYPaddingPx = Math.max(0, cellHeightPx - cellContentHeight) / 2;
-            desiredWorkspaceLeftRightMarginPx = (int) (desiredWorkspaceLeftRightOriginalPx * scale);
+            desiredWorkspaceHorizontalMarginPx =
+                    (int) (desiredWorkspaceHorizontalMarginOriginalPx * scale);
         } else {
             cellWidthPx = iconSizePx + iconDrawablePaddingPx;
             cellHeightPx = (int) Math.ceil(iconSizePx * ICON_OVERLAP_FACTOR)
@@ -605,8 +688,10 @@
 
         // All apps
         if (numShownAllAppsColumns != inv.numColumns) {
-            allAppsIconSizePx = pxFromDp(inv.allAppsIconSize, mMetrics);
-            allAppsIconTextSizePx = pxFromSp(inv.allAppsIconTextSize, mMetrics);
+            allAppsIconSizePx =
+                    pxFromDp(inv.iconSize[InvariantDeviceProfile.INDEX_ALL_APPS], mMetrics);
+            allAppsIconTextSizePx =
+                    pxFromSp(inv.iconTextSize[InvariantDeviceProfile.INDEX_ALL_APPS], mMetrics);
             allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
             autoResizeAllAppsCells();
         } else {
@@ -616,6 +701,7 @@
             allAppsCellHeightPx = getCellSize().y;
         }
         allAppsCellWidthPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx;
+        updateAllAppsWidth();
 
         if (isVerticalLayout) {
             hideWorkspaceLabelsIfNotEnoughSpace();
@@ -652,14 +738,14 @@
 
         // Check if the icons fit within the available height.
         float contentUsedHeight = folderCellHeightPx * inv.numFolderRows
-                + ((inv.numFolderRows - 1) * folderCellLayoutBorderSpacingPx);
+                + ((inv.numFolderRows - 1) * folderCellLayoutBorderSpacePx.y);
         int contentMaxHeight = availableHeightPx - totalWorkspacePadding.y - folderBottomPanelSize
                 - folderMargin - folderContentPaddingTop;
         float scaleY = contentMaxHeight / contentUsedHeight;
 
         // Check if the icons fit within the available width.
         float contentUsedWidth = folderCellWidthPx * inv.numFolderColumns
-                + ((inv.numFolderColumns - 1) * folderCellLayoutBorderSpacingPx);
+                + ((inv.numFolderColumns - 1) * folderCellLayoutBorderSpacePx.x);
         int contentMaxWidth = availableWidthPx - totalWorkspacePadding.x - folderMargin
                 - folderContentPaddingLeftRight * 2;
         float scaleX = contentMaxWidth / contentUsedWidth;
@@ -671,9 +757,12 @@
     }
 
     private void updateFolderCellSize(float scale, Resources res) {
-        float invIconSizeDp = isVerticalBarLayout() ? inv.landscapeIconSize : inv.iconSize;
+        float invIconSizeDp = isVerticalBarLayout()
+                ? inv.iconSize[InvariantDeviceProfile.INDEX_LANDSCAPE]
+                : inv.iconSize[InvariantDeviceProfile.INDEX_DEFAULT];
         folderChildIconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics, scale));
-        folderChildTextSizePx = pxFromSp(inv.iconTextSize, mMetrics, scale);
+        folderChildTextSizePx =
+                pxFromSp(inv.iconTextSize[InvariantDeviceProfile.INDEX_DEFAULT], mMetrics, scale);
         folderLabelTextSizePx = (int) (folderChildTextSizePx * folderLabelTextScale);
 
         int textHeight = Utilities.calculateTextHeight(folderChildTextSizePx);
@@ -685,10 +774,10 @@
             folderCellWidthPx = (int) Math.max(minWidth, cellWidthPx * scale);
             folderCellHeightPx = (int) Math.max(minHeight, cellHeightPx * scale);
 
-            int borderSpacing = (int) (cellLayoutBorderSpacingOriginalPx * scale);
-            folderCellLayoutBorderSpacingPx = borderSpacing;
-            folderContentPaddingLeftRight = borderSpacing;
-            folderContentPaddingTop = borderSpacing;
+            int scaledSpace = (int) (folderCellLayoutBorderSpaceOriginalPx * scale);
+            folderCellLayoutBorderSpacePx = new Point(scaledSpace, scaledSpace);
+            folderContentPaddingLeftRight = scaledSpace;
+            folderContentPaddingTop = scaledSpace;
         } else {
             int cellPaddingX = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_x_padding)
                     * scale);
@@ -724,13 +813,18 @@
         if (result == null) {
             result = new Point();
         }
+
         // Since we are only concerned with the overall padding, layout direction does
         // not matter.
         Point padding = getTotalWorkspacePadding();
-        result.x = calculateCellWidth(availableWidthPx - padding.x
-                - cellLayoutPaddingLeftRightPx * 2, cellLayoutBorderSpacingPx, inv.numColumns);
+
+        int numColumns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns;
+        int cellLayoutTotalPadding =
+                isTwoPanels ? 4 * cellLayoutPaddingLeftRightPx : 2 * cellLayoutPaddingLeftRightPx;
+        int screenWidthPx = availableWidthPx - padding.x - cellLayoutTotalPadding;
+        result.x = calculateCellWidth(screenWidthPx, cellLayoutBorderSpacePx.x, numColumns);
         result.y = calculateCellHeight(availableHeightPx - padding.y
-                - cellLayoutBottomPaddingPx, cellLayoutBorderSpacingPx, inv.numRows);
+                - cellLayoutBottomPaddingPx, cellLayoutBorderSpacePx.y, inv.numRows);
         return result;
     }
 
@@ -757,38 +851,22 @@
                 padding.right = hotseatBarSizePx;
             }
         } else {
-            int hotseatTop = isTaskbarPresent ? taskbarSize : hotseatBarSizePx;
+            // Pad the bottom of the workspace with search/hotseat bar sizes
+            int hotseatTop = hotseatBarSizePx;
             int paddingBottom = hotseatTop + workspacePageIndicatorHeight
                     + workspaceBottomPadding - mWorkspacePageIndicatorOverlapWorkspace;
-            if (isTablet) {
-                // Pad the left and right of the workspace to ensure consistent spacing
-                // between all icons
-                // The amount of screen space available for left/right padding.
-                int availablePaddingX = Math.max(0, widthPx - ((inv.numColumns * cellWidthPx) +
-                        ((inv.numColumns - 1) * cellWidthPx)));
-                availablePaddingX = (int) Math.min(availablePaddingX,
-                        widthPx * MAX_HORIZONTAL_PADDING_PERCENT);
-                int hotseatVerticalPadding = isTaskbarPresent ? 0
-                        : hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx;
-                int availablePaddingY = Math.max(0, heightPx - edgeMarginPx - paddingBottom
-                        - (2 * inv.numRows * cellHeightPx) - hotseatVerticalPadding);
-                padding.set(availablePaddingX / 2, edgeMarginPx + availablePaddingY / 2,
-                        availablePaddingX / 2, paddingBottom + availablePaddingY / 2);
 
-                if (isTwoPanels) {
-                    padding.set(0, padding.top, 0, padding.bottom);
-                }
-            } else {
-                // Pad the top and bottom of the workspace with search/hotseat bar sizes
-                padding.set(desiredWorkspaceLeftRightMarginPx,
-                        workspaceTopPadding + (isScalableGrid ? 0 : edgeMarginPx),
-                        desiredWorkspaceLeftRightMarginPx,
-                        paddingBottom);
-            }
+            padding.set(desiredWorkspaceHorizontalMarginPx,
+                    workspaceTopPadding + (isScalableGrid ? 0 : edgeMarginPx),
+                    desiredWorkspaceHorizontalMarginPx,
+                    paddingBottom);
         }
     }
 
-    public Rect getHotseatLayoutPadding() {
+    /**
+     * Returns the padding for hotseat view
+     */
+    public Rect getHotseatLayoutPadding(Context context) {
         if (isVerticalBarLayout()) {
             if (isSeascape()) {
                 mHotseatPadding.set(mInsets.left + hotseatBarSidePaddingStartPx,
@@ -797,6 +875,30 @@
                 mHotseatPadding.set(hotseatBarSidePaddingEndPx, mInsets.top,
                         mInsets.right + hotseatBarSidePaddingStartPx, mInsets.bottom);
             }
+        } else if (isTaskbarPresent) {
+            int hotseatHeight = workspacePadding.bottom;
+            int taskbarOffset = getTaskbarOffsetY();
+            int hotseatTopDiff = hotseatHeight - taskbarOffset;
+
+            int endOffset = ApiWrapper.getHotseatEndOffset(context);
+            int requiredWidth = iconSizePx * numShownHotseatIcons;
+
+            Resources res = context.getResources();
+            float taskbarIconSize = res.getDimension(R.dimen.taskbar_icon_size);
+            float taskbarIconSpacing = 2 * res.getDimension(R.dimen.taskbar_icon_spacing);
+            int maxSize = (int) (requiredWidth
+                    * (taskbarIconSize + taskbarIconSpacing) / taskbarIconSize);
+            int hotseatSize = Math.min(maxSize, availableWidthPx - endOffset);
+            int sideSpacing = (availableWidthPx - hotseatSize) / 2;
+            mHotseatPadding.set(sideSpacing, hotseatTopDiff, sideSpacing, taskbarOffset);
+
+            if (endOffset > sideSpacing) {
+                int diff = Utilities.isRtl(context.getResources())
+                        ? sideSpacing - endOffset
+                        : endOffset - sideSpacing;
+                mHotseatPadding.left -= diff;
+                mHotseatPadding.right += diff;
+            }
         } else {
             // We want the edges of the hotseat to line up with the edges of the workspace, but the
             // icons in the hotseat are a different size, and so don't line up perfectly. To account
@@ -818,6 +920,30 @@
     }
 
     /**
+     * Returns the number of pixels the QSB is translated from the bottom of the screen.
+     */
+    public int getQsbOffsetY() {
+        int freeSpace = isTaskbarPresent
+                ? workspacePadding.bottom
+                : hotseatBarSizePx - hotseatCellHeightPx - hotseatQsbHeight;
+
+        if (isScalableGrid && qsbBottomMarginPx > mInsets.bottom) {
+            // Note that taskbarSize = 0 unless isTaskbarPresent.
+            return Math.min(qsbBottomMarginPx + taskbarSize, freeSpace);
+        } else {
+            return (int) (freeSpace * QSB_CENTER_FACTOR)
+                    + (isTaskbarPresent ? taskbarSize : mInsets.bottom);
+        }
+    }
+
+    /**
+     * Returns the number of pixels the taskbar is translated from the bottom of the screen.
+     */
+    public int getTaskbarOffsetY() {
+        return (getQsbOffsetY() - taskbarSize) / 2;
+    }
+
+    /**
      * @return the bounds for which the open folders should be contained within
      */
     public Rect getAbsoluteOpenFolderBounds() {
@@ -841,6 +967,7 @@
     public static int calculateCellWidth(int width, int borderSpacing, int countX) {
         return (width - ((countX - 1) * borderSpacing)) / countX;
     }
+
     public static int calculateCellHeight(int height, int borderSpacing, int countY) {
         return (height - ((countY - 1) * borderSpacing)) / countY;
     }
@@ -923,11 +1050,10 @@
 
         writer.println(prefix + "\tisScalableGrid:" + isScalableGrid);
 
-        writer.println(prefix + "\tinv.minCellWidth:" + inv.minCellWidth + "dp");
-        writer.println(prefix + "\tinv.minCellHeight:" + inv.minCellHeight + "dp");
+        writer.println(prefix + "\tinv.numColumns: " + inv.numColumns);
+        writer.println(prefix + "\tinv.numRows: " + inv.numRows);
 
-        writer.println(prefix + "\tinv.numColumns:" + inv.numColumns);
-        writer.println(prefix + "\tinv.numRows:" + inv.numRows);
+        writer.println(prefix + "\tminCellSize: " + inv.minCellSize[mTypeIndex] + "dp");
 
         writer.println(prefix + pxToDpStr("cellWidthPx", cellWidthPx));
         writer.println(prefix + pxToDpStr("cellHeightPx", cellHeightPx));
@@ -935,7 +1061,11 @@
         writer.println(prefix + pxToDpStr("getCellSize().x", getCellSize().x));
         writer.println(prefix + pxToDpStr("getCellSize().y", getCellSize().y));
 
-        writer.println(prefix + "\tinv.iconSize:" + inv.iconSize + "dp");
+        writer.println(prefix + pxToDpStr("cellLayoutBorderSpacePx Horizontal",
+                cellLayoutBorderSpacePx.x));
+        writer.println(prefix + pxToDpStr("cellLayoutBorderSpacePx Vertical",
+                cellLayoutBorderSpacePx.y));
+
         writer.println(prefix + pxToDpStr("iconSizePx", iconSizePx));
         writer.println(prefix + pxToDpStr("iconTextSizePx", iconTextSizePx));
         writer.println(prefix + pxToDpStr("iconDrawablePaddingPx", iconDrawablePaddingPx));
@@ -946,13 +1076,12 @@
         writer.println(prefix + pxToDpStr("folderChildTextSizePx", folderChildTextSizePx));
         writer.println(prefix + pxToDpStr("folderChildDrawablePaddingPx",
                 folderChildDrawablePaddingPx));
-        writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpacingPx",
-                folderCellLayoutBorderSpacingPx));
-
-        writer.println(prefix + pxToDpStr("cellLayoutBorderSpacingPx",
-                cellLayoutBorderSpacingPx));
-        writer.println(prefix + pxToDpStr("desiredWorkspaceLeftRightMarginPx",
-                desiredWorkspaceLeftRightMarginPx));
+        writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpaceOriginalPx",
+                folderCellLayoutBorderSpaceOriginalPx));
+        writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpacePx Horizontal",
+                folderCellLayoutBorderSpacePx.x));
+        writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpacePx Vertical",
+                folderCellLayoutBorderSpacePx.y));
 
         writer.println(prefix + pxToDpStr("allAppsIconSizePx", allAppsIconSizePx));
         writer.println(prefix + pxToDpStr("allAppsIconTextSizePx", allAppsIconTextSizePx));
@@ -972,11 +1101,11 @@
         writer.println(prefix + "\tnumShownHotseatIcons: " + numShownHotseatIcons);
 
         writer.println(prefix + "\tisTaskbarPresent:" + isTaskbarPresent);
-
+        writer.println(prefix + "\tisTaskbarPresentInApps:" + isTaskbarPresentInApps);
         writer.println(prefix + pxToDpStr("taskbarSize", taskbarSize));
-        writer.println(prefix + pxToDpStr("nonOverlappingTaskbarInset",
-                nonOverlappingTaskbarInset));
 
+        writer.println(prefix + pxToDpStr("desiredWorkspaceHorizontalMarginPx",
+                desiredWorkspaceHorizontalMarginPx));
         writer.println(prefix + pxToDpStr("workspacePadding.left", workspacePadding.left));
         writer.println(prefix + pxToDpStr("workspacePadding.top", workspacePadding.top));
         writer.println(prefix + pxToDpStr("workspacePadding.right", workspacePadding.right));
@@ -985,6 +1114,7 @@
         writer.println(prefix + pxToDpStr("iconScale", iconScale));
         writer.println(prefix + pxToDpStr("cellScaleToFit ", cellScaleToFit));
         writer.println(prefix + pxToDpStr("extraSpace", extraSpace));
+        writer.println(prefix + pxToDpStr("unscaled extraSpace", extraSpace / iconScale));
 
         if (inv.devicePaddings != null) {
             int unscaledExtraSpace = (int) (extraSpace / iconScale);
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index 88f6c49..eb42a65 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.View;
@@ -32,10 +33,13 @@
 import android.view.ViewPropertyAnimator;
 import android.widget.FrameLayout;
 
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragController.DragListener;
 import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.testing.TestProtocol;
 
 /*
  * The top bar containing various drop targets: Delete/App Info/Uninstall.
@@ -212,6 +216,9 @@
     }
 
     public void animateToVisibility(boolean isVisible) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_DROP_TARGET, "8");
+        }
         if (mVisible != isVisible) {
             mVisible = isVisible;
 
@@ -238,6 +245,9 @@
      */
     @Override
     public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_DROP_TARGET, "7");
+        }
         animateToVisibility(true);
     }
 
@@ -261,4 +271,12 @@
     public ButtonDropTarget[] getDropTargets() {
         return mDropTargets;
     }
+
+    @Override
+    protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+        if (TestProtocol.sDebugTracing && visibility == VISIBLE) {
+            Log.d(TestProtocol.NO_DROP_TARGET, "9");
+        }
+    }
 }
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index 21bc479..3b5b454 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -22,11 +22,9 @@
 import android.util.AttributeSet;
 import android.view.DragEvent;
 import android.view.KeyEvent;
-import android.view.View;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.EditText;
 
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.views.ActivityContext;
 
 
@@ -99,18 +97,6 @@
         }
     }
 
-    /**
-     * Sets whether EditText background should be visible
-     * @param maxAlpha defines the maximum alpha the background should animates to
-     */
-    public void setBackgroundVisibility(boolean visible, float maxAlpha) {}
-
-    /**
-     * Returns whether a visible background is set on EditText
-     */
-    public boolean getBackgroundVisibility() {
-        return getBackground() != null;
-    }
 
     public void showKeyboard() {
         mShowImeAfterFirstLayout = !showSoftInput();
@@ -150,15 +136,5 @@
         if (!TextUtils.isEmpty(getText())) {
             setText("");
         }
-        if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
-            return;
-        }
-        if (isFocused()) {
-            View nextFocus = focusSearch(View.FOCUS_DOWN);
-            if (nextFocus != null) {
-                nextFocus.requestFocus();
-            }
-        }
-        hideKeyboard();
     }
 }
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index eaca162..ffe3816 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -49,8 +49,6 @@
     private final View mQsb;
     private final int mQsbHeight;
 
-    private final int mTaskbarViewHeight;
-
     public Hotseat(Context context) {
         this(context, null);
     }
@@ -63,10 +61,9 @@
         super(context, attrs, defStyle);
 
         mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
-        mQsbHeight = mQsb.getLayoutParams().height;
         addView(mQsb);
 
-        mTaskbarViewHeight = context.getResources().getDimensionPixelSize(R.dimen.taskbar_size);
+        mQsbHeight = getResources().getDimensionPixelSize(R.dimen.qsb_widget_height);
     }
 
     /**
@@ -114,18 +111,13 @@
             lp.gravity = Gravity.BOTTOM;
             lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
             lp.height = (grid.isTaskbarPresent
-                        ? grid.workspacePadding.bottom
+                    ? grid.workspacePadding.bottom
                         : grid.hotseatBarSizePx)
                     + (grid.isTaskbarPresent ? grid.taskbarSize : insets.bottom);
         }
 
-        if (!grid.isTaskbarPresent) {
-            // When taskbar is present, we set the padding separately to ensure a seamless visual
-            // handoff between taskbar and hotseat during drag and drop.
-            Rect padding = grid.getHotseatLayoutPadding();
-            setPadding(padding.left, padding.top, padding.right, padding.bottom);
-        }
-
+        Rect padding = grid.getHotseatLayoutPadding(getContext());
+        setPadding(padding.left, padding.top, padding.right, padding.bottom);
         setLayoutParams(lp);
         InsettableFrameLayout.dispatchInsets(this, insets);
     }
@@ -159,7 +151,8 @@
             }
             return mWorkspace.onTouchEvent(event);
         }
-        return event.getY() > getCellHeight();
+        // Always let touch follow through to Workspace.
+        return false;
     }
 
     @Override
@@ -193,43 +186,22 @@
         int left = (r - l - qsbWidth) / 2;
         int right = left + qsbWidth;
 
-        int bottom = b - t - getQsbOffsetY();
+        int bottom = b - t - mActivity.getDeviceProfile().getQsbOffsetY();
         int top = bottom - mQsbHeight;
         mQsb.layout(left, top, right, bottom);
     }
 
     /**
-     * Returns the number of pixels the QSB is translated from the bottom of the screen.
-     */
-    private int getQsbOffsetY() {
-        DeviceProfile dp = mActivity.getDeviceProfile();
-        int freeSpace = dp.isTaskbarPresent
-                ? dp.workspacePadding.bottom
-                : dp.hotseatBarSizePx - dp.hotseatCellHeightPx - mQsbHeight;
-
-        if (dp.isScalableGrid && dp.qsbBottomMarginPx > dp.getInsets().bottom) {
-            return Math.min(dp.qsbBottomMarginPx, freeSpace);
-        } else {
-            return (int) (freeSpace * QSB_CENTER_FACTOR) + (dp.isTaskbarPresent
-                    ? dp.taskbarSize
-                    : dp.getInsets().bottom);
-        }
-    }
-
-    /**
-     * Returns the number of pixels the taskbar is translated from the bottom of the screen.
-     */
-    public int getTaskbarOffsetY() {
-        return (getQsbOffsetY() - mTaskbarViewHeight) / 2;
-    }
-
-    /**
      * Sets the alpha value of just our ShortcutAndWidgetContainer.
      */
     public void setIconsAlpha(float alpha) {
         getShortcutsAndWidgets().setAlpha(alpha);
     }
 
+    public float getIconsAlpha() {
+        return getShortcutsAndWidgets().getAlpha();
+    }
+
     /**
      * Returns the QSB inside hotseat
      */
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 29a0223..d844b87 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -17,7 +17,6 @@
 package com.android.launcher3;
 
 import static com.android.launcher3.Utilities.dpiFromPx;
-import static com.android.launcher3.Utilities.getPointString;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_TWO_PANEL_HOME;
 import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
 import static com.android.launcher3.util.DisplayController.CHANGE_SUPPORTED_BOUNDS;
@@ -32,6 +31,7 @@
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -45,7 +45,8 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.model.DeviceGridState;
+import com.android.launcher3.provider.RestoreDbTask;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.IntArray;
@@ -58,6 +59,7 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -68,12 +70,6 @@
     public static final MainThreadInitializedObject<InvariantDeviceProfile> INSTANCE =
             new MainThreadInitializedObject<>(InvariantDeviceProfile::new);
 
-    public static final String KEY_MIGRATION_SRC_WORKSPACE_SIZE = "migration_src_workspace_size";
-    public static final String KEY_MIGRATION_SRC_HOTSEAT_COUNT = "migration_src_hotseat_count";
-
-    private static final int DEFAULT_TRUE = -1;
-    private static final int DEFAULT_SPLIT_DISPLAY = 2;
-
     private static final String KEY_IDP_GRID_NAME = "idp_grid_name";
 
     private static final float ICON_SIZE_DEFINED_IN_APP_DP = 48;
@@ -86,6 +82,15 @@
     // used to offset float not being able to express extremely small weights in extreme cases.
     private static final float WEIGHT_EFFICIENT = 100000f;
 
+    // Used for arrays to specify different sizes (e.g. border spaces, width/height) in different
+    // constraints
+    static final int COUNT_SIZES = 5;
+    static final int INDEX_DEFAULT = 0;
+    static final int INDEX_LANDSCAPE = 1;
+    static final int INDEX_TWO_PANEL_PORTRAIT = 2;
+    static final int INDEX_TWO_PANEL_LANDSCAPE = 3;
+    static final int INDEX_ALL_APPS = 4;
+
     /**
      * Number of icons per row and column in the workspace.
      */
@@ -97,18 +102,18 @@
      */
     public int numFolderRows;
     public int numFolderColumns;
-    public float iconSize;
-    public float landscapeIconSize;
-    public float landscapeIconTextSize;
+    public float[] iconSize;
+    public float[] iconTextSize;
     public int iconBitmapSize;
     public int fillResIconDpi;
-    public float iconTextSize;
-    public float allAppsIconSize;
-    public float allAppsIconTextSize;
+    public boolean isSplitDisplay;
 
-    public float minCellHeight;
-    public float minCellWidth;
-    public float borderSpacing;
+    public PointF[] minCellSize;
+
+    public PointF[] borderSpaces;
+    public float folderBorderSpace;
+
+    public float[] horizontalMargin;
 
     private SparseArray<TypedValue> mExtraAttrs;
 
@@ -145,7 +150,8 @@
      */
     public List<DeviceProfile> supportedProfiles = Collections.EMPTY_LIST;
 
-    @Nullable public DevicePaddings devicePaddings;
+    @Nullable
+    public DevicePaddings devicePaddings;
 
     public Point defaultWallpaperSize;
     public Rect defaultWidgetPadding;
@@ -153,34 +159,7 @@
     private final ArrayList<OnIDPChangeListener> mChangeListeners = new ArrayList<>();
 
     @VisibleForTesting
-    public InvariantDeviceProfile() {}
-
-    private InvariantDeviceProfile(InvariantDeviceProfile p) {
-        numRows = p.numRows;
-        numColumns = p.numColumns;
-        numFolderRows = p.numFolderRows;
-        numFolderColumns = p.numFolderColumns;
-        iconSize = p.iconSize;
-        landscapeIconSize = p.landscapeIconSize;
-        iconBitmapSize = p.iconBitmapSize;
-        iconTextSize = p.iconTextSize;
-        landscapeIconTextSize = p.landscapeIconTextSize;
-        numShownHotseatIcons = p.numShownHotseatIcons;
-        numDatabaseHotseatIcons = p.numDatabaseHotseatIcons;
-        numAllAppsColumns = p.numAllAppsColumns;
-        numDatabaseAllAppsColumns = p.numDatabaseAllAppsColumns;
-        isScalable = p.isScalable;
-        devicePaddingId = p.devicePaddingId;
-        minCellHeight = p.minCellHeight;
-        minCellWidth = p.minCellWidth;
-        borderSpacing = p.borderSpacing;
-        dbFile = p.dbFile;
-        allAppsIconSize = p.allAppsIconSize;
-        allAppsIconTextSize = p.allAppsIconTextSize;
-        defaultLayoutId = p.defaultLayoutId;
-        demoModeLayoutId = p.demoModeLayoutId;
-        mExtraAttrs = p.mExtraAttrs;
-        devicePaddings = p.devicePaddings;
+    public InvariantDeviceProfile() {
     }
 
     @TargetApi(23)
@@ -190,12 +169,9 @@
         if (!newGridName.equals(gridName)) {
             Utilities.getPrefs(context).edit().putString(KEY_IDP_GRID_NAME, newGridName).apply();
         }
-        Utilities.getPrefs(context).edit()
-                .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, numDatabaseHotseatIcons)
-                .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, getPointString(numColumns, numRows))
-                .apply();
+        new DeviceGridState(this).writeToPrefs(context);
 
-        DisplayController.INSTANCE.get(context).addChangeListener(
+        DisplayController.INSTANCE.get(context).setPriorityListener(
                 (displayContext, info, flags) -> {
                     if ((flags & (CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS)) != 0) {
                         onConfigChanged(displayContext);
@@ -224,28 +200,52 @@
         // Get the display info based on default display and interpolate it to existing display
         DisplayOption defaultDisplayOption = invDistWeightedInterpolate(
                 DisplayController.INSTANCE.get(context).getInfo(),
-                getPredefinedDeviceProfiles(context, gridName, false), false);
+                getPredefinedDeviceProfiles(context, gridName, false, false), false);
 
         Info myInfo = new Info(context, display);
         DisplayOption myDisplayOption = invDistWeightedInterpolate(
-                myInfo, getPredefinedDeviceProfiles(context, gridName, false), false);
+                myInfo, getPredefinedDeviceProfiles(context, gridName, false, false), false);
 
         DisplayOption result = new DisplayOption(defaultDisplayOption.grid)
                 .add(myDisplayOption);
-        result.iconSize = defaultDisplayOption.iconSize;
-        result.landscapeIconSize = defaultDisplayOption.landscapeIconSize;
-        if (defaultDisplayOption.allAppsIconSize < myDisplayOption.allAppsIconSize) {
-            result.allAppsIconSize = defaultDisplayOption.allAppsIconSize;
-        } else {
-            result.allAppsIconSize = myDisplayOption.allAppsIconSize;
+        result.iconSizes[INDEX_DEFAULT] =
+                defaultDisplayOption.iconSizes[INDEX_DEFAULT];
+        for (int i = 1; i < COUNT_SIZES; i++) {
+            result.iconSizes[i] = Math.min(
+                    defaultDisplayOption.iconSizes[i], myDisplayOption.iconSizes[i]);
         }
-        result.minCellHeight = defaultDisplayOption.minCellHeight;
-        result.minCellWidth = defaultDisplayOption.minCellWidth;
-        result.borderSpacing = defaultDisplayOption.borderSpacing;
+
+        System.arraycopy(defaultDisplayOption.minCellSize, 0, result.minCellSize, 0,
+                COUNT_SIZES);
+        System.arraycopy(defaultDisplayOption.borderSpaces, 0, result.borderSpaces, 0,
+                COUNT_SIZES);
 
         initGrid(context, myInfo, result, false);
     }
 
+    /**
+     * Reinitialize the current grid after a restore, where some grids might now be disabled.
+     */
+    public void reinitializeAfterRestore(Context context) {
+        String currentDbFile = dbFile;
+        String gridName = getCurrentGridName(context);
+        String newGridName = initGrid(context, gridName);
+        if (!newGridName.equals(gridName)) {
+            Log.d(TAG, "Restored grid is disabled : " + gridName
+                    + ", migrating to: " + newGridName
+                    + ", removing all other grid db files");
+            for (String gridDbFile : LauncherFiles.GRID_DB_FILES) {
+                if (gridDbFile.equals(currentDbFile)) {
+                    continue;
+                }
+                if (context.getDatabasePath(gridDbFile).delete()) {
+                    Log.d(TAG, "Removed old grid db file: " + gridDbFile);
+                }
+            }
+            setCurrentGrid(context, gridName);
+        }
+    }
+
     public static String getCurrentGridName(Context context) {
         return Utilities.isGridOptionsEnabled(context)
                 ? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null) : null;
@@ -253,20 +253,14 @@
 
     private String initGrid(Context context, String gridName) {
         Info displayInfo = DisplayController.INSTANCE.get(context).getInfo();
-        // Determine if we have split display
-
-        boolean isTablet = false, isPhone = false;
-        for (WindowBounds bounds : displayInfo.supportedBounds) {
-            if (displayInfo.isTablet(bounds)) {
-                isTablet = true;
-            } else {
-                isPhone = true;
-            }
-        }
-        boolean isSplitDisplay = isPhone && isTablet && ENABLE_TWO_PANEL_HOME.get();
+        // Each screen has two profiles (portrait/landscape), so devices with four or more
+        // supported profiles implies two or more internal displays.
+        boolean isSplitDisplay =
+                displayInfo.supportedBounds.size() >= 4 && ENABLE_TWO_PANEL_HOME.get();
 
         ArrayList<DisplayOption> allOptions =
-                getPredefinedDeviceProfiles(context, gridName, isSplitDisplay);
+                getPredefinedDeviceProfiles(context, gridName, isSplitDisplay,
+                        RestoreDbTask.isPending(context));
         DisplayOption displayOption =
                 invDistWeightedInterpolate(displayInfo, allOptions, isSplitDisplay);
         initGrid(context, displayInfo, displayOption, isSplitDisplay);
@@ -287,19 +281,22 @@
         numFolderColumns = closestProfile.numFolderColumns;
         isScalable = closestProfile.isScalable;
         devicePaddingId = closestProfile.devicePaddingId;
+        this.isSplitDisplay = isSplitDisplay;
 
         mExtraAttrs = closestProfile.extraAttrs;
 
-        iconSize = displayOption.iconSize;
-        landscapeIconSize = displayOption.landscapeIconSize;
-        iconBitmapSize = ResourceUtils.pxFromDp(iconSize, metrics);
-        iconTextSize = displayOption.iconTextSize;
-        landscapeIconTextSize = displayOption.landscapeIconTextSize;
+        iconSize = displayOption.iconSizes;
+        iconBitmapSize = ResourceUtils.pxFromDp(iconSize[INDEX_DEFAULT], metrics);
         fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
 
-        minCellHeight = displayOption.minCellHeight;
-        minCellWidth = displayOption.minCellWidth;
-        borderSpacing = displayOption.borderSpacing;
+        iconTextSize = displayOption.textSizes;
+
+        minCellSize = displayOption.minCellSize;
+
+        borderSpaces = displayOption.borderSpaces;
+        folderBorderSpace = displayOption.folderBorderSpace;
+
+        horizontalMargin = displayOption.horizontalMargin;
 
         numShownHotseatIcons = closestProfile.numHotseatIcons;
         numDatabaseHotseatIcons = isSplitDisplay
@@ -309,12 +306,9 @@
         numDatabaseAllAppsColumns = isSplitDisplay
                 ? closestProfile.numDatabaseAllAppsColumns : closestProfile.numAllAppsColumns;
 
-        if (Utilities.isGridOptionsEnabled(context)) {
-            allAppsIconSize = displayOption.allAppsIconSize;
-            allAppsIconTextSize = displayOption.allAppsIconTextSize;
-        } else {
-            allAppsIconSize = iconSize;
-            allAppsIconTextSize = iconTextSize;
+        if (!Utilities.isGridOptionsEnabled(context)) {
+            iconSize[INDEX_ALL_APPS] = iconSize[INDEX_DEFAULT];
+            iconTextSize[INDEX_ALL_APPS] = iconTextSize[INDEX_DEFAULT];
         }
 
         if (devicePaddingId != 0) {
@@ -372,20 +366,31 @@
         MAIN_EXECUTOR.execute(() -> onConfigChanged(appContext));
     }
 
+    private Object[] toModelState() {
+        return new Object[]{
+                numColumns, numRows, numDatabaseHotseatIcons, iconBitmapSize, fillResIconDpi,
+                numDatabaseAllAppsColumns, dbFile};
+    }
+
     private void onConfigChanged(Context context) {
+        Object[] oldState = toModelState();
+
         // Re-init grid
         String gridName = getCurrentGridName(context);
         initGrid(context, gridName);
 
+        boolean modelPropsChanged = !Arrays.equals(oldState, toModelState());
         for (OnIDPChangeListener listener : mChangeListeners) {
-            listener.onIdpChanged(this);
+            listener.onIdpChanged(modelPropsChanged);
         }
     }
 
     private static ArrayList<DisplayOption> getPredefinedDeviceProfiles(
-            Context context, String gridName, boolean isSplitDisplay) {
+            Context context, String gridName, boolean isSplitDisplay, boolean allowDisabledGrid) {
         ArrayList<DisplayOption> profiles = new ArrayList<>();
-        try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
+        int xmlResource = isSplitDisplay ? R.xml.device_profiles_split : R.xml.device_profiles;
+
+        try (XmlResourceParser parser = context.getResources().getXml(xmlResource)) {
             final int depth = parser.getDepth();
             int type;
             while (((type = parser.next()) != XmlPullParser.END_TAG ||
@@ -393,28 +398,31 @@
                 if ((type == XmlPullParser.START_TAG)
                         && GridOption.TAG_NAME.equals(parser.getName())) {
 
-                    GridOption gridOption = new GridOption(context, Xml.asAttributeSet(parser));
-                    final int displayDepth = parser.getDepth();
-                    while (((type = parser.next()) != XmlPullParser.END_TAG ||
-                            parser.getDepth() > displayDepth)
-                            && type != XmlPullParser.END_DOCUMENT) {
-                        if ((type == XmlPullParser.START_TAG) && "display-option".equals(
-                                parser.getName())) {
-                            profiles.add(new DisplayOption(gridOption, context,
-                                    Xml.asAttributeSet(parser),
-                                    isSplitDisplay ? DEFAULT_SPLIT_DISPLAY : DEFAULT_TRUE));
+                    GridOption gridOption =
+                            new GridOption(context, Xml.asAttributeSet(parser), isSplitDisplay);
+                    if (gridOption.isEnabled || allowDisabledGrid) {
+                        final int displayDepth = parser.getDepth();
+                        while (((type = parser.next()) != XmlPullParser.END_TAG
+                                || parser.getDepth() > displayDepth)
+                                && type != XmlPullParser.END_DOCUMENT) {
+                            if ((type == XmlPullParser.START_TAG) && "display-option".equals(
+                                    parser.getName())) {
+                                profiles.add(new DisplayOption(gridOption, context,
+                                        Xml.asAttributeSet(parser)));
+                            }
                         }
                     }
                 }
             }
-        } catch (IOException|XmlPullParserException e) {
+        } catch (IOException | XmlPullParserException e) {
             throw new RuntimeException(e);
         }
 
         ArrayList<DisplayOption> filteredProfiles = new ArrayList<>();
         if (!TextUtils.isEmpty(gridName)) {
             for (DisplayOption option : profiles) {
-                if (gridName.equals(option.grid.name)) {
+                if (gridName.equals(option.grid.name)
+                        && (option.grid.isEnabled || allowDisabledGrid)) {
                     filteredProfiles.add(option);
                 }
             }
@@ -433,9 +441,37 @@
         return filteredProfiles;
     }
 
+    /**
+     * @return all the grid options that can be shown on the device
+     */
+    public List<GridOption> parseAllGridOptions(Context context) {
+        List<GridOption> result = new ArrayList<>();
+        int xmlResource = isSplitDisplay ? R.xml.device_profiles_split : R.xml.device_profiles;
+
+        try (XmlResourceParser parser = context.getResources().getXml(xmlResource)) {
+            final int depth = parser.getDepth();
+            int type;
+            while (((type = parser.next()) != XmlPullParser.END_TAG
+                    || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+                if ((type == XmlPullParser.START_TAG)
+                        && GridOption.TAG_NAME.equals(parser.getName())) {
+                    GridOption option =
+                            new GridOption(context, Xml.asAttributeSet(parser), isSplitDisplay);
+                    if (option.isEnabled) {
+                        result.add(option);
+                    }
+                }
+            }
+        } catch (IOException | XmlPullParserException e) {
+            Log.e(TAG, "Error parsing device profile", e);
+            return Collections.emptyList();
+        }
+        return result;
+    }
+
     private int getLauncherIconDensity(int requiredSize) {
         // Densities typically defined by an app.
-        int[] densityBuckets = new int[] {
+        int[] densityBuckets = new int[]{
                 DisplayMetrics.DENSITY_LOW,
                 DisplayMetrics.DENSITY_MEDIUM,
                 DisplayMetrics.DENSITY_TV,
@@ -502,37 +538,51 @@
                 Float.compare(dist(width, height, a.minWidthDps, a.minHeightDps),
                         dist(width, height, b.minWidthDps, b.minHeightDps)));
 
-        GridOption closestOption = points.get(0).grid;
+        DisplayOption closestPoint = points.get(0);
+        GridOption closestOption = closestPoint.grid;
         float weights = 0;
 
-        DisplayOption p = points.get(0);
-        if (dist(width, height, p.minWidthDps, p.minHeightDps) == 0) {
-            return p;
+        if (dist(width, height, closestPoint.minWidthDps, closestPoint.minHeightDps) == 0) {
+            return closestPoint;
         }
 
         DisplayOption out = new DisplayOption(closestOption);
         for (int i = 0; i < points.size() && i < KNEARESTNEIGHBOR; ++i) {
-            p = points.get(i);
+            DisplayOption p = points.get(i);
             float w = weight(width, height, p.minWidthDps, p.minHeightDps, WEIGHT_POWER);
             weights += w;
             out.add(new DisplayOption().add(p).multiply(w));
         }
-        return out.multiply(1.0f / weights);
+        out.multiply(1.0f / weights);
+
+        // Since the bitmaps are persisted, ensure that the default bitmap size is same as
+        // predefined size to avoid cache invalidation
+        out.iconSizes[INDEX_DEFAULT] =
+                closestPoint.iconSizes[INDEX_DEFAULT];
+        for (int i = INDEX_DEFAULT + 1; i < COUNT_SIZES; i++) {
+            out.iconSizes[i] = Math.min(out.iconSizes[i],
+                    out.iconSizes[INDEX_DEFAULT]);
+        }
+
+        return out;
     }
 
     public DeviceProfile getDeviceProfile(Context context) {
         Resources res = context.getResources();
         Configuration config = context.getResources().getConfiguration();
 
-        float availableWidth = config.screenWidthDp * res.getDisplayMetrics().density;
-        float availableHeight = config.screenHeightDp * res.getDisplayMetrics().density;
+        float screenWidth = config.screenWidthDp * res.getDisplayMetrics().density;
+        float screenHeight = config.screenHeightDp * res.getDisplayMetrics().density;
+        return getBestMatch(screenWidth, screenHeight);
+    }
 
+    public DeviceProfile getBestMatch(float screenWidth, float screenHeight) {
         DeviceProfile bestMatch = supportedProfiles.get(0);
         float minDiff = Float.MAX_VALUE;
 
         for (DeviceProfile profile : supportedProfiles) {
-            float diff = Math.abs(profile.availableWidthPx - availableWidth)
-                    + Math.abs(profile.availableHeightPx - availableHeight);
+            float diff = Math.abs(profile.widthPx - screenWidth)
+                    + Math.abs(profile.heightPx - screenHeight);
             if (diff < minDiff) {
                 minDiff = diff;
                 bestMatch = profile;
@@ -561,8 +611,8 @@
         // We will use these two data points to extrapolate how much the wallpaper parallax effect
         // to span (ie travel) at any aspect ratio:
 
-        final float ASPECT_RATIO_LANDSCAPE = 16/10f;
-        final float ASPECT_RATIO_PORTRAIT = 10/16f;
+        final float ASPECT_RATIO_LANDSCAPE = 16 / 10f;
+        final float ASPECT_RATIO_PORTRAIT = 10 / 16f;
         final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f;
         final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f;
 
@@ -572,7 +622,8 @@
         //   (10/16)x + y = 1.2
         // We solve for x and y and end up with a final formula:
         final float x =
-                (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) /
+                (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE
+                        - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) /
                         (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT);
         final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT;
         return x * aspectRatio + y;
@@ -583,7 +634,7 @@
         /**
          * Called when the device provide changes
          */
-        void onIdpChanged(InvariantDeviceProfile profile);
+        void onIdpChanged(boolean modelPropertiesChanged);
     }
 
 
@@ -594,6 +645,7 @@
         public final String name;
         public final int numRows;
         public final int numColumns;
+        public final boolean isEnabled;
 
         private final int numFolderRows;
         private final int numFolderColumns;
@@ -613,7 +665,7 @@
 
         private final SparseArray<TypedValue> extraAttrs;
 
-        public GridOption(Context context, AttributeSet attrs) {
+        public GridOption(Context context, AttributeSet attrs, boolean isSplitDisplay) {
             TypedArray a = context.obtainStyledAttributes(
                     attrs, R.styleable.GridDisplayOption);
             name = a.getString(R.styleable.GridDisplayOption_name);
@@ -621,8 +673,10 @@
             numColumns = a.getInt(R.styleable.GridDisplayOption_numColumns, 0);
 
             dbFile = a.getString(R.styleable.GridDisplayOption_dbFile);
-            defaultLayoutId = a.getResourceId(
-                    R.styleable.GridDisplayOption_defaultLayoutId, 0);
+            defaultLayoutId = a.getResourceId(isSplitDisplay && a.hasValue(
+                    R.styleable.GridDisplayOption_defaultSplitDisplayLayoutId)
+                    ? R.styleable.GridDisplayOption_defaultSplitDisplayLayoutId
+                    : R.styleable.GridDisplayOption_defaultLayoutId, 0);
             demoModeLayoutId = a.getResourceId(
                     R.styleable.GridDisplayOption_demoModeLayoutId, defaultLayoutId);
 
@@ -646,6 +700,8 @@
             devicePaddingId = a.getResourceId(
                     R.styleable.GridDisplayOption_devicePaddingId, 0);
 
+            isEnabled = a.getBoolean(R.styleable.GridDisplayOption_gridEnabled, true);
+
             a.recycle();
             extraAttrs = Themes.createValueMap(context, attrs,
                     IntArray.wrap(R.styleable.GridDisplayOption));
@@ -661,44 +717,119 @@
         private final float minHeightDps;
         private final boolean canBeDefault;
 
-        private float minCellHeight;
-        private float minCellWidth;
-        private float borderSpacing;
+        private final PointF[] minCellSize = new PointF[COUNT_SIZES];
 
-        private float iconSize;
-        private float iconTextSize;
-        private float landscapeIconSize;
-        private float landscapeIconTextSize;
-        private float allAppsIconSize;
-        private float allAppsIconTextSize;
+        private float folderBorderSpace;
+        private final PointF[] borderSpaces = new PointF[COUNT_SIZES];
+        private final float[] horizontalMargin = new float[COUNT_SIZES];
 
-        DisplayOption(GridOption grid, Context context, AttributeSet attrs, int defaultFlagValue) {
+        private final float[] iconSizes = new float[COUNT_SIZES];
+        private final float[] textSizes = new float[COUNT_SIZES];
+
+        DisplayOption(GridOption grid, Context context, AttributeSet attrs) {
             this.grid = grid;
 
-            TypedArray a = context.obtainStyledAttributes(
-                    attrs, R.styleable.ProfileDisplayOption);
+            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ProfileDisplayOption);
 
             minWidthDps = a.getFloat(R.styleable.ProfileDisplayOption_minWidthDps, 0);
             minHeightDps = a.getFloat(R.styleable.ProfileDisplayOption_minHeightDps, 0);
 
-            canBeDefault = a.getInt(R.styleable.ProfileDisplayOption_canBeDefault, 0)
-                    == defaultFlagValue;
+            canBeDefault = a.getBoolean(R.styleable.ProfileDisplayOption_canBeDefault, false);
 
-            minCellHeight = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightDps, 0);
-            minCellWidth = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthDps, 0);
-            borderSpacing = a.getFloat(R.styleable.ProfileDisplayOption_borderSpacingDps, 0);
+            float x;
+            float y;
 
-            iconSize = a.getFloat(R.styleable.ProfileDisplayOption_iconImageSize, 0);
-            landscapeIconSize = a.getFloat(R.styleable.ProfileDisplayOption_landscapeIconSize,
-                    iconSize);
-            iconTextSize = a.getFloat(R.styleable.ProfileDisplayOption_iconTextSize, 0);
-            landscapeIconTextSize = a.getFloat(
-                    R.styleable.ProfileDisplayOption_landscapeIconTextSize, iconTextSize);
+            x = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthDps, 0);
+            y = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightDps, 0);
+            minCellSize[INDEX_DEFAULT] = new PointF(x, y);
+            minCellSize[INDEX_LANDSCAPE] = new PointF(x, y);
+            minCellSize[INDEX_ALL_APPS] = new PointF(x, y);
 
-            allAppsIconSize = a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconSize,
-                    iconSize);
-            allAppsIconTextSize = a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconTextSize,
-                    iconTextSize);
+            x = a.getFloat(R.styleable.ProfileDisplayOption_twoPanelPortraitMinCellWidthDps,
+                    minCellSize[INDEX_DEFAULT].x);
+            y = a.getFloat(R.styleable.ProfileDisplayOption_twoPanelPortraitMinCellHeightDps,
+                    minCellSize[INDEX_DEFAULT].y);
+            minCellSize[INDEX_TWO_PANEL_PORTRAIT] = new PointF(x, y);
+
+            x = a.getFloat(R.styleable.ProfileDisplayOption_twoPanelLandscapeMinCellWidthDps,
+                    minCellSize[INDEX_DEFAULT].x);
+            y = a.getFloat(R.styleable.ProfileDisplayOption_twoPanelLandscapeMinCellHeightDps,
+                    minCellSize[INDEX_DEFAULT].y);
+            minCellSize[INDEX_TWO_PANEL_LANDSCAPE] = new PointF(x, y);
+
+            float borderSpace = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceDps, 0);
+            float twoPanelPortraitBorderSpaceDps = a.getFloat(
+                    R.styleable.ProfileDisplayOption_twoPanelPortraitBorderSpaceDps, borderSpace);
+            float twoPanelLandscapeBorderSpaceDps = a.getFloat(
+                    R.styleable.ProfileDisplayOption_twoPanelLandscapeBorderSpaceDps, borderSpace);
+
+            x = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceHorizontalDps, borderSpace);
+            y = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceVerticalDps, borderSpace);
+            borderSpaces[INDEX_DEFAULT] = new PointF(x, y);
+            borderSpaces[INDEX_LANDSCAPE] = new PointF(x, y);
+
+            x = a.getFloat(
+                    R.styleable.ProfileDisplayOption_twoPanelPortraitBorderSpaceHorizontalDps,
+                    twoPanelPortraitBorderSpaceDps);
+            y = a.getFloat(
+                    R.styleable.ProfileDisplayOption_twoPanelPortraitBorderSpaceVerticalDps,
+                    twoPanelPortraitBorderSpaceDps);
+            borderSpaces[INDEX_TWO_PANEL_PORTRAIT] = new PointF(x, y);
+
+            x = a.getFloat(
+                    R.styleable.ProfileDisplayOption_twoPanelLandscapeBorderSpaceHorizontalDps,
+                    twoPanelLandscapeBorderSpaceDps);
+            y = a.getFloat(
+                    R.styleable.ProfileDisplayOption_twoPanelLandscapeBorderSpaceVerticalDps,
+                    twoPanelLandscapeBorderSpaceDps);
+            borderSpaces[INDEX_TWO_PANEL_LANDSCAPE] = new PointF(x, y);
+
+            x = y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellSpacingDps,
+                    borderSpace);
+            borderSpaces[INDEX_ALL_APPS] = new PointF(x, y);
+            folderBorderSpace = borderSpace;
+
+            iconSizes[INDEX_DEFAULT] =
+                    a.getFloat(R.styleable.ProfileDisplayOption_iconImageSize, 0);
+            iconSizes[INDEX_LANDSCAPE] =
+                    a.getFloat(R.styleable.ProfileDisplayOption_landscapeIconSize,
+                            iconSizes[INDEX_DEFAULT]);
+            iconSizes[INDEX_ALL_APPS] =
+                    a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconSize,
+                            iconSizes[INDEX_DEFAULT]);
+            iconSizes[INDEX_TWO_PANEL_PORTRAIT] =
+                    a.getFloat(R.styleable.ProfileDisplayOption_twoPanelPortraitIconSize,
+                            iconSizes[INDEX_DEFAULT]);
+            iconSizes[INDEX_TWO_PANEL_LANDSCAPE] =
+                    a.getFloat(R.styleable.ProfileDisplayOption_twoPanelLandscapeIconSize,
+                            iconSizes[INDEX_DEFAULT]);
+
+            textSizes[INDEX_DEFAULT] =
+                    a.getFloat(R.styleable.ProfileDisplayOption_iconTextSize, 0);
+            textSizes[INDEX_LANDSCAPE] =
+                    a.getFloat(R.styleable.ProfileDisplayOption_landscapeIconTextSize,
+                            textSizes[INDEX_DEFAULT]);
+            textSizes[INDEX_ALL_APPS] =
+                    a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconTextSize,
+                            textSizes[INDEX_DEFAULT]);
+            textSizes[INDEX_TWO_PANEL_PORTRAIT] =
+                    a.getFloat(R.styleable.ProfileDisplayOption_twoPanelPortraitIconTextSize,
+                            textSizes[INDEX_DEFAULT]);
+            textSizes[INDEX_TWO_PANEL_LANDSCAPE] =
+                    a.getFloat(R.styleable.ProfileDisplayOption_twoPanelLandscapeIconTextSize,
+                            textSizes[INDEX_DEFAULT]);
+
+            horizontalMargin[INDEX_DEFAULT] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_horizontalMargin, 0);
+            horizontalMargin[INDEX_LANDSCAPE] = horizontalMargin[INDEX_DEFAULT];
+            horizontalMargin[INDEX_ALL_APPS] = horizontalMargin[INDEX_DEFAULT];
+            horizontalMargin[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_twoPanelLandscapeHorizontalMargin,
+                    horizontalMargin[INDEX_DEFAULT]);
+            horizontalMargin[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_twoPanelPortraitHorizontalMargin,
+                    horizontalMargin[INDEX_DEFAULT]);
+
             a.recycle();
         }
 
@@ -711,34 +842,43 @@
             minWidthDps = 0;
             minHeightDps = 0;
             canBeDefault = false;
-            minCellHeight = 0;
-            minCellWidth = 0;
-            borderSpacing = 0;
+            for (int i = 0; i < COUNT_SIZES; i++) {
+                iconSizes[i] = 0;
+                textSizes[i] = 0;
+                borderSpaces[i] = new PointF();
+                minCellSize[i] = new PointF();
+            }
         }
 
         private DisplayOption multiply(float w) {
-            iconSize *= w;
-            landscapeIconSize *= w;
-            allAppsIconSize *= w;
-            iconTextSize *= w;
-            landscapeIconTextSize *= w;
-            allAppsIconTextSize *= w;
-            minCellHeight *= w;
-            minCellWidth *= w;
-            borderSpacing *= w;
+            for (int i = 0; i < COUNT_SIZES; i++) {
+                iconSizes[i] *= w;
+                textSizes[i] *= w;
+                borderSpaces[i].x *= w;
+                borderSpaces[i].y *= w;
+                minCellSize[i].x *= w;
+                minCellSize[i].y *= w;
+                horizontalMargin[i] *= w;
+            }
+
+            folderBorderSpace *= w;
+
             return this;
         }
 
         private DisplayOption add(DisplayOption p) {
-            iconSize += p.iconSize;
-            landscapeIconSize += p.landscapeIconSize;
-            allAppsIconSize += p.allAppsIconSize;
-            iconTextSize += p.iconTextSize;
-            landscapeIconTextSize += p.landscapeIconTextSize;
-            allAppsIconTextSize += p.allAppsIconTextSize;
-            minCellHeight += p.minCellHeight;
-            minCellWidth += p.minCellWidth;
-            borderSpacing += p.borderSpacing;
+            for (int i = 0; i < COUNT_SIZES; i++) {
+                iconSizes[i] += p.iconSizes[i];
+                textSizes[i] += p.textSizes[i];
+                borderSpaces[i].x += p.borderSpaces[i].x;
+                borderSpaces[i].y += p.borderSpaces[i].y;
+                minCellSize[i].x += p.minCellSize[i].x;
+                minCellSize[i].y += p.minCellSize[i].y;
+                horizontalMargin[i] += p.horizontalMargin[i];
+            }
+
+            folderBorderSpace += p.folderBorderSpace;
+
             return this;
         }
     }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5fb862e..8d92bf2 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -23,9 +23,11 @@
 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.launcher3.AbstractFloatingView.TYPE_FOLDER;
 import static com.android.launcher3.AbstractFloatingView.TYPE_ICON_SURFACE;
 import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
 import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR;
+import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
@@ -48,7 +50,6 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RECONFIGURED;
 import static com.android.launcher3.model.ItemInstallQueue.FLAG_ACTIVITY_PAUSED;
 import static com.android.launcher3.model.ItemInstallQueue.FLAG_DRAG_AND_DROP;
-import static com.android.launcher3.model.ItemInstallQueue.FLAG_LOADER_RUNNING;
 import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
 import static com.android.launcher3.popup.SystemShortcut.INSTALL;
 import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
@@ -80,6 +81,7 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.CancellationSignal;
@@ -87,6 +89,7 @@
 import android.os.Process;
 import android.os.StrictMode;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.text.TextUtils;
 import android.text.method.TextKeyListener;
 import android.util.Log;
@@ -106,6 +109,7 @@
 import android.widget.Toast;
 
 import androidx.annotation.CallSuper;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
 import androidx.annotation.VisibleForTesting;
@@ -117,6 +121,7 @@
 import com.android.launcher3.allapps.AllAppsStore;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.allapps.DiscoveryBounce;
+import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.anim.PropertyListBuilder;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
@@ -166,6 +171,7 @@
 import com.android.launcher3.util.ActivityTracker;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
@@ -173,6 +179,7 @@
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.PendingRequestArgs;
+import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.Themes;
@@ -248,8 +255,6 @@
     protected static final int REQUEST_LAST = 100;
 
     // Type: int
-    private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
-    // Type: int
     private static final String RUNTIME_STATE = "launcher.state";
     // Type: PendingRequestArgs
     private static final String RUNTIME_STATE_PENDING_REQUEST_ARGS = "launcher.request_args";
@@ -259,6 +264,8 @@
     private static final String RUNTIME_STATE_PENDING_ACTIVITY_RESULT = "launcher.activity_result";
     // Type: SparseArray<Parcelable>
     private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel";
+    // Type int[]
+    private static final String RUNTIME_STATE_CURRENT_SCREEN_IDS = "launcher.current_screen_ids";
 
     public static final String ON_CREATE_EVT = "Launcher.onCreate";
     public static final String ON_START_EVT = "Launcher.onStart";
@@ -276,6 +283,11 @@
 
     private static final int THEME_CROSS_FADE_ANIMATION_DURATION = 375;
 
+    private static final String DISPLAY_WORKSPACE_TRACE_METHOD_NAME = "DisplayWorkspaceFirstFrame";
+    private static final String DISPLAY_ALL_APPS_TRACE_METHOD_NAME = "DisplayAllApps";
+    public static final int DISPLAY_WORKSPACE_TRACE_COOKIE = 0;
+    public static final int DISPLAY_ALL_APPS_TRACE_COOKIE = 1;
+
     private Configuration mOldConfig;
 
     @Thunk
@@ -322,8 +334,8 @@
 
     private PopupDataProvider mPopupDataProvider;
 
-    private int mSynchronouslyBoundPage = PagedView.INVALID_PAGE;
-    private int mPageToBindSynchronously = PagedView.INVALID_PAGE;
+    private IntSet mSynchronouslyBoundPages = new IntSet();
+    @NonNull private IntSet mPagesToBindSynchronously = new IntSet();
 
     // We only want to get the SharedPreferences once since it does an FS stat each time we get
     // it from the context.
@@ -362,7 +374,15 @@
     private LauncherState mPrevLauncherState;
 
     @Override
+    @TargetApi(Build.VERSION_CODES.S)
     protected void onCreate(Bundle savedInstanceState) {
+        // Only use a hard-coded cookie since we only want to trace this once.
+        if (Utilities.ATLEAST_S) {
+            Trace.beginAsyncSection(
+                    DISPLAY_WORKSPACE_TRACE_METHOD_NAME, DISPLAY_WORKSPACE_TRACE_COOKIE);
+            Trace.beginAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME,
+                    DISPLAY_ALL_APPS_TRACE_COOKIE);
+        }
         Object traceToken = TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT,
                 TraceHelper.FLAG_UI_EVENT);
         if (DEBUG_STRICT_MODE) {
@@ -458,13 +478,12 @@
         restoreState(savedInstanceState);
         mStateManager.reapplyState();
 
-        // We only load the page synchronously if the user rotates (or triggers a
-        // configuration change) while launcher is in the foreground
-        int currentScreen = PagedView.INVALID_PAGE;
         if (savedInstanceState != null) {
-            currentScreen = savedInstanceState.getInt(RUNTIME_STATE_CURRENT_SCREEN, currentScreen);
+            int[] pageIds = savedInstanceState.getIntArray(RUNTIME_STATE_CURRENT_SCREEN_IDS);
+            if (pageIds != null) {
+                mPagesToBindSynchronously = IntSet.wrap(pageIds);
+            }
         }
-        mPageToBindSynchronously = currentScreen;
 
         if (!mModel.addCallbacksAndLoad(this)) {
             if (!internalStateHandled) {
@@ -552,10 +571,18 @@
     }
 
     @Override
+    public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) {
+        super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig);
+        // Always update device profile when multi window mode changed.
+        initDeviceProfile(mDeviceProfile.inv);
+        dispatchDeviceProfileChanged();
+    }
+
+    @Override
     public void onConfigurationChanged(Configuration newConfig) {
         int diff = newConfig.diff(mOldConfig);
         if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
-            onIdpChanged(mDeviceProfile.inv);
+            onIdpChanged(false);
         }
 
         mOldConfig.setTo(newConfig);
@@ -563,8 +590,8 @@
     }
 
     @Override
-    public void onIdpChanged(InvariantDeviceProfile idp) {
-        initDeviceProfile(idp);
+    public void onIdpChanged(boolean modelPropertiesChanged) {
+        initDeviceProfile(mDeviceProfile.inv);
         dispatchDeviceProfileChanged();
         reapplyUi();
         mDragLayer.recreateControllers();
@@ -596,7 +623,7 @@
         }
 
         onDeviceProfileInitiated();
-        mModelWriter = mModel.getWriter(getDeviceProfile().isVerticalBarLayout(), true);
+        mModelWriter = mModel.getWriter(getDeviceProfile().isVerticalBarLayout(), true, this);
     }
 
     public RotationHelper getRotationHelper() {
@@ -868,11 +895,11 @@
         if (dropLayout == null) {
             // it's possible that the add screen was removed because it was
             // empty and a re-bind occurred
-            mWorkspace.addExtraEmptyScreen();
-            return mWorkspace.commitExtraEmptyScreen();
-        } else {
-            return screenId;
+            mWorkspace.addExtraEmptyScreens();
+            IntSet emptyPagesAdded = mWorkspace.commitExtraEmptyScreens();
+            return emptyPagesAdded.isEmpty() ? -1 : emptyPagesAdded.getArray().get(0);
         }
+        return screenId;
     }
 
     @Thunk
@@ -1192,7 +1219,7 @@
         // Until the workspace is bound, ensure that we keep the wallpaper offset locked to the
         // default state, otherwise we will update to the wrong offsets in RTL
         mWorkspace.lockWallpaperToDefaultPage();
-        mWorkspace.bindAndInitFirstWorkspaceScreen(null /* recycled qsb */);
+        mWorkspace.bindAndInitFirstWorkspaceScreen();
         mDragController.addDragListener(mWorkspace);
 
         // Get the search/delete/uninstall bar
@@ -1239,7 +1266,7 @@
      *
      * @param data The intent describing the shortcut.
      */
-    private void completeAddShortcut(Intent data, int container, int screenId, int cellX,
+    protected void completeAddShortcut(Intent data, int container, int screenId, int cellX,
             int cellY, PendingRequestArgs args) {
         if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT
                 || args.getPendingIntent().getComponent() == null) {
@@ -1294,7 +1321,7 @@
             }
 
             if (!foundCellSpan) {
-                mWorkspace.onNoCellFound(layout);
+                mWorkspace.onNoCellFound(layout, info, /* logInstanceId= */ null);
                 return;
             }
 
@@ -1312,7 +1339,8 @@
         }
     }
 
-    public FolderIcon findFolderIcon(final int folderIconId) {
+    @Override
+    public @Nullable FolderIcon findFolderIcon(final int folderIconId) {
         return (FolderIcon) mWorkspace.getHomescreenIconByItemId(folderIconId);
     }
 
@@ -1363,22 +1391,7 @@
             final LauncherAppWidgetHostView launcherHostView = (LauncherAppWidgetHostView) hostView;
             CellLayout cellLayout = getCellLayout(launcherInfo.container, launcherInfo.screenId);
             if (mStateManager.getState() == NORMAL) {
-                // Show resize frame once the widget layout is drawn.
-                View.OnLayoutChangeListener onLayoutChangeListener =
-                        new View.OnLayoutChangeListener() {
-                            @Override
-                            public void onLayoutChange(View view, int left, int top, int right,
-                                    int bottom, int oldLeft, int oldTop, int oldRight,
-                                    int oldBottom) {
-                                AppWidgetResizeFrame.showForWidget(launcherHostView, cellLayout);
-                                launcherHostView.removeOnLayoutChangeListener(this);
-                            }
-                        };
-                launcherHostView.addOnLayoutChangeListener(onLayoutChangeListener);
-                // There is a small chance that the layout was already drawn before the layout
-                // change listener was registered, which means that the resize frame wouldn't be
-                // shown. Directly call requestLayout to force a layout change.
-                launcherHostView.requestLayout();
+                AppWidgetResizeFrame.showForWidget(launcherHostView, cellLayout);
             } else {
                 mStateManager.addStateListener(new StateManager.StateListener<LauncherState>() {
                     @Override
@@ -1587,18 +1600,22 @@
     @Override
     public void onRestoreInstanceState(Bundle state) {
         super.onRestoreInstanceState(state);
-        mWorkspace.restoreInstanceStateForChild(mSynchronouslyBoundPage);
+        if (mSynchronouslyBoundPages != null) {
+            mSynchronouslyBoundPages.forEach(screenId -> {
+                int pageIndex = mWorkspace.getPageIndexForScreenId(screenId);
+                if (pageIndex != PagedView.INVALID_PAGE) {
+                    mWorkspace.restoreInstanceStateForChild(pageIndex);
+                }
+            });
+        }
     }
 
     @Override
     protected void onSaveInstanceState(Bundle outState) {
-        if (mWorkspace.getChildCount() > 0) {
-            outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getNextPage());
-
-        }
+        outState.putIntArray(RUNTIME_STATE_CURRENT_SCREEN_IDS,
+                mWorkspace.getCurrentPageScreenIds().getArray().toArray());
         outState.putInt(RUNTIME_STATE, mStateManager.getState().ordinal);
 
-
         AbstractFloatingView widgets = AbstractFloatingView
                 .getOpenView(this, AbstractFloatingView.TYPE_WIDGETS_FULL_SHEET);
         if (widgets != null) {
@@ -1921,7 +1938,7 @@
 
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
-        TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Key event", event);
+        TestLogging.recordKeyEvent(TestProtocol.SEQUENCE_MAIN, "Key event", event);
         return (event.getKeyCode() == KeyEvent.KEYCODE_HOME) || super.dispatchKeyEvent(event);
     }
 
@@ -2077,25 +2094,58 @@
     }
 
     /**
-     * Sets the next page to bind synchronously on next bind.
-     * @param page
+     * Sets the next pages to bind synchronously on next bind.
+     * @param pages should not be null.
      */
-    public void setPageToBindSynchronously(int page) {
-        mPageToBindSynchronously = page;
+    public void setPagesToBindSynchronously(@NonNull IntSet pages) {
+        mPagesToBindSynchronously = pages;
     }
 
-    /**
-     * Implementation of the method from LauncherModel.Callbacks.
-     */
     @Override
-    public int getPageToBindSynchronously() {
-        if (mPageToBindSynchronously != PagedView.INVALID_PAGE) {
-            return mPageToBindSynchronously;
-        } else  if (mWorkspace != null) {
-            return mWorkspace.getCurrentPage();
+    public IntSet getPagesToBindSynchronously(IntArray orderedScreenIds) {
+        IntSet visibleIds;
+        if (!mPagesToBindSynchronously.isEmpty()) {
+            visibleIds = mPagesToBindSynchronously;
+        } else if (!mWorkspaceLoading) {
+            visibleIds = mWorkspace.getCurrentPageScreenIds();
         } else {
-            return 0;
+            // If workspace binding is still in progress, getCurrentPageScreenIds won't be accurate,
+            // and we should use mSynchronouslyBoundPages that's set during initial binding.
+            visibleIds = mSynchronouslyBoundPages;
         }
+        IntArray actualIds = new IntArray();
+
+        IntSet result = new IntSet();
+        if (visibleIds.isEmpty()) {
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.NULL_INT_SET, "getPagesToBindSynchronously (1): "
+                        + result);
+            }
+            return result;
+        }
+        for (int id : orderedScreenIds.toArray()) {
+            actualIds.add(id);
+        }
+        int firstId = visibleIds.getArray().get(0);
+        int pairId = mWorkspace.getScreenPair(firstId);
+        // Double check that actual screenIds contains the visibleId, as empty screens are hidden
+        // in single panel.
+        if (actualIds.contains(firstId)) {
+            result.add(firstId);
+            if (mDeviceProfile.isTwoPanels && actualIds.contains(pairId)) {
+                result.add(pairId);
+            }
+        } else if (LauncherAppState.getIDP(this).supportedProfiles.stream().anyMatch(
+                deviceProfile -> deviceProfile.isTwoPanels) && actualIds.contains(pairId)) {
+            // Add the right panel if left panel is hidden when switching display, due to empty
+            // pages being hidden in single panel.
+            result.add(pairId);
+        }
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NULL_INT_SET, "getPagesToBindSynchronously (2): "
+                    + result);
+        }
+        return result;
     }
 
     /**
@@ -2105,7 +2155,7 @@
     @Override
     public void clearPendingBinds() {
         if (mPendingExecutor != null) {
-            mPendingExecutor.markCompleted();
+            mPendingExecutor.cancel();
             mPendingExecutor = null;
 
             // We might have set this flag previously and forgot to clear it.
@@ -2143,14 +2193,14 @@
 
     @Override
     public void bindScreens(IntArray orderedScreenIds) {
-        // Make sure the first screen is always at the start.
+        int firstScreenPosition = 0;
         if (FeatureFlags.QSB_ON_FIRST_SCREEN &&
-                orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) {
+                orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != firstScreenPosition) {
             orderedScreenIds.removeValue(Workspace.FIRST_SCREEN_ID);
-            orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID);
+            orderedScreenIds.add(firstScreenPosition, Workspace.FIRST_SCREEN_ID);
         } else if (!FeatureFlags.QSB_ON_FIRST_SCREEN && orderedScreenIds.isEmpty()) {
             // If there are no screens, we need to have an empty screen
-            mWorkspace.addExtraEmptyScreen();
+            mWorkspace.addExtraEmptyScreens();
         }
         bindAddScreens(orderedScreenIds);
 
@@ -2161,13 +2211,22 @@
     }
 
     private void bindAddScreens(IntArray orderedScreenIds) {
+        if (mDeviceProfile.isTwoPanels) {
+            // Some empty pages might have been removed while the phone was in a single panel
+            // mode, so we want to add those empty pages back.
+            IntSet screenIds = IntSet.wrap(orderedScreenIds);
+            orderedScreenIds.forEach(screenId -> screenIds.add(mWorkspace.getScreenPair(screenId)));
+            orderedScreenIds = screenIds.getArray();
+        }
+
         int count = orderedScreenIds.size();
         for (int i = 0; i < count; i++) {
             int screenId = orderedScreenIds.get(i);
-            if (!FeatureFlags.QSB_ON_FIRST_SCREEN || screenId != Workspace.FIRST_SCREEN_ID) {
+            if (FeatureFlags.QSB_ON_FIRST_SCREEN && screenId == Workspace.FIRST_SCREEN_ID) {
                 // No need to bind the first screen, as its always bound.
-                mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(screenId);
+                continue;
             }
+            mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(screenId);
         }
     }
 
@@ -2187,6 +2246,9 @@
             ArrayList<ItemInfo> addAnimated) {
         // Add the new screens
         if (newScreens != null) {
+            // newScreens can contain an empty right panel that is already bound, but not known
+            // by BgDataModel.
+            newScreens.removeAllValues(mWorkspace.mScreenOrder);
             bindAddScreens(newScreens);
         }
 
@@ -2228,7 +2290,7 @@
             final boolean focusFirstItemForAccessibility) {
         // Get the list of added items and intersect them with the set of items here
         final Collection<Animator> bounceAnims = new ArrayList<>();
-        final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation();
+        boolean canAnimatePageChange = canAnimatePageChange();
         Workspace workspace = mWorkspace;
         int newItemsScreenId = -1;
         int end = items.size();
@@ -2289,7 +2351,7 @@
                 }
             }
             workspace.addInScreenFromBind(view, item);
-            if (animateIcons) {
+            if (forceAnimateIcons) {
                 // Animate all the applications up now
                 view.setAlpha(0f);
                 view.setScaleX(0f);
@@ -2305,7 +2367,7 @@
 
         View viewToFocus = newView;
         // Animate to the correct pager
-        if (animateIcons && newItemsScreenId > -1) {
+        if (forceAnimateIcons && newItemsScreenId > -1) {
             AnimatorSet anim = new AnimatorSet();
             anim.playTogether(bounceAnims);
             if (focusFirstItemForAccessibility && viewToFocus != null) {
@@ -2321,7 +2383,7 @@
             final int newScreenIndex = mWorkspace.getPageIndexForScreenId(newItemsScreenId);
             final Runnable startBounceAnimRunnable = anim::start;
 
-            if (newItemsScreenId != currentScreenId) {
+            if (canAnimatePageChange && newItemsScreenId != currentScreenId) {
                 // We post the animation slightly delayed to prevent slowdowns
                 // when we are loading right after we return to launcher.
                 mWorkspace.postDelayed(new Runnable() {
@@ -2532,25 +2594,6 @@
         return info;
     }
 
-    public void onPageBoundSynchronously(int page) {
-        mSynchronouslyBoundPage = page;
-        mWorkspace.setCurrentPage(page);
-        mPageToBindSynchronously = PagedView.INVALID_PAGE;
-    }
-
-    @Override
-    public void executeOnNextDraw(ViewOnDrawExecutor executor) {
-        clearPendingBinds();
-        mPendingExecutor = executor;
-        if (!isInState(ALL_APPS)) {
-            mAppsView.getAppsStore().enableDeferUpdates(AllAppsStore.DEFER_UPDATES_NEXT_DRAW);
-            mPendingExecutor.execute(() -> mAppsView.getAppsStore().disableDeferUpdates(
-                    AllAppsStore.DEFER_UPDATES_NEXT_DRAW));
-        }
-
-        executor.attachTo(this);
-    }
-
     public void clearPendingExecutor(ViewOnDrawExecutor executor) {
         if (mPendingExecutor == executor) {
             mPendingExecutor = null;
@@ -2558,22 +2601,33 @@
     }
 
     @Override
-    public void finishFirstPageBind(final ViewOnDrawExecutor executor) {
+    @TargetApi(Build.VERSION_CODES.S)
+    public void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks) {
+        mSynchronouslyBoundPages = boundPages;
+        mPagesToBindSynchronously = new IntSet();
+
+        clearPendingBinds();
+        ViewOnDrawExecutor executor = new ViewOnDrawExecutor(pendingTasks);
+        mPendingExecutor = executor;
+        if (!isInState(ALL_APPS)) {
+            mAppsView.getAppsStore().enableDeferUpdates(AllAppsStore.DEFER_UPDATES_NEXT_DRAW);
+            pendingTasks.add(() -> mAppsView.getAppsStore().disableDeferUpdates(
+                    AllAppsStore.DEFER_UPDATES_NEXT_DRAW));
+        }
+
         AlphaProperty property = mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD);
         if (property.getValue() < 1) {
             ObjectAnimator anim = ObjectAnimator.ofFloat(property, MultiValueAlpha.VALUE, 1);
-            if (executor != null) {
-                anim.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        executor.onLoadAnimationCompleted();
-                    }
-                });
-            }
+            anim.addListener(AnimatorListeners.forEndCallback(executor::onLoadAnimationCompleted));
             anim.start();
-        } else if (executor != null) {
+        } else {
             executor.onLoadAnimationCompleted();
         }
+        executor.attachTo(this);
+        if (Utilities.ATLEAST_S) {
+            Trace.endAsyncSection(DISPLAY_WORKSPACE_TRACE_METHOD_NAME,
+                    DISPLAY_WORKSPACE_TRACE_COOKIE);
+        }
     }
 
     /**
@@ -2581,7 +2635,7 @@
      *
      * Implementation of the method from LauncherModel.Callbacks.
      */
-    public void finishBindingItems(int pageBoundFirst) {
+    public void finishBindingItems(IntSet pagesBoundFirst) {
         Object traceToken = TraceHelper.INSTANCE.beginSection("finishBindingItems");
         mWorkspace.restoreInstanceStateForRemainingPages();
 
@@ -2593,14 +2647,14 @@
             mPendingActivityResult = null;
         }
 
-        ItemInstallQueue.INSTANCE.get(this)
-                .resumeModelPush(FLAG_LOADER_RUNNING);
-
+        int currentPage = pagesBoundFirst != null && !pagesBoundFirst.isEmpty()
+                ? mWorkspace.getPageIndexForScreenId(pagesBoundFirst.getArray().get(0))
+                : PagedView.INVALID_PAGE;
         // When undoing the removal of the last item on a page, return to that page.
         // Since we are just resetting the current page without user interaction,
         // override the previous page so we don't log the page switch.
-        mWorkspace.setCurrentPage(pageBoundFirst, pageBoundFirst /* overridePrevPage */);
-        mPageToBindSynchronously = PagedView.INVALID_PAGE;
+        mWorkspace.setCurrentPage(currentPage, currentPage /* overridePrevPage */);
+        mPagesToBindSynchronously = new IntSet();
 
         // Cache one page worth of icons
         getViewCache().setCacheSize(R.layout.folder_application,
@@ -2610,7 +2664,7 @@
         TraceHelper.INSTANCE.endSection(traceToken);
     }
 
-    private boolean canRunNewAppsAnimation() {
+    private boolean canAnimatePageChange() {
         if (mDragController.isDragging()) {
             return false;
         } else {
@@ -2637,9 +2691,14 @@
      * Implementation of the method from LauncherModel.Callbacks.
      */
     @Override
+    @TargetApi(Build.VERSION_CODES.S)
     public void bindAllApplications(AppInfo[] apps, int flags) {
         mAppsView.getAppsStore().setApps(apps, flags);
         PopupContainerWithArrow.dismissInvalidPopup(this);
+        if (Utilities.ATLEAST_S) {
+            Trace.endAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME,
+                    DISPLAY_ALL_APPS_TRACE_COOKIE);
+        }
     }
 
     /**
@@ -2670,7 +2729,7 @@
     @Override
     public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) {
         if (!updated.isEmpty()) {
-            mWorkspace.updateShortcuts(updated);
+            mWorkspace.updateWorkspaceItems(updated, this);
             PopupContainerWithArrow.dismissInvalidPopup(this);
         }
     }
@@ -2682,7 +2741,7 @@
      */
     @Override
     public void bindRestoreItemsChange(HashSet<ItemInfo> updates) {
-        mWorkspace.updateRestoreItems(updates);
+        mWorkspace.updateRestoreItems(updates, this);
     }
 
     /**
@@ -2834,13 +2893,39 @@
                 if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
                     Log.d(TestProtocol.PERMANENT_DIAG_TAG, "Opening options popup on key up");
                 }
-                OptionsPopupView.showDefaultOptions(this, -1, -1);
+                showDefaultOptions(-1, -1);
             }
             return true;
         }
         return super.onKeyUp(keyCode, event);
     }
 
+    /**
+     * Shows the default options popup
+     */
+    public void showDefaultOptions(float x, float y) {
+        OptionsPopupView.show(this, getPopupTarget(x, y), OptionsPopupView.getOptions(this),
+                false);
+    }
+
+    /**
+     * Returns target rectangle for anchoring a popup menu.
+     */
+    protected RectF getPopupTarget(float x, float y) {
+        float halfSize = getResources().getDimension(R.dimen.options_menu_thumb_size) / 2;
+        if (x < 0 || y < 0) {
+            x = mDragLayer.getWidth() / 2;
+            y = mDragLayer.getHeight() / 2;
+        }
+        return new RectF(x - halfSize, y - halfSize, x + halfSize, y + halfSize);
+    }
+
+    @Override
+    public boolean shouldUseColorExtractionForPopup() {
+        return getTopOpenViewWithType(this, TYPE_FOLDER) == null
+                && getStateManager().getState() != LauncherState.ALL_APPS;
+    }
+
     @Override
     protected void collectStateHandlers(List<StateHandler> out) {
         out.add(getAllAppsController());
@@ -2884,13 +2969,6 @@
         return new float[] {NO_SCALE, NO_OFFSET};
     }
 
-    /**
-     * @see LauncherState#getTaskbarScale(Launcher)
-     */
-    public float getNormalTaskbarScale() {
-        return 1f;
-    }
-
     public static Launcher getLauncher(Context context) {
         return fromContext(context);
     }
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 3d6be69..10023b4 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -48,10 +48,9 @@
 import com.android.launcher3.util.SettingsCache;
 import com.android.launcher3.util.SimpleBroadcastReceiver;
 import com.android.launcher3.util.Themes;
-import com.android.launcher3.widget.DatabaseWidgetPreviewLoader;
 import com.android.launcher3.widget.custom.CustomWidgetManager;
 
-public class LauncherAppState {
+public class LauncherAppState implements SafeCloseable {
 
     public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher";
     private static final String KEY_ICON_STATE = "pref_icon_shape_path";
@@ -64,7 +63,6 @@
     private final LauncherModel mModel;
     private final IconProvider mIconProvider;
     private final IconCache mIconCache;
-    private final DatabaseWidgetPreviewLoader mWidgetCache;
     private final InvariantDeviceProfile mInvariantDeviceProfile;
     private final RunnableList mOnTerminateCallback = new RunnableList();
 
@@ -85,7 +83,11 @@
         Log.v(Launcher.TAG, "LauncherAppState initiated");
         Preconditions.assertUIThread();
 
-        mInvariantDeviceProfile.addOnChangeListener(idp -> refreshAndReloadLauncher());
+        mInvariantDeviceProfile.addOnChangeListener(modelPropertiesChanged -> {
+            if (modelPropertiesChanged) {
+                refreshAndReloadLauncher();
+            }
+        });
 
         mContext.getSystemService(LauncherApps.class).registerCallback(mModel);
 
@@ -136,11 +138,11 @@
         mContext = context;
 
         mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context);
-        mIconProvider =  new IconProvider(context, Themes.isThemedIconEnabled(context));
+        mIconProvider = new IconProvider(context, Themes.isThemedIconEnabled(context));
         mIconCache = new IconCache(mContext, mInvariantDeviceProfile,
                 iconCacheFileName, mIconProvider);
-        mWidgetCache = new DatabaseWidgetPreviewLoader(mContext, mIconCache);
-        mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext));
+        mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext),
+                iconCacheFileName != null);
         mOnTerminateCallback.add(mIconCache::close);
     }
 
@@ -155,14 +157,14 @@
         LauncherIcons.clearPool();
         mIconCache.updateIconParams(
                 mInvariantDeviceProfile.fillResIconDpi, mInvariantDeviceProfile.iconBitmapSize);
-        mWidgetCache.refresh();
         mModel.forceReload();
     }
 
     /**
      * Call from Application.onTerminate(), which is not guaranteed to ever be called.
      */
-    public void onTerminate() {
+    @Override
+    public void close() {
         mModel.destroy();
         mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel);
         CustomWidgetManager.INSTANCE.get(mContext).setWidgetRefreshCallback(null);
@@ -181,10 +183,6 @@
         return mModel;
     }
 
-    public DatabaseWidgetPreviewLoader getWidgetCache() {
-        return mWidgetCache;
-    }
-
     public InvariantDeviceProfile getInvariantDeviceProfile() {
         return mInvariantDeviceProfile;
     }
diff --git a/src/com/android/launcher3/LauncherBackupAgent.java b/src/com/android/launcher3/LauncherBackupAgent.java
index 140794b..dc533f0 100644
--- a/src/com/android/launcher3/LauncherBackupAgent.java
+++ b/src/com/android/launcher3/LauncherBackupAgent.java
@@ -31,6 +31,6 @@
 
     @Override
     public void onRestoreFinished() {
-        RestoreDbTask.setPending(this, true);
+        RestoreDbTask.setPending(this);
     }
 }
diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java
index 6c0daa4..64f1d95 100644
--- a/src/com/android/launcher3/LauncherFiles.java
+++ b/src/com/android/launcher3/LauncherFiles.java
@@ -1,5 +1,6 @@
 package com.android.launcher3;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -29,16 +30,24 @@
     public static final String WIDGET_PREVIEWS_DB = "widgetpreviews.db";
     public static final String APP_ICONS_DB = "app_icons.db";
 
-    public static final List<String> ALL_FILES = Collections.unmodifiableList(Arrays.asList(
+    public static final List<String> GRID_DB_FILES = Collections.unmodifiableList(Arrays.asList(
             LAUNCHER_DB,
             LAUNCHER_4_BY_5_DB,
             LAUNCHER_4_BY_4_DB,
             LAUNCHER_3_BY_3_DB,
-            LAUNCHER_2_BY_2_DB,
+            LAUNCHER_2_BY_2_DB));
+
+    public static final List<String> OTHER_FILES = Collections.unmodifiableList(Arrays.asList(
             BACKUP_DB,
             SHARED_PREFERENCES_KEY + XML,
             WIDGET_PREVIEWS_DB,
             MANAGED_USER_PREFERENCES_KEY + XML,
             DEVICE_PREFERENCES_KEY + XML,
             APP_ICONS_DB));
+
+    public static final List<String> ALL_FILES = Collections.unmodifiableList(
+            new ArrayList<String>() {{
+                addAll(GRID_DB_FILES);
+                addAll(OTHER_FILES);
+            }});
 }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 545f4c3..ee6f51e 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -60,6 +60,7 @@
 import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.shortcuts.ShortcutRequest;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.PackageUserKey;
@@ -96,9 +97,10 @@
     // our monitoring of the package manager provides all updates and we never
     // need to do a requery. This is only ever touched from the loader thread.
     private boolean mModelLoaded;
+    private boolean mModelDestroyed = false;
     public boolean isModelLoaded() {
         synchronized (mLock) {
-            return mModelLoaded && mLoaderTask == null;
+            return mModelLoaded && mLoaderTask == null && !mModelDestroyed;
         }
     }
 
@@ -125,10 +127,12 @@
         }
     };
 
-    LauncherModel(Context context, LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
+    LauncherModel(Context context, LauncherAppState app, IconCache iconCache, AppFilter appFilter,
+            boolean isPrimaryInstance) {
         mApp = app;
         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
-        mModelDelegate = ModelDelegate.newInstance(context, app, mBgAllAppsList, mBgDataModel);
+        mModelDelegate = ModelDelegate.newInstance(context, app, mBgAllAppsList, mBgDataModel,
+                isPrimaryInstance);
     }
 
     public ModelDelegate getModelDelegate() {
@@ -145,9 +149,10 @@
         enqueueModelUpdateTask(new AddWorkspaceItemsTask(itemList));
     }
 
-    public ModelWriter getWriter(boolean hasVerticalHotseat, boolean verifyChanges) {
+    public ModelWriter getWriter(boolean hasVerticalHotseat, boolean verifyChanges,
+            @Nullable Callbacks owner) {
         return new ModelWriter(mApp.getContext(), this, mBgDataModel,
-                hasVerticalHotseat, verifyChanges);
+                hasVerticalHotseat, verifyChanges, owner);
     }
 
     @Override
@@ -244,6 +249,7 @@
      * Called when the model is destroyed
      */
     public void destroy() {
+        mModelDestroyed = true;
         MODEL_EXECUTOR.execute(mModelDelegate::destroy);
     }
 
@@ -330,7 +336,7 @@
     public boolean addCallbacksAndLoad(Callbacks callbacks) {
         synchronized (mLock) {
             addCallbacks(callbacks);
-            return startLoader();
+            return startLoader(new Callbacks[] { callbacks });
 
         }
     }
@@ -341,6 +347,12 @@
     public void addCallbacks(Callbacks callbacks) {
         Preconditions.assertUIThread();
         synchronized (mCallbacksList) {
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.NULL_INT_SET, "addCallbacks pointer: "
+                        + callbacks
+                        + ", name: "
+                        + callbacks.getClass().getName(), new Exception());
+            }
             mCallbacksList.add(callbacks);
         }
     }
@@ -350,26 +362,32 @@
      * @return true if the page could be bound synchronously.
      */
     public boolean startLoader() {
+        return startLoader(new Callbacks[0]);
+    }
+
+    private boolean startLoader(Callbacks[] newCallbacks) {
         // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
         ItemInstallQueue.INSTANCE.get(mApp.getContext())
                 .pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING);
         synchronized (mLock) {
-            // Don't bother to start the thread if we know it's not going to do anything
-            final Callbacks[] callbacksList = getCallbacks();
+            // If there is already one running, tell it to stop.
+            boolean wasRunning = stopLoader();
+            boolean bindDirectly = mModelLoaded && !mIsLoaderTaskRunning;
+            boolean bindAllCallbacks = wasRunning || !bindDirectly || newCallbacks.length == 0;
+            final Callbacks[] callbacksList = bindAllCallbacks ? getCallbacks() : newCallbacks;
+
             if (callbacksList.length > 0) {
                 // Clear any pending bind-runnables from the synchronized load process.
                 for (Callbacks cb : callbacksList) {
                     MAIN_EXECUTOR.execute(cb::clearPendingBinds);
                 }
 
-                // If there is already one running, tell it to stop.
-                stopLoader();
                 LoaderResults loaderResults = new LoaderResults(
                         mApp, mBgDataModel, mBgAllAppsList, callbacksList);
-                if (mModelLoaded && !mIsLoaderTaskRunning) {
+                if (bindDirectly) {
                     // Divide the set of loaded items into those that we are binding synchronously,
                     // and everything else that is to be bound normally (asynchronously).
-                    loaderResults.bindWorkspace();
+                    loaderResults.bindWorkspace(bindAllCallbacks);
                     // For now, continue posting the binding of AllApps as there are other
                     // issues that arise from that.
                     loaderResults.bindAllApps();
@@ -394,7 +412,7 @@
      * If there is already a loader task running, tell it to stop.
      * @return true if an existing loader was stopped.
      */
-    public boolean stopLoader() {
+    private boolean stopLoader() {
         synchronized (mLock) {
             LoaderTask oldTask = mLoaderTask;
             mLoaderTask = null;
@@ -550,6 +568,9 @@
     }
 
     public void enqueueModelUpdateTask(ModelUpdateTask task) {
+        if (mModelDestroyed) {
+            return;
+        }
         task.init(mApp, this, mBgDataModel, mBgAllAppsList, MAIN_EXECUTOR);
         MODEL_EXECUTOR.execute(task);
     }
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 440e9e3..df09f29 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
 import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
 import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
 import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
@@ -61,7 +60,6 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.DbDowngradeHelper;
-import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.provider.LauncherDbUtils;
 import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
@@ -99,7 +97,7 @@
      * Represents the schema of the database. Changes in scheme need not be backwards compatible.
      * When increasing the scheme version, ensure that downgrade_schema.json is updated
      */
-    public static final int SCHEMA_VERSION = 29;
+    public static final int SCHEMA_VERSION = 30;
 
     public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".settings";
     public static final String KEY_LAYOUT_PROVIDER_AUTHORITY = "KEY_LAYOUT_PROVIDER_AUTHORITY";
@@ -153,15 +151,7 @@
             mOpenHelper = DatabaseHelper.createDatabaseHelper(
                     getContext(), false /* forMigration */);
 
-            if (RestoreDbTask.isPending(getContext())) {
-                if (!RestoreDbTask.performRestore(getContext(), mOpenHelper,
-                        new BackupManager(getContext()))) {
-                    mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
-                }
-                // Set is pending to false irrespective of the result, so that it doesn't get
-                // executed again.
-                RestoreDbTask.setPending(getContext(), false);
-            }
+            RestoreDbTask.restoreIfNeeded(getContext(), mOpenHelper);
         }
     }
 
@@ -434,32 +424,26 @@
                 return null;
             }
             case LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER: {
-                if (MULTI_DB_GRID_MIRATION_ALGO.get()) {
-                    Bundle result = new Bundle();
-                    result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
-                            prepForMigration(
-                                    InvariantDeviceProfile.INSTANCE.get(getContext()).dbFile,
-                                    Favorites.TMP_TABLE,
-                                    () -> mOpenHelper,
-                                    () -> DatabaseHelper.createDatabaseHelper(
-                                            getContext(), true /* forMigration */)));
-                    return result;
-                }
-                return null;
+                Bundle result = new Bundle();
+                result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
+                        prepForMigration(
+                                InvariantDeviceProfile.INSTANCE.get(getContext()).dbFile,
+                                Favorites.TMP_TABLE,
+                                () -> mOpenHelper,
+                                () -> DatabaseHelper.createDatabaseHelper(
+                                        getContext(), true /* forMigration */)));
+                return result;
             }
             case LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW: {
-                if (MULTI_DB_GRID_MIRATION_ALGO.get()) {
-                    Bundle result = new Bundle();
-                    result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
-                            prepForMigration(
-                                    arg /* dbFile */,
-                                    Favorites.PREVIEW_TABLE_NAME,
-                                    () -> DatabaseHelper.createDatabaseHelper(
-                                            getContext(), arg, true /* forMigration */),
-                                    () -> mOpenHelper));
-                    return result;
-                }
-                return null;
+                Bundle result = new Bundle();
+                result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
+                        prepForMigration(
+                                arg /* dbFile */,
+                                Favorites.PREVIEW_TABLE_NAME,
+                                () -> DatabaseHelper.createDatabaseHelper(
+                                        getContext(), arg, true /* forMigration */),
+                                () -> mOpenHelper));
+                return result;
             }
             case LauncherSettings.Settings.METHOD_SWITCH_DATABASE: {
                 if (TextUtils.equals(arg, mOpenHelper.getDatabaseName())) return null;
@@ -655,8 +639,7 @@
         static DatabaseHelper createDatabaseHelper(Context context, String dbName,
                 boolean forMigration) {
             if (dbName == null) {
-                dbName = MULTI_DB_GRID_MIRATION_ALGO.get() ? InvariantDeviceProfile.INSTANCE.get(
-                        context).dbFile : LauncherFiles.LAUNCHER_DB;
+                dbName = InvariantDeviceProfile.INSTANCE.get(context).dbFile;
             }
             DatabaseHelper databaseHelper = new DatabaseHelper(context, dbName, forMigration);
             // Table creation sometimes fails silently, which leads to a crash loop.
@@ -667,10 +650,6 @@
                 // This operation is a no-op if the table already exists.
                 databaseHelper.addFavoritesTable(databaseHelper.getWritableDatabase(), true);
             }
-            if (!MULTI_DB_GRID_MIRATION_ALGO.get()) {
-                databaseHelper.mBackupTableExists = tableExists(
-                        databaseHelper.getReadableDatabase(), Favorites.BACKUP_TABLE_NAME);
-            }
             databaseHelper.mHotseatRestoreTableExists = tableExists(
                     databaseHelper.getReadableDatabase(), Favorites.HYBRID_HOTSEAT_BACKUP_TABLE);
 
@@ -852,11 +831,7 @@
                 case 25:
                     convertShortcutsToLauncherActivities(db);
                 case 26:
-                    // QSB was moved to the grid. Clear the first row on screen 0.
-                    if (FeatureFlags.QSB_ON_FIRST_SCREEN &&
-                            !LauncherDbUtils.prepareScreenZeroToHostQsb(mContext, db)) {
-                        break;
-                    }
+                    // QSB was moved to the grid. Ignore overlapping items
                 case 27: {
                     // Update the favorites table so that the screen ids are ordered based on
                     // workspace page rank.
@@ -889,6 +864,11 @@
                     }
                 }
                 case 29: {
+                    // Remove widget panel related leftover workspace items
+                    db.delete(Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
+                            Favorites.SCREEN, IntArray.wrap(-777, -778)), null);
+                }
+                case 30: {
                     // DB Upgraded successfully
                     return;
                 }
@@ -1090,7 +1070,7 @@
         }
 
         private int initializeMaxScreenId(SQLiteDatabase db) {
-            return getMaxId(db, "SELECT MAX(%1$s) FROM %2$s WHERE %3$s = %4$d",
+            return getMaxId(db, "SELECT MAX(%1$s) FROM %2$s WHERE %3$s = %4$d AND %1$s >= 0",
                     Favorites.SCREEN, Favorites.TABLE_NAME, Favorites.CONTAINER,
                     Favorites.CONTAINER_DESKTOP);
         }
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index f26cfe8..5ef3690 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -1,18 +1,24 @@
 package com.android.launcher3;
 
+import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
 import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
 
 import android.annotation.TargetApi;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Canvas;
+import android.graphics.Insets;
 import android.graphics.Rect;
 import android.os.Build;
 import android.util.AttributeSet;
 import android.view.ViewDebug;
 import android.view.WindowInsets;
 
+import androidx.annotation.RequiresApi;
+
 import com.android.launcher3.graphics.SysUiScrim;
 import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.uioverrides.ApiWrapper;
 
 import java.util.Collections;
 import java.util.List;
@@ -42,15 +48,8 @@
     }
 
     private void handleSystemWindowInsets(Rect insets) {
-        DeviceProfile dp = mActivity.getDeviceProfile();
-
-        // Taskbar provides insets, but we don't want that for most Launcher elements so remove it.
-        mTempRect.set(insets);
-        insets = mTempRect;
-        insets.bottom = Math.max(0, insets.bottom - dp.nonOverlappingTaskbarInset);
-
         // Update device profile before notifying the children.
-        dp.updateInsets(insets);
+        mActivity.getDeviceProfile().updateInsets(insets);
         boolean resetState = !insets.equals(mInsets);
         setInsets(insets);
 
@@ -61,12 +60,73 @@
 
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        mTempRect.set(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
-                insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
+        if (Utilities.ATLEAST_R) {
+            insets = updateInsetsDueToTaskbar(insets);
+            Insets systemWindowInsets = insets.getInsetsIgnoringVisibility(
+                    WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
+            mTempRect.set(systemWindowInsets.left, systemWindowInsets.top, systemWindowInsets.right,
+                    systemWindowInsets.bottom);
+        } else {
+            mTempRect.set(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
+                    insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
+        }
         handleSystemWindowInsets(mTempRect);
         return insets;
     }
 
+    /**
+     * Taskbar provides nav bar and tappable insets. However, taskbar is not attached immediately,
+     * and can be destroyed and recreated. Thus, instead of relying on taskbar being present to
+     * get its insets, we calculate them ourselves so they are stable regardless of whether taskbar
+     * is currently attached.
+     *
+     * @param oldInsets The system-provided insets, which we are modifying.
+     * @return The updated insets.
+     */
+    @RequiresApi(api = Build.VERSION_CODES.R)
+    private WindowInsets updateInsetsDueToTaskbar(WindowInsets oldInsets) {
+        if (!ApiWrapper.TASKBAR_DRAWN_IN_PROCESS) {
+            // 3P launchers based on Launcher3 should still be inset like normal.
+            return oldInsets;
+        }
+
+        WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
+
+        DeviceProfile dp = mActivity.getDeviceProfile();
+        Resources resources = getResources();
+
+        Insets oldNavInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
+        Rect newNavInsets = new Rect(oldNavInsets.left, oldNavInsets.top, oldNavInsets.right,
+                oldNavInsets.bottom);
+
+        if (dp.isLandscape) {
+            boolean isGesturalMode = ResourceUtils.getIntegerByName(
+                    "config_navBarInteractionMode",
+                    resources,
+                    INVALID_RESOURCE_HANDLE) == 2;
+            if (dp.isTablet || isGesturalMode) {
+                newNavInsets.bottom = ResourceUtils.getNavbarSize(
+                        "navigation_bar_height_landscape", resources);
+            } else {
+                int navWidth = ResourceUtils.getNavbarSize("navigation_bar_width", resources);
+                if (dp.isSeascape()) {
+                    newNavInsets.left = navWidth;
+                } else {
+                    newNavInsets.right = navWidth;
+                }
+            }
+        } else {
+            newNavInsets.bottom = ResourceUtils.getNavbarSize("navigation_bar_height", resources);
+        }
+        updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), Insets.of(newNavInsets));
+        updatedInsetsBuilder.setInsetsIgnoringVisibility(WindowInsets.Type.navigationBars(),
+                Insets.of(newNavInsets));
+
+        mActivity.updateWindowInsets(updatedInsetsBuilder, oldInsets);
+
+        return updatedInsetsBuilder.build();
+    }
+
     @Override
     public void setInsets(Rect insets) {
         // If the insets haven't changed, this is a no-op. Avoid unnecessary layout caused by
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index d663480..048aaaa 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -95,6 +95,12 @@
         public static final int ITEM_TYPE_DEEP_SHORTCUT = 6;
 
         /**
+         * The favroite is a search action
+         */
+        public static final int ITEM_TYPE_SEARCH_ACTION = 7;
+
+
+        /**
          * Type of the item is recents task.
          * TODO(hyunyoungs): move constants not related to Favorites DB to a better location.
          */
@@ -199,6 +205,7 @@
         public static final int CONTAINER_WIDGETS_TRAY = -105;
         public static final int CONTAINER_BOTTOM_WIDGETS_TRAY = -112;
         public static final int CONTAINER_PIN_WIDGETS = -113;
+        public static final int CONTAINER_WALLPAPERS = -114;
         // Represents search results view.
         public static final int CONTAINER_SEARCH_RESULTS = -106;
         public static final int CONTAINER_SHORTCUTS = -107;
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 3399ce9..15378e0 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -181,14 +181,6 @@
         return launcher.getNormalOverviewScaleAndOffset();
     }
 
-    public float getTaskbarScale(Launcher launcher) {
-        return launcher.getNormalTaskbarScale();
-    }
-
-    public float getTaskbarTranslationY(Launcher launcher) {
-        return -launcher.getHotseat().getTaskbarOffsetY();
-    }
-
     public float getOverviewFullscreenProgress() {
         return 0;
     }
@@ -205,6 +197,10 @@
         return (getVisibleElements(launcher) & elements) == elements;
     }
 
+    public boolean isTaskbarStashed() {
+        return false;
+    }
+
     /**
      * Fraction shift in the vertical translation UI and related properties
      *
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index b423871..523ac72 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3;
 
+import static androidx.annotation.VisibleForTesting.PACKAGE_PRIVATE;
+
 import static com.android.launcher3.anim.Interpolators.SCROLL;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
@@ -48,6 +50,7 @@
 import android.widget.ScrollView;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
@@ -55,6 +58,7 @@
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.touch.PagedOrientationHandler.ChildBounds;
 import com.android.launcher3.util.EdgeEffectCompat;
+import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.views.ActivityContext;
 
@@ -100,6 +104,12 @@
 
     @ViewDebug.ExportedProperty(category = "launcher")
     protected int mCurrentPage;
+    // Difference between current scroll position and mCurrentPage's page scroll. Used to maintain
+    // relative scroll position unchanged in updateCurrentPageScroll. Cleared when snapping to a
+    // page.
+    protected int mCurrentPageScrollDiff;
+    // The current page the PagedView is scrolling over on it's way to the destination page.
+    protected int mCurrentScrollOverPage;
 
     @ViewDebug.ExportedProperty(category = "launcher")
     protected int mNextPage = INVALID_PAGE;
@@ -172,6 +182,7 @@
 
         mScroller = new OverScroller(context, SCROLL);
         mCurrentPage = 0;
+        mCurrentScrollOverPage = 0;
 
         final ViewConfiguration configuration = ViewConfiguration.get(context);
         mTouchSlop = configuration.getScaledTouchSlop();
@@ -197,7 +208,7 @@
     public void initParentViews(View parent) {
         if (mPageIndicatorViewId > -1) {
             mPageIndicator = parent.findViewById(mPageIndicatorViewId);
-            mPageIndicator.setMarkersCount(getChildCount());
+            mPageIndicator.setMarkersCount(getChildCount() / getPanelCount());
         }
     }
 
@@ -230,10 +241,6 @@
         return getChildAt(index);
     }
 
-    protected int indexToPage(int index) {
-        return index;
-    }
-
     /**
      * Updates the scroll of the current page immediately to its final scroll position.  We use this
      * in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of
@@ -243,11 +250,11 @@
         // If the current page is invalid, just reset the scroll position to zero
         int newPosition = 0;
         if (0 <= mCurrentPage && mCurrentPage < getPageCount()) {
-            newPosition = getScrollForPage(mCurrentPage);
+            newPosition = getScrollForPage(mCurrentPage) + mCurrentPageScrollDiff;
         }
         mOrientationHandler.set(this, VIEW_SCROLL_TO, newPosition);
         mScroller.startScroll(mScroller.getCurrX(), 0, newPosition - mScroller.getCurrX(), 0);
-        forceFinishScroller(true);
+        forceFinishScroller();
     }
 
     /**
@@ -269,14 +276,16 @@
         }
     }
 
-    private void forceFinishScroller(boolean resetNextPage) {
+    /**
+     * Immediately finishes any in-progress scroll, maintaining the current position. Also sets
+     * mNextPage = INVALID_PAGE and calls pageEndTransition().
+     */
+    public void forceFinishScroller() {
         mScroller.forceFinished(true);
         // We need to clean up the next page here to avoid computeScrollHelper from
         // updating current page on the pass.
-        if (resetNextPage) {
-            mNextPage = INVALID_PAGE;
-            pageEndTransition();
-        }
+        mNextPage = INVALID_PAGE;
+        pageEndTransition();
     }
 
     private int validateNewPage(int newPage) {
@@ -285,15 +294,21 @@
         newPage = Utilities.boundToRange(newPage, 0, getPageCount() - 1);
 
         if (getPanelCount() > 1) {
-            // Always return left panel as new page
+            // Always return left most panel as new page
             newPage = getLeftmostVisiblePageForIndex(newPage);
         }
         return newPage;
     }
 
-    private int getLeftmostVisiblePageForIndex(int pageIndex) {
+    /**
+     * In most cases where panelCount is 1, this method will just return the page index that was
+     * passed in.
+     * But for example when two panel home is enabled we might need the leftmost visible page index
+     * because that page is the current page.
+     */
+    public int getLeftmostVisiblePageForIndex(int pageIndex) {
         int panelCount = getPanelCount();
-        return (pageIndex / panelCount) * panelCount;
+        return pageIndex - pageIndex % panelCount;
     }
 
     /**
@@ -304,23 +319,81 @@
     }
 
     /**
+     * Returns an IntSet with the indices of the currently visible pages
+     */
+    @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
+    public IntSet getVisiblePageIndices() {
+        return getPageIndices(mCurrentPage);
+    }
+
+    /**
+     * In case the panelCount is 1 this just returns the same page index in an IntSet.
+     * But in cases where the panelCount > 1 this will return all the page indices that belong
+     * together, i.e. on the Workspace they are next to each other and shown at the same time.
+     */
+    private IntSet getPageIndices(int pageIndex) {
+        // we want to make sure the pageIndex is the leftmost page
+        pageIndex = getLeftmostVisiblePageForIndex(pageIndex);
+
+        IntSet pageIndices = new IntSet();
+        int panelCount = getPanelCount();
+        int pageCount = getPageCount();
+        for (int page = pageIndex; page < pageIndex + panelCount && page < pageCount; page++) {
+            pageIndices.add(page);
+        }
+        return pageIndices;
+    }
+
+    /**
+     * Returns an IntSet with the indices of the neighbour pages that are in the focus direction.
+     */
+    private IntSet getNeighbourPageIndices(int focus) {
+        int panelCount = getPanelCount();
+        // getNextPage is more reliable than getCurrentPage
+        int currentPage = getNextPage();
+
+        int nextPage;
+        if (focus == View.FOCUS_LEFT) {
+            nextPage = currentPage - panelCount;
+        } else if (focus == View.FOCUS_RIGHT) {
+            nextPage = currentPage + panelCount;
+        } else {
+            // no neighbours to other directions
+            return new IntSet();
+        }
+        nextPage = validateNewPage(nextPage);
+        if (nextPage == currentPage) {
+            // We reached the end of the pages
+            return new IntSet();
+        }
+
+        return getPageIndices(nextPage);
+    }
+
+    /**
      * Executes the callback against each visible page
      */
     public void forEachVisiblePage(Consumer<View> callback) {
-        int panelCount = getPanelCount();
-        for (int i = mCurrentPage; i < mCurrentPage + panelCount; i++) {
-            View page = getPageAt(i);
+        getVisiblePageIndices().forEach(pageIndex -> {
+            View page = getPageAt(pageIndex);
             if (page != null) {
                 callback.accept(page);
             }
-        }
+        });
     }
 
     /**
      * Returns true if the view is on one of the current pages, false otherwise.
      */
     public boolean isVisible(View child) {
-        return getLeftmostVisiblePageForIndex(indexOfChild(child)) == mCurrentPage;
+        return isVisible(indexOfChild(child));
+    }
+
+    /**
+     * Returns true if the page with the given index is currently visible, false otherwise.
+     */
+    private boolean isVisible(int pageIndex) {
+        return getLeftmostVisiblePageForIndex(pageIndex) == mCurrentPage;
     }
 
     /**
@@ -369,6 +442,7 @@
         }
         int prevPage = overridePrevPage != INVALID_PAGE ? overridePrevPage : mCurrentPage;
         mCurrentPage = validateNewPage(currentPage);
+        mCurrentScrollOverPage = mCurrentPage;
         updateCurrentPageScroll();
         notifyPageSwitchListener(prevPage);
         invalidate();
@@ -424,6 +498,7 @@
      * to provide custom behavior during animation.
      */
     protected void onPageEndTransition() {
+        mCurrentPageScrollDiff = 0;
         AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
         AccessibilityManagerCompat.sendCustomAccessibilityEvent(getPageAt(mCurrentPage),
                 AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
@@ -488,9 +563,11 @@
                 if (newPos < mMinScroll && oldPos >= mMinScroll) {
                     mEdgeGlowLeft.onAbsorb((int) mScroller.getCurrVelocity());
                     mScroller.abortAnimation();
+                    onEdgeAbsorbingScroll();
                 } else if (newPos > mMaxScroll && oldPos <= mMaxScroll) {
                     mEdgeGlowRight.onAbsorb((int) mScroller.getCurrVelocity());
                     mScroller.abortAnimation();
+                    onEdgeAbsorbingScroll();
                 }
             }
 
@@ -508,6 +585,7 @@
             sendScrollAccessibilityEvent();
             int prevPage = mCurrentPage;
             mCurrentPage = validateNewPage(mNextPage);
+            mCurrentScrollOverPage = mCurrentPage;
             mNextPage = INVALID_PAGE;
             notifyPageSwitchListener(prevPage);
 
@@ -560,7 +638,10 @@
     }
 
     private int getPageWidthSize(int widthSize) {
-        return (widthSize - mInsets.left - mInsets.right) / getPanelCount();
+        // It's necessary to add the padding back because it is remove when measuring children,
+        // like when MeasureSpec.getSize in CellLayout.
+        return (widthSize - mInsets.left - mInsets.right - getPaddingLeft() - getPaddingRight())
+                / getPanelCount() + getPaddingLeft() + getPaddingRight();
     }
 
     @Override
@@ -676,6 +757,7 @@
         final int scrollOffsetStart = mOrientationHandler.getScrollOffsetStart(this, mInsets);
         final int scrollOffsetEnd = mOrientationHandler.getScrollOffsetEnd(this, mInsets);
         boolean pageScrollChanged = false;
+        int panelCount = getPanelCount();
 
         for (int i = startIndex, childStart = scrollOffsetStart; i != endIndex; i += delta) {
             final View child = getPageAt(i);
@@ -693,14 +775,19 @@
                     pageScrollChanged = true;
                     outPageScrolls[i] = pageScroll;
                 }
-                childStart += primaryDimension + mPageSpacing + getChildGap();
+                childStart += primaryDimension + getChildGap();
+
+                // This makes sure that the space is added after the page, not after each panel
+                int lastPanel = mIsRtl ? 0 : panelCount - 1;
+                if (i % panelCount == lastPanel) {
+                    childStart += mPageSpacing;
+                }
             }
         }
 
-        int panelCount = getPanelCount();
         if (panelCount > 1) {
             for (int i = 0; i < childCount; i++) {
-                // In case we have multiple panels, always use left panel's page scroll for all
+                // In case we have multiple panels, always use left most panel's page scroll for all
                 // panels on the screen.
                 int adjustedScroll = outPageScrolls[getLeftmostVisiblePageForIndex(i)];
                 if (outPageScrolls[i] != adjustedScroll) {
@@ -746,7 +833,7 @@
 
     private void dispatchPageCountChanged() {
         if (mPageIndicator != null) {
-            mPageIndicator.setMarkersCount(getChildCount());
+            mPageIndicator.setMarkersCount(getChildCount() / getPanelCount());
         }
         // This ensures that when children are added, they get the correct transforms / alphas
         // in accordance with any scroll effects.
@@ -763,6 +850,7 @@
     public void onViewRemoved(View child) {
         super.onViewRemoved(child);
         mCurrentPage = validateNewPage(mCurrentPage);
+        mCurrentScrollOverPage = mCurrentPage;
         dispatchPageCountChanged();
     }
 
@@ -779,8 +867,8 @@
 
     @Override
     public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
-        int page = indexToPage(indexOfChild(child));
-        if (page != mCurrentPage || !mScroller.isFinished()) {
+        int page = indexOfChild(child);
+        if (!isVisible(page) || !mScroller.isFinished()) {
             if (immediate) {
                 setCurrentPage(page);
             } else {
@@ -819,21 +907,25 @@
                 direction = View.FOCUS_LEFT;
             }
         }
-        if (direction == View.FOCUS_LEFT) {
-            if (getCurrentPage() > 0) {
-                int nextPage = validateNewPage(getCurrentPage() - 1);
-                snapToPage(nextPage);
-                getChildAt(nextPage).requestFocus(direction);
-                return true;
-            }
-        } else if (direction == View.FOCUS_RIGHT) {
-            if (getCurrentPage() < getPageCount() - 1) {
-                int nextPage = validateNewPage(getCurrentPage() + 1);
-                snapToPage(nextPage);
-                getChildAt(nextPage).requestFocus(direction);
-                return true;
+
+        int currentPage = getNextPage();
+        int closestNeighbourIndex = -1;
+        int closestNeighbourDistance = Integer.MAX_VALUE;
+        // Find the closest neighbour page
+        for (int neighbourPageIndex : getNeighbourPageIndices(direction)) {
+            int distance = Math.abs(neighbourPageIndex - currentPage);
+            if (closestNeighbourDistance > distance) {
+                closestNeighbourDistance = distance;
+                closestNeighbourIndex = neighbourPageIndex;
             }
         }
+        if (closestNeighbourIndex != -1) {
+            View page = getPageAt(closestNeighbourIndex);
+            snapToPage(closestNeighbourIndex);
+            page.requestFocus(direction);
+            return true;
+        }
+
         return false;
     }
 
@@ -843,28 +935,12 @@
             return;
         }
 
-        // Add the current page's views as focusable and the next possible page's too. If the
-        // last focus change action was left then the left neighbour's views will be added, and
-        // if it was right then the right neighbour's views will be added.
-        // Unfortunately mCurrentPage can be outdated if there were multiple control actions in a
-        // short period of time, but mNextPage is up to date because it is always updated by
-        // method snapToPage.
-        int nextPage = getNextPage();
-        // XXX-RTL: This will be fixed in a future CL
-        if (nextPage >= 0 && nextPage < getPageCount()) {
-            getPageAt(nextPage).addFocusables(views, direction, focusableMode);
-        }
-        if (direction == View.FOCUS_LEFT) {
-            if (nextPage > 0) {
-                nextPage = validateNewPage(nextPage - 1);
-                getPageAt(nextPage).addFocusables(views, direction, focusableMode);
-            }
-        } else if (direction == View.FOCUS_RIGHT) {
-            if (nextPage < getPageCount() - 1) {
-                nextPage = validateNewPage(nextPage + 1);
-                getPageAt(nextPage).addFocusables(views, direction, focusableMode);
-            }
-        }
+        // nextPage is more reliable when multiple control movements have been done in a short
+        // period of time
+        getPageIndices(getNextPage())
+                .addAll(getNeighbourPageIndices(direction))
+                .forEach(pageIndex ->
+                        getPageAt(pageIndex).addFocusables(views, direction, focusableMode));
     }
 
     /**
@@ -984,7 +1060,7 @@
     /**
      * If being flinged and user touches the screen, initiate drag; otherwise don't.
      */
-    private void updateIsBeingDraggedOnTouchDown(MotionEvent ev) {
+    protected void updateIsBeingDraggedOnTouchDown(MotionEvent ev) {
         // mScroller.isFinished should be false when being flinged.
         final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX());
         final boolean finishedScrolling = (mScroller.isFinished() || xDist < mPageSlop / 3);
@@ -1054,26 +1130,25 @@
 
     protected float getScrollProgress(int screenCenter, View v, int page) {
         final int halfScreenSize = getMeasuredWidth() / 2;
-
         int delta = screenCenter - (getScrollForPage(page) + halfScreenSize);
-        int count = getChildCount();
+        int panelCount = getPanelCount();
+        int pageCount = getChildCount();
 
-        final int totalDistance;
-
-        int adjacentPage = page + 1;
+        int adjacentPage = page + panelCount;
         if ((delta < 0 && !mIsRtl) || (delta > 0 && mIsRtl)) {
-            adjacentPage = page - 1;
+            adjacentPage = page - panelCount;
         }
 
-        if (adjacentPage < 0 || adjacentPage > count - 1) {
-            totalDistance = v.getMeasuredWidth() + mPageSpacing;
+        final int totalDistance;
+        if (adjacentPage < 0 || adjacentPage > pageCount - 1) {
+            totalDistance = (v.getMeasuredWidth() + mPageSpacing) * panelCount;
         } else {
             totalDistance = Math.abs(getScrollForPage(adjacentPage) - getScrollForPage(page));
         }
 
         float scrollProgress = delta / (totalDistance * 1.0f);
         scrollProgress = Math.min(scrollProgress, MAX_SCROLL_PROGRESS);
-        scrollProgress = Math.max(scrollProgress, - MAX_SCROLL_PROGRESS);
+        scrollProgress = Math.max(scrollProgress, -MAX_SCROLL_PROGRESS);
         return scrollProgress;
     }
 
@@ -1120,6 +1195,10 @@
         mAllowOverScroll = enable;
     }
 
+    protected float getSignificantMoveThreshold() {
+        return SIGNIFICANT_MOVE_THRESHOLD;
+    }
+
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         // Skip touch handling if there are no pages to swipe
@@ -1191,6 +1270,7 @@
                     }
                     delta -= consumed;
                 }
+                delta /= mOrientationHandler.getPrimaryScale(this);
 
                 // Only scroll and update mLastMotionX if we have moved some discrete amount.  We
                 // keep the remainder because we are actually testing if we've moved from the last
@@ -1243,11 +1323,12 @@
 
                 int velocity = (int) mOrientationHandler.getPrimaryVelocity(velocityTracker,
                     mActivePointerId);
-                int delta = (int) (primaryDirection - mDownMotionPrimary);
+                float delta = primaryDirection - mDownMotionPrimary;
+                delta /= mOrientationHandler.getPrimaryScale(this);
                 int pageOrientedSize = mOrientationHandler.getMeasuredSize(getPageAt(mCurrentPage));
 
-                boolean isSignificantMove = Math.abs(delta) > pageOrientedSize *
-                    SIGNIFICANT_MOVE_THRESHOLD;
+                boolean isSignificantMove = Math.abs(delta)
+                        > pageOrientedSize * getSignificantMoveThreshold();
 
                 mTotalMotion += Math.abs(mLastMotion + mLastMotionRemainder - primaryDirection);
                 boolean passedSlop = mAllowEasyFling || mTotalMotion > mPageSlop;
@@ -1341,6 +1422,20 @@
 
     protected void onNotSnappingToPageInFreeScroll() { }
 
+    /**
+     * Called when the view edges absorb part of the scroll. Subclasses can override this
+     * to provide custom behavior during animation.
+     */
+    protected void onEdgeAbsorbingScroll() {
+    }
+
+    /**
+     * Called when the current page closest to the center of the screen changes as part of the
+     * scroll. Subclasses can override this to provide custom behavior during scroll.
+     */
+    protected void onScrollOverPageChanged() {
+    }
+
     protected boolean shouldFlingForVelocity(int velocity) {
         float threshold = mAllowEasyFling ? mEasyFlingThresholdVelocity : mFlingThresholdVelocity;
         return Math.abs(velocity) > threshold;
@@ -1439,8 +1534,8 @@
             setCurrentPage(nextPage);
         }
 
-        int page = indexToPage(indexOfChild(child));
-        if (page >= 0 && page != getCurrentPage() && !isInTouchMode()) {
+        int page = indexOfChild(child);
+        if (page >= 0 && !isVisible(page) && !isInTouchMode()) {
             snapToPage(page);
         }
     }
@@ -1486,7 +1581,7 @@
         return getDisplacementFromScreenCenter(childIndex, screenCenter);
     }
 
-    private int getScreenCenter(int primaryScroll) {
+    protected int getScreenCenter(int primaryScroll) {
         float primaryScale = mOrientationHandler.getPrimaryScale(this);
         float primaryPivot =  mOrientationHandler.getPrimaryValue(getPivotX(), getPivotY());
         int pageOrientationSize = mOrientationHandler.getMeasuredSize(this);
@@ -1571,7 +1666,7 @@
             return false;
         }
 
-        if (FeatureFlags.IS_STUDIO_BUILD) {
+        if (FeatureFlags.IS_STUDIO_BUILD && !Utilities.IS_RUNNING_IN_TEST_HARNESS) {
             duration *= Settings.Global.getFloat(getContext().getContentResolver(),
                     Settings.Global.WINDOW_ANIMATION_SCALE, 1);
         }
@@ -1610,7 +1705,7 @@
 
     public boolean scrollLeft() {
         if (getNextPage() > 0) {
-            snapToPage(getNextPage() - 1);
+            snapToPage(getNextPage() - getPanelCount());
             return true;
         }
         return mAllowOverScroll;
@@ -1618,13 +1713,22 @@
 
     public boolean scrollRight() {
         if (getNextPage() < getChildCount() - 1) {
-            snapToPage(getNextPage() + 1);
+            snapToPage(getNextPage() + getPanelCount());
             return true;
         }
         return mAllowOverScroll;
     }
 
     @Override
+    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        int newDestinationPage = getDestinationPage();
+        if (newDestinationPage >= 0 && newDestinationPage != mCurrentScrollOverPage) {
+            mCurrentScrollOverPage = newDestinationPage;
+            onScrollOverPageChanged();
+        }
+    }
+
+    @Override
     public CharSequence getAccessibilityClassName() {
         // Some accessibility services have special logic for ScrollView. Since we provide same
         // accessibility info as ScrollView, inform the service to handle use the same way.
diff --git a/src/com/android/launcher3/Partner.java b/src/com/android/launcher3/Partner.java
index 0bdb37c..2e27f32 100644
--- a/src/com/android/launcher3/Partner.java
+++ b/src/com/android/launcher3/Partner.java
@@ -142,7 +142,7 @@
         }
 
         if (iconSize > 0) {
-            inv.iconSize = iconSize;
+            inv.iconSize[InvariantDeviceProfile.INDEX_DEFAULT] = iconSize;
         }
     }
 }
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index bebbf4f..fec1d68 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -23,6 +23,7 @@
 
 import android.app.WallpaperManager;
 import android.content.Context;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.view.MotionEvent;
 import android.view.View;
@@ -48,7 +49,7 @@
 
     private int mCellWidth;
     private int mCellHeight;
-    private int mBorderSpacing;
+    private Point mBorderSpace;
 
     private int mCountX;
     private int mCountY;
@@ -64,12 +65,12 @@
     }
 
     public void setCellDimensions(int cellWidth, int cellHeight, int countX, int countY,
-            int borderSpacing) {
+            Point borderSpace) {
         mCellWidth = cellWidth;
         mCellHeight = cellHeight;
         mCountX = countX;
         mCountY = countY;
-        mBorderSpacing = borderSpacing;
+        mBorderSpace = borderSpace;
     }
 
     public View getChildAt(int cellX, int cellY) {
@@ -108,10 +109,10 @@
             DeviceProfile profile = mActivity.getDeviceProfile();
             ((NavigableAppWidgetHostView) child).getWidgetInset(profile, mTempRect);
             lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
-                    profile.appWidgetScale.x, profile.appWidgetScale.y, mBorderSpacing, mTempRect);
+                    profile.appWidgetScale.x, profile.appWidgetScale.y, mBorderSpace, mTempRect);
         } else {
             lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
-                    mBorderSpacing, null);
+                    mBorderSpace, null);
         }
     }
 
@@ -132,10 +133,10 @@
         if (child instanceof NavigableAppWidgetHostView) {
             ((NavigableAppWidgetHostView) child).getWidgetInset(dp, mTempRect);
             lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
-                    dp.appWidgetScale.x, dp.appWidgetScale.y, mBorderSpacing, mTempRect);
+                    dp.appWidgetScale.x, dp.appWidgetScale.y, mBorderSpace, mTempRect);
         } else {
             lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
-                    mBorderSpacing, null);
+                    mBorderSpace, null);
             // Center the icon/folder
             int cHeight = getCellContentHeight();
             int cellPaddingY = dp.isScalableGrid && mContainerType == WORKSPACE
@@ -143,8 +144,9 @@
                     : (int) Math.max(0, ((lp.height - cHeight) / 2f));
 
             // No need to add padding when cell layout border spacing is present.
-            boolean noPaddingX = (dp.cellLayoutBorderSpacingPx > 0 && mContainerType == WORKSPACE)
-                    || (dp.folderCellLayoutBorderSpacingPx > 0 && mContainerType == FOLDER);
+            boolean noPaddingX =
+                    (dp.cellLayoutBorderSpacePx.x > 0 && mContainerType == WORKSPACE)
+                            || (dp.folderCellLayoutBorderSpacePx.x > 0 && mContainerType == FOLDER);
             int cellPaddingX = noPaddingX
                     ? 0
                     : mContainerType == WORKSPACE
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 75f6278..7a38fe7 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -45,6 +45,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.InsetDrawable;
@@ -68,6 +69,7 @@
 import android.view.animation.Interpolator;
 import android.widget.LinearLayout;
 
+import androidx.annotation.NonNull;
 import androidx.core.graphics.ColorUtils;
 import androidx.core.os.BuildCompat;
 
@@ -80,11 +82,13 @@
 import com.android.launcher3.icons.ShortcutCachingLogic;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.model.data.SearchActionItemInfo;
 import com.android.launcher3.pm.ShortcutConfigActivityInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.shortcuts.ShortcutRequest;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 
@@ -380,6 +384,21 @@
     }
 
     /**
+     * Similar to {@link #scaleRectAboutCenter(Rect, float)} except this allows different scales
+     * for X and Y
+     */
+    public static void scaleRectFAboutCenter(RectF r, float scaleX, float scaleY) {
+        float px = r.centerX();
+        float py = r.centerY();
+        r.offset(-px, -py);
+        r.left = r.left * scaleX;
+        r.top = r.top * scaleY;
+        r.right = r.right * scaleX;
+        r.bottom = r.bottom * scaleY;
+        r.offset(px, py);
+    }
+
+    /**
      * Maps t from one range to another range.
      * @param t The value to map.
      * @param fromMin The lower bound of the range that t is being mapped from.
@@ -632,21 +651,6 @@
         handler.sendMessage(msg);
     }
 
-    /**
-     * Parses a string encoded using {@link #getPointString(int, int)}
-     */
-    public static Point parsePoint(String point) {
-        String[] split = point.split(",");
-        return new Point(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
-    }
-
-    /**
-     * Encodes a point to string to that it can be persisted atomically.
-     */
-    public static String getPointString(int x, int y) {
-        return String.format(Locale.ENGLISH, "%d,%d", x, y);
-    }
-
     public static void unregisterReceiverSafely(Context context, BroadcastReceiver receiver) {
         try {
             context.unregisterReceiver(receiver);
@@ -659,25 +663,26 @@
      * @param outObj this is set to the internal data associated with {@param info},
      *               eg {@link LauncherActivityInfo} or {@link ShortcutInfo}.
      */
-    public static Drawable getFullDrawable(Launcher launcher, ItemInfo info, int width, int height,
+    public static Drawable getFullDrawable(Context context, ItemInfo info, int width, int height,
             Object[] outObj) {
-        Drawable icon = loadFullDrawableWithoutTheme(launcher, info, width, height, outObj);
+        Drawable icon = loadFullDrawableWithoutTheme(context, info, width, height, outObj);
         if (icon instanceof BitmapInfo.Extender) {
-            icon = ((BitmapInfo.Extender) icon).getThemedDrawable(launcher);
+            icon = ((BitmapInfo.Extender) icon).getThemedDrawable(context);
         }
         return icon;
     }
 
-    private static Drawable loadFullDrawableWithoutTheme(Launcher launcher, ItemInfo info,
+    private static Drawable loadFullDrawableWithoutTheme(Context context, ItemInfo info,
             int width, int height, Object[] outObj) {
-        LauncherAppState appState = LauncherAppState.getInstance(launcher);
+        ActivityContext activity = ActivityContext.lookupContext(context);
+        LauncherAppState appState = LauncherAppState.getInstance(context);
         if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
-            LauncherActivityInfo activityInfo = launcher.getSystemService(LauncherApps.class)
+            LauncherActivityInfo activityInfo = context.getSystemService(LauncherApps.class)
                     .resolveActivity(info.getIntent(), info.user);
             outObj[0] = activityInfo;
-            return activityInfo == null ? null : LauncherAppState.getInstance(launcher)
+            return activityInfo == null ? null : LauncherAppState.getInstance(context)
                     .getIconProvider().getIcon(
-                            activityInfo, launcher.getDeviceProfile().inv.fillResIconDpi);
+                            activityInfo, activity.getDeviceProfile().inv.fillResIconDpi);
         } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
             if (info instanceof PendingAddShortcutInfo) {
                 ShortcutConfigActivityInfo activityInfo =
@@ -686,23 +691,27 @@
                 return activityInfo.getFullResIcon(appState.getIconCache());
             }
             List<ShortcutInfo> si = ShortcutKey.fromItemInfo(info)
-                    .buildRequest(launcher)
+                    .buildRequest(context)
                     .query(ShortcutRequest.ALL);
             if (si.isEmpty()) {
                 return null;
             } else {
                 outObj[0] = si.get(0);
-                return ShortcutCachingLogic.getIcon(launcher, si.get(0),
+                return ShortcutCachingLogic.getIcon(context, si.get(0),
                         appState.getInvariantDeviceProfile().fillResIconDpi);
             }
         } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
             FolderAdaptiveIcon icon = FolderAdaptiveIcon.createFolderAdaptiveIcon(
-                    launcher, info.id, new Point(width, height));
+                    activity, info.id, new Point(width, height));
             if (icon == null) {
                 return null;
             }
             outObj[0] = icon;
             return icon;
+        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION
+                && info instanceof SearchActionItemInfo) {
+            return new AdaptiveIconDrawable(
+                    new FastBitmapDrawable(((SearchActionItemInfo) info).bitmap), null);
         } else {
             return null;
         }
@@ -715,8 +724,8 @@
      * badge. When dragged from workspace or folder, it may contain app AND/OR work profile badge
      **/
     @TargetApi(Build.VERSION_CODES.O)
-    public static Drawable getBadge(Launcher launcher, ItemInfo info, Object obj) {
-        LauncherAppState appState = LauncherAppState.getInstance(launcher);
+    public static Drawable getBadge(Context context, ItemInfo info, Object obj) {
+        LauncherAppState appState = LauncherAppState.getInstance(context);
         int iconSize = appState.getInvariantDeviceProfile().iconBitmapSize;
         if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
             boolean iconBadged = (info instanceof ItemInfoWithIcon)
@@ -736,7 +745,7 @@
         } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
             return ((FolderAdaptiveIcon) obj).getBadge();
         } else {
-            return launcher.getPackageManager()
+            return context.getPackageManager()
                     .getUserBadgedIcon(new FixedSizeEmptyDrawable(iconSize), info.user);
         }
     }
@@ -838,6 +847,12 @@
         view.setLayoutParams(lp);
     }
 
+    public static Rect getViewBounds(@NonNull View v) {
+        int[] pos = new int[2];
+        v.getLocationOnScreen(pos);
+        return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
+    }
+
     private static class FixedSizeEmptyDrawable extends ColorDrawable {
 
         private final int mSize;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 2bb4e5c..8095280 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -63,6 +63,8 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.Toast;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
 import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
 import com.android.launcher3.anim.Interpolators;
@@ -79,16 +81,17 @@
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.folder.PreviewBackground;
 import com.android.launcher3.graphics.DragPreviewProvider;
-import com.android.launcher3.graphics.PreloadIconDrawable;
 import com.android.launcher3.icons.BitmapRenderer;
 import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
 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.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.SearchActionItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pageindicators.WorkspacePageIndicator;
 import com.android.launcher3.popup.PopupContainerWithArrow;
@@ -99,8 +102,10 @@
 import com.android.launcher3.util.EdgeEffectCompat;
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.IntSparseArrayMap;
 import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.LauncherBindableItemsContainer;
 import com.android.launcher3.util.OverlayEdgeEffect;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.RunnableList;
@@ -119,8 +124,9 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
@@ -132,7 +138,7 @@
 public class Workspace extends PagedView<WorkspacePageIndicator>
         implements DropTarget, DragSource, View.OnTouchListener,
         DragController.DragListener, Insettable, StateHandler<LauncherState>,
-        WorkspaceLayoutManager {
+        WorkspaceLayoutManager, LauncherBindableItemsContainer {
 
     /** The value that {@link #mTransitionProgress} must be greater than for
      * {@link #transitionStateShouldAllowDrop()} to return true. */
@@ -194,7 +200,6 @@
     private final int[] mTempXY = new int[2];
     private final float[] mTempFXY = new float[2];
     @Thunk float[] mDragViewVisualCenter = new float[2];
-    private final float[] mTempTouchCoordinates = new float[2];
 
     private SpringLoadedDragController mSpringLoadedDragController;
 
@@ -204,8 +209,6 @@
 
     private boolean mStripScreensOnPageStopMoving = false;
 
-    private DragPreviewProvider mOutlineProvider = null;
-
     private boolean mWorkspaceFadeInAdjacentScreens;
 
     final WallpaperOffsetInterpolator mWallpaperOffset;
@@ -222,6 +225,9 @@
     // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
     private float mXDown;
     private float mYDown;
+    private View mQsb;
+    private boolean mIsEventOverQsb;
+
     final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
     final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
     final static float TOUCH_SLOP_DAMPING_FACTOR = 4;
@@ -309,12 +315,8 @@
         Rect padding = grid.workspacePadding;
         setPadding(padding.left, padding.top, padding.right, padding.bottom);
         mInsets.set(insets);
-        // Increase our bottom insets so we don't overlap with the taskbar.
-        mInsets.bottom += grid.nonOverlappingTaskbarInset;
 
-        if (isTwoPanelEnabled()) {
-            setPageSpacing(0); // we have two pages and we don't want any spacing
-        } else if (mWorkspaceFadeInAdjacentScreens) {
+        if (mWorkspaceFadeInAdjacentScreens) {
             // In landscape mode the page spacing is set to the default.
             setPageSpacing(grid.edgeMarginPx);
         } else {
@@ -326,30 +328,35 @@
             setPageSpacing(Math.max(maxInsets, maxPadding));
         }
 
+        updateWorkspaceScreensPadding();
+    }
+
+    private void updateWorkspaceScreensPadding() {
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx;
         int paddingBottom = grid.cellLayoutBottomPaddingPx;
-        int twoPanelLandscapeSidePadding = paddingLeftRight * 2;
-        int twoPanelPortraitSidePadding = paddingLeftRight / 2;
 
         int panelCount = getPanelCount();
-        for (int i = mWorkspaceScreens.size() - 1; i >= 0; i--) {
+        int rightPanelModulus = mIsRtl ? 0 : panelCount - 1;
+        int leftPanelModulus = mIsRtl ? panelCount - 1 : 0;
+        int numberOfScreens = mScreenOrder.size();
+        for (int i = 0; i < numberOfScreens; i++) {
             int paddingLeft = paddingLeftRight;
             int paddingRight = paddingLeftRight;
+            // Add missing cellLayout border in-between panels.
             if (panelCount > 1) {
-                if (i % panelCount == 0) { // left side panel
-                    paddingLeft = grid.isLandscape ? twoPanelLandscapeSidePadding
-                            : twoPanelPortraitSidePadding;
-                    paddingRight = 0;
-                } else if (i % panelCount == panelCount - 1) { // right side panel
-                    paddingLeft = 0;
-                    paddingRight = grid.isLandscape ? twoPanelLandscapeSidePadding
-                            : twoPanelPortraitSidePadding;
+                if (i % panelCount == leftPanelModulus) {
+                    paddingRight += grid.cellLayoutBorderSpacePx.x / 2;
+                } else if (i % panelCount == rightPanelModulus) { // right side panel
+                    paddingLeft += grid.cellLayoutBorderSpacePx.x / 2;
                 } else { // middle panel
-                    paddingLeft = 0;
-                    paddingRight = 0;
+                    paddingLeft += grid.cellLayoutBorderSpacePx.x / 2;
+                    paddingRight += grid.cellLayoutBorderSpacePx.x / 2;
                 }
             }
-            mWorkspaceScreens.valueAt(i).setPadding(paddingLeft, 0, paddingRight, paddingBottom);
+            // SparseArrayMap doesn't keep the order
+            mWorkspaceScreens.get(mScreenOrder.get(i))
+                    .setPadding(paddingLeft, 0, paddingRight, paddingBottom);
         }
     }
 
@@ -462,7 +469,7 @@
     }
 
     @Override
-    protected int getPanelCount() {
+    public int getPanelCount() {
         return isTwoPanelEnabled() ? 2 : super.getPanelCount();
     }
 
@@ -491,7 +498,6 @@
         });
 
         mDragInfo = null;
-        mOutlineProvider = null;
         mDragSourceInternal = null;
     }
 
@@ -546,19 +552,19 @@
 
     /**
      * Initializes and binds the first page
-     * @param qsb an existing qsb to recycle or null.
      */
-    public void bindAndInitFirstWorkspaceScreen(View qsb) {
+    public void bindAndInitFirstWorkspaceScreen() {
         if (!FeatureFlags.QSB_ON_FIRST_SCREEN) {
             return;
         }
+
         // Add the first page
-        CellLayout firstPage = insertNewWorkspaceScreen(Workspace.FIRST_SCREEN_ID, 0);
+        CellLayout firstPage = insertNewWorkspaceScreen(Workspace.FIRST_SCREEN_ID, getChildCount());
         // Always add a QSB on the first screen.
-        if (qsb == null) {
+        if (mQsb == null) {
             // In transposed layout, we add the QSB in the Grid. As workspace does not touch the
             // edges, we do not need a full width QSB.
-            qsb = LayoutInflater.from(getContext())
+            mQsb = LayoutInflater.from(getContext())
                     .inflate(R.layout.search_container_workspace, firstPage, false);
         }
 
@@ -567,8 +573,9 @@
         CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, firstPage.getCountX(),
                 cellVSpan);
         lp.canReorder = false;
-        if (!firstPage.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true)) {
+        if (!firstPage.addViewToCellLayout(mQsb, 0, R.id.search_container_workspace, lp, true)) {
             Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
+            mQsb = null;
         }
     }
 
@@ -578,9 +585,8 @@
         disableLayoutTransitions();
 
         // Recycle the QSB widget
-        View qsb = findViewById(R.id.search_container_workspace);
-        if (qsb != null) {
-            ((ViewGroup) qsb.getParent()).removeView(qsb);
+        if (mQsb != null) {
+            ((ViewGroup) mQsb.getParent()).removeView(mQsb);
         }
 
         // Remove the pages and clear the screen models
@@ -593,7 +599,7 @@
         mLauncher.mHandler.removeCallbacksAndMessages(DeferredWidgetRefresh.class);
 
         // Ensure that the first page is always present
-        bindAndInitFirstWorkspaceScreen(qsb);
+        bindAndInitFirstWorkspaceScreen();
 
         // Re-enable the layout transitions
         enableLayoutTransitions();
@@ -622,10 +628,6 @@
         // created CellLayout.
         CellLayout newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate(
                         R.layout.workspace_screen, this, false /* attachToRoot */);
-        DeviceProfile grid = mLauncher.getDeviceProfile();
-        int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx;
-        int paddingBottom = grid.cellLayoutBottomPaddingPx;
-        newScreen.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
 
         mWorkspaceScreens.put(screenId, newScreen);
         mScreenOrder.add(insertIndex, screenId);
@@ -634,6 +636,7 @@
                 mLauncher.getStateManager().getState(), newScreen, insertIndex);
 
         updatePageScrollValues();
+        updateWorkspaceScreensPadding();
         return newScreen;
     }
 
@@ -642,18 +645,36 @@
         boolean childOnFinalScreen = false;
 
         if (mDragSourceInternal != null) {
+            int dragSourceChildCount = mDragSourceInternal.getChildCount();
+
+            // If the icon was dragged from Hotseat, there is no page pair
+            if (isTwoPanelEnabled() && !(mDragSourceInternal.getParent() instanceof Hotseat)) {
+                int pagePairScreenId = getScreenPair(dragObject.dragInfo.screenId);
+                CellLayout pagePair = mWorkspaceScreens.get(pagePairScreenId);
+                if (pagePair == null) {
+                    // TODO: after http://b/198820019 is fixed, remove this
+                    throw new IllegalStateException("Page pair is null, "
+                            + "dragScreenId: " + dragObject.dragInfo.screenId
+                            + ", pagePairScreenId: " + pagePairScreenId
+                            + ", mScreenOrder: " + mScreenOrder.toConcatString()
+                    );
+                }
+                dragSourceChildCount += pagePair.getShortcutsAndWidgets().getChildCount();
+            }
+
             // When the drag view content is a LauncherAppWidgetHostView, we should increment the
             // drag source child count by 1 because the widget in drag has been detached from its
             // original parent, ShortcutAndWidgetContainer, and reattached to the DragView.
-            int dragSourceChildCount =
-                    dragObject.dragView.getContentView() instanceof LauncherAppWidgetHostView
-                            ? mDragSourceInternal.getChildCount() + 1
-                            : mDragSourceInternal.getChildCount();
+            if (dragObject.dragView.getContentView() instanceof LauncherAppWidgetHostView) {
+                dragSourceChildCount++;
+            }
+
             if (dragSourceChildCount == 1) {
                 lastChildOnScreen = true;
             }
             CellLayout cl = (CellLayout) mDragSourceInternal.getParent();
-            if (indexOfChild(cl) == getChildCount() - 1) {
+            if (getLeftmostVisiblePageForIndex(indexOfChild(cl))
+                    == getLeftmostVisiblePageForIndex(getPageCount() - 1)) {
                 childOnFinalScreen = true;
             }
         }
@@ -662,40 +683,83 @@
         if (lastChildOnScreen && childOnFinalScreen) {
             return;
         }
-        if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) {
-            insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID);
+
+        forEachExtraEmptyPageId(extraEmptyPageId -> {
+            if (!mWorkspaceScreens.containsKey(extraEmptyPageId)) {
+                insertNewWorkspaceScreen(extraEmptyPageId);
+            }
+        });
+    }
+
+    /**
+     * Inserts extra empty pages to the end of the existing workspaces.
+     * Usually we add one extra empty screen, but when two panel home is enabled we add
+     * two extra screens.
+     **/
+    public void addExtraEmptyScreens() {
+        forEachExtraEmptyPageId(extraEmptyPageId -> {
+            if (!mWorkspaceScreens.containsKey(extraEmptyPageId)) {
+                insertNewWorkspaceScreen(extraEmptyPageId);
+            }
+        });
+    }
+
+    /**
+     * Calls the consumer with all the necessary extra empty page IDs.
+     * On a normal one panel Workspace that means only EXTRA_EMPTY_SCREEN_ID,
+     * but in a two panel scenario this also includes EXTRA_EMPTY_SCREEN_SECOND_ID.
+     */
+    private void forEachExtraEmptyPageId(Consumer<Integer> callback) {
+        callback.accept(EXTRA_EMPTY_SCREEN_ID);
+        if (isTwoPanelEnabled()) {
+            callback.accept(EXTRA_EMPTY_SCREEN_SECOND_ID);
         }
     }
 
-    public boolean addExtraEmptyScreen() {
-        if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) {
-            insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID);
-            return true;
-        }
-        return false;
-    }
-
+    /**
+     * If two panel home is enabled we convert the last two screens that are visible at the same
+     * time. In other cases we only convert the last page.
+     */
     private void convertFinalScreenToEmptyScreenIfNecessary() {
         if (mLauncher.isWorkspaceLoading()) {
             // Invalid and dangerous operation if workspace is loading
             return;
         }
 
-        if (hasExtraEmptyScreen() || mScreenOrder.size() == 0) return;
-        int finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1);
+        int panelCount = getPanelCount();
+        if (hasExtraEmptyScreens() || mScreenOrder.size() < panelCount) {
+            return;
+        }
 
-        CellLayout finalScreen = mWorkspaceScreens.get(finalScreenId);
+        SparseArray<CellLayout> finalScreens = new SparseArray<>();
 
-        // If the final screen is empty, convert it to the extra empty screen
-        if (finalScreen != null
-                && finalScreen.getShortcutsAndWidgets().getChildCount() == 0
-                && !finalScreen.isDropPending()) {
-            mWorkspaceScreens.remove(finalScreenId);
-            mScreenOrder.removeValue(finalScreenId);
+        int pageCount = mScreenOrder.size();
+        // First we add the last page(s) to the finalScreens collection. The number of final pages
+        // depends on the panel count.
+        for (int pageIndex = pageCount - panelCount; pageIndex < pageCount; pageIndex++) {
+            int screenId = mScreenOrder.get(pageIndex);
+            CellLayout screen = mWorkspaceScreens.get(screenId);
+            if (screen == null || screen.getShortcutsAndWidgets().getChildCount() != 0
+                    || screen.isDropPending()) {
+                // Final screen doesn't exist or it isn't empty or there's a pending drop
+                return;
+            }
+            finalScreens.append(screenId, screen);
+        }
 
-            // if this is the last screen, convert it to the empty screen
-            mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, finalScreen);
-            mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
+        // Then we remove the final screens from the collections (but not from the view hierarchy)
+        // and we store them as extra empty screens.
+        for (int i = 0; i < finalScreens.size(); i++) {
+            int screenId = finalScreens.keyAt(i);
+            CellLayout screen = finalScreens.get(screenId);
+
+            mWorkspaceScreens.remove(screenId);
+            mScreenOrder.removeValue(screenId);
+
+            int newScreenId = mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)
+                    ? EXTRA_EMPTY_SCREEN_SECOND_ID : EXTRA_EMPTY_SCREEN_ID;
+            mWorkspaceScreens.put(newScreenId, screen);
+            mScreenOrder.add(newScreenId);
         }
     }
 
@@ -703,6 +767,23 @@
         removeExtraEmptyScreenDelayed(0, stripEmptyScreens, null);
     }
 
+    /**
+     * The purpose of this method is to remove empty pages from Workspace.
+     * Empty page(s) from the end of mWorkspaceScreens will always be removed. The pages with
+     * ID = Workspace.EXTRA_EMPTY_SCREEN_IDS will be removed if there are other non-empty pages.
+     * If there are no more non-empty pages left, extra empty page(s) will either stay or get added.
+     *
+     * If stripEmptyScreens is true, all empty pages (not just the ones on the end) will be removed
+     * from the Workspace, and if there are no more pages left then extra empty page(s) will be
+     * added.
+     *
+     * The number of extra empty pages is equal to what getPanelCount() returns.
+     *
+     * After the method returns the possible pages are:
+     * stripEmptyScreens = true : [non-empty pages, extra empty page(s) alone]
+     * stripEmptyScreens = false : [non-empty pages, empty pages (not in the end),
+     *                             extra empty page(s) alone]
+     */
     public void removeExtraEmptyScreenDelayed(
             int delay, boolean stripEmptyScreens, Runnable onComplete) {
         if (mLauncher.isWorkspaceLoading()) {
@@ -716,18 +797,26 @@
             return;
         }
 
+        // First we convert the last page to an extra page if the last page is empty
+        // and we don't already have an extra page.
         convertFinalScreenToEmptyScreenIfNecessary();
-        if (hasExtraEmptyScreen()) {
-            removeView(mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID));
+        // Then we remove the extra page(s) if they are not the only pages left in Workspace.
+        if (hasExtraEmptyScreens()) {
+            forEachExtraEmptyPageId(extraEmptyPageId -> {
+                removeView(mWorkspaceScreens.get(extraEmptyPageId));
+                mWorkspaceScreens.remove(extraEmptyPageId);
+                mScreenOrder.removeValue(extraEmptyPageId);
+            });
+
             setCurrentPage(getNextPage());
-            mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
-            mScreenOrder.removeValue(EXTRA_EMPTY_SCREEN_ID);
 
             // Update the page indicator to reflect the removed page.
             showPageIndicatorAtCurrentScroll();
         }
 
         if (stripEmptyScreens) {
+            // This will remove all empty pages from the Workspace. If there are no more pages left,
+            // it will add extra page(s) so that users can put items on at least one page.
             stripEmptyScreens();
         }
 
@@ -736,27 +825,51 @@
         }
     }
 
-    public boolean hasExtraEmptyScreen() {
-        return mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) && getChildCount() > 1;
+    public boolean hasExtraEmptyScreens() {
+        return mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)
+                && getChildCount() > getPanelCount()
+                && (!isTwoPanelEnabled()
+                || mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_SECOND_ID));
     }
 
-    public int commitExtraEmptyScreen() {
+    /**
+     *  Commits the extra empty pages then returns the screen ids of those new screens.
+     *  Usually there's only one extra empty screen, but when two panel home is enabled we commit
+     *  two extra screens.
+     *
+     *  Returns an empty IntSet in case we cannot commit any new screens.
+     */
+    public IntSet commitExtraEmptyScreens() {
         if (mLauncher.isWorkspaceLoading()) {
             // Invalid and dangerous operation if workspace is loading
-            return -1;
+            return new IntSet();
         }
 
-        CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
-        mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
-        mScreenOrder.removeValue(EXTRA_EMPTY_SCREEN_ID);
+        IntSet extraEmptyPageIds = new IntSet();
+        forEachExtraEmptyPageId(extraEmptyPageId ->
+                extraEmptyPageIds.add(commitExtraEmptyScreen(extraEmptyPageId)));
 
-        int newId = LauncherSettings.Settings.call(getContext().getContentResolver(),
-                LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
-                .getInt(LauncherSettings.Settings.EXTRA_VALUE);
-        mWorkspaceScreens.put(newId, cl);
-        mScreenOrder.add(newId);
+        return extraEmptyPageIds;
+    }
 
-        return newId;
+    private int commitExtraEmptyScreen(int emptyScreenId) {
+        CellLayout cl = mWorkspaceScreens.get(emptyScreenId);
+        mWorkspaceScreens.remove(emptyScreenId);
+        mScreenOrder.removeValue(emptyScreenId);
+
+        int newScreenId = -1;
+        // Launcher database isn't aware of empty pages that are already bound, so we need to
+        // skip those IDs manually.
+        while (newScreenId == -1 || mWorkspaceScreens.containsKey(newScreenId)) {
+            newScreenId = LauncherSettings.Settings.call(getContext().getContentResolver(),
+                    LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
+                    .getInt(LauncherSettings.Settings.EXTRA_VALUE);
+        }
+
+        mWorkspaceScreens.put(newScreenId, cl);
+        mScreenOrder.add(newScreenId);
+
+        return newScreenId;
     }
 
     @Override
@@ -786,6 +899,10 @@
         return indexOfChild(mWorkspaceScreens.get(screenId));
     }
 
+    public IntSet getCurrentPageScreenIds() {
+        return IntSet.wrap(getScreenIdForPageIndex(getCurrentPage()));
+    }
+
     public int getScreenIdForPageIndex(int index) {
         if (0 <= index && index < mScreenOrder.size()) {
             return mScreenOrder.get(index);
@@ -797,6 +914,34 @@
         return mScreenOrder;
     }
 
+    /**
+     * Returns the screen ID of a page that is shown together with the given page screen ID when the
+     * two panel UI is enabled.
+     */
+    public int getScreenPair(int screenId) {
+        if (screenId % 2 == 0) {
+            return screenId + 1;
+        } else {
+            return screenId - 1;
+        }
+    }
+
+    /**
+     * Returns {@link CellLayout} that is shown together with the given {@link CellLayout} when the
+     * two panel UI is enabled.
+     */
+    @Nullable
+    public CellLayout getScreenPair(CellLayout cellLayout) {
+        if (!isTwoPanelEnabled()) {
+            return null;
+        }
+        int screenId = getIdForScreen(cellLayout);
+        if (screenId == -1) {
+            return null;
+        }
+        return getScreenWithId(getScreenPair(screenId));
+    }
+
     public void stripEmptyScreens() {
         if (mLauncher.isWorkspaceLoading()) {
             // Don't strip empty screens if the workspace is still loading.
@@ -822,9 +967,26 @@
             }
         }
 
-        // We enforce at least one page to add new items to. In the case that we remove the last
-        // such screen, we convert the last screen to the empty screen
-        int minScreens = 1;
+        // When two panel home is enabled we only remove an empty page if both visible pages are
+        // empty.
+        if (isTwoPanelEnabled()) {
+            // We go through all the pages that were marked as removable and check their page pair
+            Iterator<Integer> removeScreensIterator = removeScreens.iterator();
+            while (removeScreensIterator.hasNext()) {
+                int pageToRemove = removeScreensIterator.next();
+                int pagePair = getScreenPair(pageToRemove);
+                if (!removeScreens.contains(pagePair)) {
+                    // The page pair isn't empty so we want to remove the current page from the
+                    // removable pages' collection
+                    removeScreensIterator.remove();
+                }
+            }
+        }
+
+        // We enforce at least one page (two pages on two panel home) to add new items to.
+        // In the case that we remove the last such screen(s), we convert the last screen(s)
+        // to the empty screen(s)
+        int minScreens = getPanelCount();
 
         int pageShift = 0;
         for (int i = 0; i < removeScreens.size(); i++) {
@@ -834,14 +996,21 @@
             mScreenOrder.removeValue(id);
 
             if (getChildCount() > minScreens) {
+                // If this isn't the last page, just remove it
                 if (indexOfChild(cl) < currentPage) {
                     pageShift++;
                 }
                 removeView(cl);
             } else {
-                // if this is the last screen, convert it to the empty screen
-                mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, cl);
-                mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
+                // The last page(s) should be converted into extra empty page(s)
+                int extraScreenId = isTwoPanelEnabled() && id % 2 == 1
+                        // This is the right panel in a two panel scenario
+                        ? EXTRA_EMPTY_SCREEN_SECOND_ID
+                        // This is either the last screen in a one panel scenario, or the left panel
+                        // in a two panel scenario when there are only two empty pages left
+                        : EXTRA_EMPTY_SCREEN_ID;
+                mWorkspaceScreens.put(extraScreenId, cl);
+                mScreenOrder.add(extraScreenId);
             }
         }
 
@@ -887,17 +1056,25 @@
     }
 
     @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            mXDown = ev.getX();
-            mYDown = ev.getY();
+    protected void updateIsBeingDraggedOnTouchDown(MotionEvent ev) {
+        super.updateIsBeingDraggedOnTouchDown(ev);
+
+        mXDown = ev.getX();
+        mYDown = ev.getY();
+        if (mQsb != null) {
+            mTempFXY[0] = mXDown + getScrollX();
+            mTempFXY[1] = mYDown + getScrollY();
+            Utilities.mapCoordInSelfToDescendant(mQsb, this, mTempFXY);
+            mIsEventOverQsb = mQsb.getLeft() <= mTempFXY[0] && mQsb.getRight() >= mTempFXY[0]
+                    && mQsb.getTop() <= mTempFXY[1] && mQsb.getBottom() >= mTempFXY[1];
+        } else {
+            mIsEventOverQsb = false;
         }
-        return super.onInterceptTouchEvent(ev);
     }
 
     @Override
     protected void determineScrollingStart(MotionEvent ev) {
-        if (!isFinishedSwitchingState()) return;
+        if (!isFinishedSwitchingState() || mIsEventOverQsb) return;
 
         float deltaX = ev.getX() - mXDown;
         float absDeltaX = Math.abs(deltaX);
@@ -1323,11 +1500,7 @@
                 position[0], position[1], 0, null);
     }
 
-    public void prepareDragWithProvider(DragPreviewProvider outlineProvider) {
-        mOutlineProvider = outlineProvider;
-    }
-
-    private void onStartStateTransition(LauncherState state) {
+    private void onStartStateTransition() {
         mIsSwitchingState = true;
         mTransitionProgress = 0;
 
@@ -1348,7 +1521,7 @@
      */
     @Override
     public void setState(LauncherState toState) {
-        onStartStateTransition(toState);
+        onStartStateTransition();
         mStateTransitionAnimation.setState(toState);
         onEndStateTransition();
     }
@@ -1359,7 +1532,7 @@
     @Override
     public void setStateWithAnimation(
             LauncherState toState, StateAnimationConfig config, PendingAnimation animation) {
-        StateTransitionListener listener = new StateTransitionListener(toState);
+        StateTransitionListener listener = new StateTransitionListener();
         mStateTransitionAnimation.setStateWithAnimation(toState, config, animation);
 
         // Invalidate the pages now, so that we have the visible pages before the
@@ -1468,8 +1641,6 @@
             icon.clearPressedBackground();
         }
 
-        mOutlineProvider = previewProvider;
-
         if (draggableView == null && child instanceof DraggableView) {
             draggableView = (DraggableView) child;
         }
@@ -1505,7 +1676,7 @@
         }
 
         if (child instanceof BubbleTextView && !dragOptions.isAccessibleDrag) {
-            PopupContainerWithArrow popupContainer = PopupContainerWithArrow
+            PopupContainerWithArrow<Launcher> popupContainer = PopupContainerWithArrow
                     .showForIcon((BubbleTextView) child);
             if (popupContainer != null) {
                 dragOptions.preDragCondition = popupContainer.createPreDragCondition();
@@ -1611,14 +1782,14 @@
 
             // Don't accept the drop if there's no room for the item
             if (!foundCell) {
-                onNoCellFound(dropTargetLayout);
+                onNoCellFound(dropTargetLayout, d.dragInfo, d.logInstanceId);
                 return false;
             }
         }
 
         int screenId = getIdForScreen(dropTargetLayout);
-        if (screenId == EXTRA_EMPTY_SCREEN_ID) {
-            commitExtraEmptyScreen();
+        if (Workspace.EXTRA_EMPTY_SCREEN_IDS.contains(screenId)) {
+            commitExtraEmptyScreens();
         }
 
         return true;
@@ -1779,19 +1950,16 @@
 
         boolean droppedOnOriginalCell = false;
 
-        int snapScreen = -1;
+        boolean snappedToNewPage = false;
         boolean resizeOnDrop = false;
+        Runnable onCompleteRunnable = null;
         if (d.dragSource != this || mDragInfo == null) {
             final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
                     (int) mDragViewVisualCenter[1] };
             onDropExternal(touchXY, dropTargetLayout, d);
         } else {
             final View cell = mDragInfo.cell;
-            final DragView dragView = d.dragView;
             boolean droppedOnOriginalCellDuringTransition = false;
-            Runnable onCompleteRunnable = dragView::resumeColorExtraction;
-
-            dragView.disableColorExtraction();
 
             if (dropTargetLayout != null && !d.cancelled) {
                 // Move internally
@@ -1864,11 +2032,14 @@
                 }
 
                 if (foundCell) {
-                    if (getScreenIdForPageIndex(mCurrentPage) != screenId && !hasMovedIntoHotseat) {
-                        snapScreen = getPageIndexForScreenId(screenId);
+                    int targetScreenIndex = getPageIndexForScreenId(screenId);
+                    int snapScreen = getLeftmostVisiblePageForIndex(targetScreenIndex);
+                    // On large screen devices two pages can be shown at the same time, and snap
+                    // isn't needed if the source and target screens appear at the same time
+                    if (snapScreen != mCurrentPage && !hasMovedIntoHotseat) {
                         snapToPage(snapScreen);
+                        snappedToNewPage = true;
                     }
-
                     final ItemInfo info = (ItemInfo) cell.getTag();
                     if (hasMovedLayouts) {
                         // Reparent the view
@@ -1902,9 +2073,7 @@
                         AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo();
                         if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE
                                 && !options.isAccessibleDrag) {
-                            final Runnable previousRunnable = onCompleteRunnable;
                             onCompleteRunnable = () -> {
-                                previousRunnable.run();
                                 if (!isPageInTransition()) {
                                     AppWidgetResizeFrame.showForWidget(hostView, cellLayout);
                                 }
@@ -1915,7 +2084,7 @@
                             lp.cellX, lp.cellY, item.spanX, item.spanY);
                 } else {
                     if (!returnToOriginalCellToPreventShuffling) {
-                        onNoCellFound(dropTargetLayout);
+                        onNoCellFound(dropTargetLayout, d.dragInfo, d.logInstanceId);
                     }
                     if (mDragInfo.cell instanceof LauncherAppWidgetHostView) {
                         d.dragView.detachContentView(/* reattachToPreviousParent= */ true);
@@ -1962,7 +2131,7 @@
                             ANIMATE_INTO_POSITION_AND_DISAPPEAR;
                     animateWidgetDrop(info, parent, d.dragView, null, animationType, cell, false);
                 } else {
-                    int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION;
+                    int duration = snappedToNewPage ? ADJACENT_SCREEN_DROP_DURATION : -1;
                     mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
                             this);
                 }
@@ -1973,7 +2142,7 @@
             parent.onDropChild(cell);
 
             mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY,
-                    forSuccessCallback(onCompleteRunnable));
+                    onCompleteRunnable == null ? null : forSuccessCallback(onCompleteRunnable));
             mStatsLogManager.logger().withItemInfo(d.dragInfo).withInstanceId(d.logInstanceId)
                     .log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
         }
@@ -1983,10 +2152,16 @@
         }
     }
 
-    public void onNoCellFound(View dropTargetLayout) {
+    public void onNoCellFound(
+            View dropTargetLayout, ItemInfo itemInfo, @Nullable InstanceId logInstanceId) {
         int strId = mLauncher.isHotseatLayout(dropTargetLayout)
                 ? R.string.hotseat_out_of_space : R.string.out_of_space;
         Toast.makeText(mLauncher, mLauncher.getString(strId), Toast.LENGTH_SHORT).show();
+        StatsLogManager.StatsLogger logger = mStatsLogManager.logger().withItemInfo(itemInfo);
+        if (logInstanceId != null) {
+            logger = logger.withInstanceId(logInstanceId);
+        }
+        logger.log(LauncherEvent.LAUNCHER_ITEM_DROP_FAILED_INSUFFICIENT_SPACE);
     }
 
     /**
@@ -2290,30 +2465,32 @@
         }
 
         int nextPage = getNextPage();
-        if (layout == null && !isPageInTransition()) {
-            // Check if the item is dragged over currentPage - 1 page
-            mTempTouchCoordinates[0] = Math.min(centerX, d.x);
-            mTempTouchCoordinates[1] = d.y;
-            layout = verifyInsidePage(nextPage + (mIsRtl ? 1 : -1), mTempTouchCoordinates);
+        IntSet pageIndexesToVerify = IntSet.wrap(nextPage - 1, nextPage + 1);
+        if (isTwoPanelEnabled()) {
+            // If two panel is enabled, users can also drag items to nextPage + 2
+            pageIndexesToVerify.add(nextPage + 2);
         }
 
-        if (layout == null && !isPageInTransition()) {
-            // Check if the item is dragged over currentPage + 1 page
-            mTempTouchCoordinates[0] = Math.max(centerX, d.x);
-            mTempTouchCoordinates[1] = d.y;
-            layout = verifyInsidePage(nextPage + (mIsRtl ? -1 : 1), mTempTouchCoordinates);
+        int touchX = (int) Math.min(centerX, d.x);
+        int touchY = d.y;
+
+        // Go through the pages and check if the dragged item is inside one of them
+        for (int pageIndex : pageIndexesToVerify) {
+            if (layout != null || isPageInTransition()) {
+                break;
+            }
+            layout = verifyInsidePage(pageIndex, touchX, touchY);
         }
 
-        // If two panel is enabled, users can also drag items to currentPage + 2
-        if (isTwoPanelEnabled() && layout == null && !isPageInTransition()) {
-            // Check if the item is dragged over currentPage + 2 page
-            mTempTouchCoordinates[0] = Math.max(centerX, d.x);
-            mTempTouchCoordinates[1] = d.y;
-            layout = verifyInsidePage(nextPage + (mIsRtl ? -2 : 2), mTempTouchCoordinates);
-        }
-
-        // Always pick the current page.
+        // If the dragged item isn't located in one of the pages above, the icon will stay on the
+        // current screen. For two panel pick the closest panel on the current screen,
+        // on one panel just choose the current page.
         if (layout == null && nextPage >= 0 && nextPage < getPageCount()) {
+            if (isTwoPanelEnabled()) {
+                nextPage = getScreenCenter(getScrollX()) > touchX
+                        ? (mIsRtl ? nextPage + 1 : nextPage) // left side
+                        : (mIsRtl ? nextPage : nextPage + 1); // right side
+            }
             layout = (CellLayout) getChildAt(nextPage);
         }
         if (layout != mDragTargetLayout) {
@@ -2327,12 +2504,11 @@
     /**
      * Returns the child CellLayout if the point is inside the page coordinates, null otherwise.
      */
-    private CellLayout verifyInsidePage(int pageNo, float[] touchXy)  {
+    private CellLayout verifyInsidePage(int pageNo, float x, float y) {
         if (pageNo >= 0 && pageNo < getPageCount()) {
             CellLayout cl = (CellLayout) getChildAt(pageNo);
-            mapPointFromSelfToChild(cl, touchXy);
-            if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() &&
-                    touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) {
+            if (x >= cl.getLeft() && x <= cl.getRight()
+                    && y >= cl.getTop() && y <= cl.getBottom()) {
                 // This point is inside the cell layout
                 return cl;
             }
@@ -2560,11 +2736,17 @@
                 case ITEM_TYPE_APPLICATION:
                 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                 case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+                case LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION:
                     if (info instanceof AppInfo) {
                         // Came from all apps -- make a copy
                         info = ((AppInfo) info).makeWorkspaceItem();
                         d.dragInfo = info;
                     }
+                    if (info instanceof SearchActionItemInfo) {
+                        info = ((SearchActionItemInfo) info).createWorkspaceItem(
+                                mLauncher.getModel());
+                        d.dragInfo = info;
+                    }
                     view = mLauncher.createShortcut(cellLayout, (WorkspaceItemInfo) info);
                     break;
                 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
@@ -2691,9 +2873,6 @@
     public void animateWidgetDrop(ItemInfo info, CellLayout cellLayout, final DragView dragView,
             final Runnable onCompleteRunnable, int animationType, final View finalView,
             boolean external) {
-        Rect from = new Rect();
-        mLauncher.getDragLayer().getViewRectRelativeToSelf(dragView, from);
-
         int[] finalPos = new int[2];
         float scaleXY[] = new float[2];
         boolean scalePreview = !(info instanceof PendingAddShortcutInfo);
@@ -2737,8 +2916,8 @@
                     }
                 }
             };
-            dragLayer.animateViewIntoPosition(dragView, from.left, from.top, finalPos[0],
-                    finalPos[1], 1, 1, 1, scaleXY[0], scaleXY[1], onComplete, endStyle,
+            dragLayer.animateViewIntoPosition(dragView, finalPos[0],
+                    finalPos[1], 1, scaleXY[0], scaleXY[1], onComplete, endStyle,
                     duration, this);
         }
     }
@@ -2966,9 +3145,9 @@
      * @param user The user of the app to match.
      */
     public View getFirstMatchForAppClose(int preferredItemId, String packageName, UserHandle user) {
-        final Workspace.ItemOperator preferredItem = (ItemInfo info, View view) ->
+        final ItemOperator preferredItem = (ItemInfo info, View view) ->
                 info != null && info.id == preferredItemId;
-        final Workspace.ItemOperator preferredItemInFolder = (info, view) -> {
+        final ItemOperator preferredItemInFolder = (info, view) -> {
             if (info instanceof FolderInfo) {
                 FolderInfo folderInfo = (FolderInfo) info;
                 for (WorkspaceItemInfo shortcutInfo : folderInfo.contents) {
@@ -2979,14 +3158,14 @@
             }
             return false;
         };
-        final Workspace.ItemOperator packageAndUserAndApp = (ItemInfo info, View view) ->
+        final ItemOperator packageAndUserAndApp = (ItemInfo info, View view) ->
                 info != null
                         && info.itemType == ITEM_TYPE_APPLICATION
                         && info.user.equals(user)
                         && info.getTargetComponent() != null
                         && TextUtils.equals(info.getTargetComponent().getPackageName(),
                                 packageName);
-        final Workspace.ItemOperator packageAndUserAndAppInFolder = (info, view) -> {
+        final ItemOperator packageAndUserAndAppInFolder = (info, view) -> {
             if (info instanceof FolderInfo) {
                 FolderInfo folderInfo = (FolderInfo) info;
                 for (WorkspaceItemInfo shortcutInfo : folderInfo.contents) {
@@ -3105,22 +3284,7 @@
         stripEmptyScreens();
     }
 
-    public 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);
-    }
-
-    /**
-     * Map the operator over the shortcuts and widgets, return the first-non-null value.
-     *
-     * @param op the operator to map over the shortcuts
-     */
+    @Override
     public void mapOverItems(ItemOperator op) {
         for (CellLayout layout : getWorkspaceAndHotseatCellLayouts()) {
             if (mapOverCellLayout(layout, op) != null) {
@@ -3146,31 +3310,6 @@
         return null;
     }
 
-    void updateShortcuts(List<WorkspaceItemInfo> shortcuts) {
-        final HashSet<WorkspaceItemInfo> updates = new HashSet<>(shortcuts);
-        ItemOperator op = (info, v) -> {
-            if (v instanceof BubbleTextView && updates.contains(info)) {
-                WorkspaceItemInfo si = (WorkspaceItemInfo) info;
-                BubbleTextView shortcut = (BubbleTextView) v;
-                Drawable oldIcon = shortcut.getIcon();
-                boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable)
-                        && ((PreloadIconDrawable) oldIcon).hasNotCompleted();
-                shortcut.applyFromWorkspaceItem(si, si.isPromise() != oldPromiseState);
-            } else if (info instanceof FolderInfo && v instanceof FolderIcon) {
-                ((FolderIcon) v).updatePreviewItems(updates::contains);
-            }
-
-            // Iterate all items
-            return false;
-        };
-
-        mapOverItems(op);
-        Folder openFolder = Folder.getOpen(mLauncher);
-        if (openFolder != null) {
-            openFolder.iterateOverItems(op);
-        }
-    }
-
     public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
         final PackageUserKey packageUserKey = new PackageUserKey(null, null);
         Predicate<ItemInfo> matcher = info -> !packageUserKey.updateFromItemInfo(info)
@@ -3210,28 +3349,6 @@
         removeItemsByMatcher(matcher);
     }
 
-    public void updateRestoreItems(final HashSet<ItemInfo> updates) {
-        ItemOperator op = (info, v) -> {
-            if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView
-                    && updates.contains(info)) {
-                ((BubbleTextView) v).applyLoadingState(false /* promiseStateChanged */);
-            } else if (v instanceof PendingAppWidgetHostView
-                    && info instanceof LauncherAppWidgetInfo
-                    && updates.contains(info)) {
-                ((PendingAppWidgetHostView) v).applyState();
-            } else if (v instanceof FolderIcon && info instanceof FolderInfo) {
-                ((FolderIcon) v).updatePreviewItems(updates::contains);
-            }
-            // process all the shortcuts
-            return false;
-        };
-        mapOverItems(op);
-        Folder folder = Folder.getOpen(mLauncher);
-        if (folder != null) {
-            folder.iterateOverItems(op);
-        }
-    }
-
     public void widgetsRestored(final ArrayList<LauncherAppWidgetInfo> changedInfo) {
         if (!changedInfo.isEmpty()) {
             DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo,
@@ -3396,12 +3513,6 @@
     private class StateTransitionListener extends AnimatorListenerAdapter
             implements AnimatorUpdateListener {
 
-        private final LauncherState mToState;
-
-        StateTransitionListener(LauncherState toState) {
-            mToState = toState;
-        }
-
         @Override
         public void onAnimationUpdate(ValueAnimator anim) {
             mTransitionProgress = anim.getAnimatedFraction();
@@ -3409,7 +3520,7 @@
 
         @Override
         public void onAnimationStart(Animator animation) {
-            onStartStateTransition(mToState);
+            onStartStateTransition();
         }
 
         @Override
diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java
index d6302ce..7e6e1b6 100644
--- a/src/com/android/launcher3/WorkspaceLayoutManager.java
+++ b/src/com/android/launcher3/WorkspaceLayoutManager.java
@@ -23,6 +23,7 @@
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.touch.ItemLongClickListener;
+import com.android.launcher3.util.IntSet;
 
 public interface WorkspaceLayoutManager {
 
@@ -30,8 +31,16 @@
 
     // The screen id used for the empty screen always present at the end.
     int EXTRA_EMPTY_SCREEN_ID = -201;
+    // The screen id used for the second empty screen always present at the end for two panel home.
+    int EXTRA_EMPTY_SCREEN_SECOND_ID = -200;
+    // The screen ids used for the empty screens at the end of the workspaces.
+    IntSet EXTRA_EMPTY_SCREEN_IDS =
+            IntSet.wrap(EXTRA_EMPTY_SCREEN_ID, EXTRA_EMPTY_SCREEN_SECOND_ID);
+
     // The is the first screen. It is always present, even if its empty.
     int FIRST_SCREEN_ID = 0;
+    // This is the second page. On two panel home it is always present, even if its empty.
+    int SECOND_SCREEN_ID = 1;
 
     /**
      * At bind time, we use the rank (screenId) to compute x and y for hotseat items.
@@ -79,9 +88,9 @@
                 return;
             }
         }
-        if (screenId == EXTRA_EMPTY_SCREEN_ID) {
+        if (EXTRA_EMPTY_SCREEN_IDS.contains(screenId)) {
             // This should never happen
-            throw new RuntimeException("Screen id should not be EXTRA_EMPTY_SCREEN_ID");
+            throw new RuntimeException("Screen id should not be extra empty screen: " + screenId);
         }
 
         final CellLayout layout;
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 9faac5b..157df5d 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -45,6 +45,7 @@
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ShortcutUtil;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.views.OptionsPopupView;
@@ -161,7 +162,8 @@
             }
         }
 
-        if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) {
+        if ((item instanceof AppInfo) || (item instanceof WorkspaceItemInfo)
+                || (item instanceof PendingAddItemInfo)) {
             out.add(mActions.get(ADD_TO_WORKSPACE));
         }
     }
@@ -221,6 +223,9 @@
         } else if (action == ADD_TO_WORKSPACE) {
             final int[] coordinates = new int[2];
             final int screenId = findSpaceOnWorkspace(item, coordinates);
+            if (screenId == -1) {
+                return false;
+            }
             mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> {
                 if (item instanceof AppInfo) {
                     WorkspaceItemInfo info = ((AppInfo) item).makeWorkspaceItem();
@@ -240,6 +245,13 @@
                     mLauncher.addPendingItem(info, Favorites.CONTAINER_DESKTOP,
                             screenId, coordinates, info.spanX, info.spanY);
                 }
+                else if (item instanceof WorkspaceItemInfo) {
+                    WorkspaceItemInfo info = ((WorkspaceItemInfo) item).clone();
+                    mLauncher.getModelWriter().addItemToDatabase(info,
+                            Favorites.CONTAINER_DESKTOP,
+                            screenId, coordinates[0], coordinates[1]);
+                    mLauncher.bindItems(Collections.singletonList(info), true, true);
+                }
             }));
             return true;
         } else if (action == MOVE_TO_WORKSPACE) {
@@ -250,6 +262,9 @@
 
             final int[] coordinates = new int[2];
             final int screenId = findSpaceOnWorkspace(item, coordinates);
+            if (screenId == -1) {
+                return false;
+            }
             mLauncher.getModelWriter().moveItemInDatabase(info,
                     Favorites.CONTAINER_DESKTOP,
                     screenId, coordinates[0], coordinates[1]);
@@ -489,8 +504,14 @@
             return screenId;
         }
 
-        workspace.addExtraEmptyScreen();
-        screenId = workspace.commitExtraEmptyScreen();
+        workspace.addExtraEmptyScreens();
+        IntSet emptyScreenIds = workspace.commitExtraEmptyScreens();
+        if (emptyScreenIds.isEmpty()) {
+            // Couldn't create extra empty screens for some reason (e.g. Workspace is loading)
+            return -1;
+        }
+
+        screenId = emptyScreenIds.getArray().get(0);
         layout = workspace.getScreenWithId(screenId);
         found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY);
 
diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
index f96afa8..bf5a24b 100644
--- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
@@ -68,6 +68,9 @@
             final WorkspaceItemInfo info = ((DeepShortcutView) host.getParent()).getFinalInfo();
             final int[] coordinates = new int[2];
             final int screenId = findSpaceOnWorkspace(item, coordinates);
+            if (screenId == -1) {
+                return false;
+            }
             mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> {
                 mLauncher.getModelWriter().addItemToDatabase(info,
                         LauncherSettings.Favorites.CONTAINER_DESKTOP,
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index e779ee8..3ba6ea4 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -17,15 +17,11 @@
 
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB;
-import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
-import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
-import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
@@ -34,7 +30,7 @@
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.Process;
-import android.text.Selection;
+import android.os.UserManager;
 import android.text.SpannableStringBuilder;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -59,7 +55,6 @@
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.ExtendedEditText;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.R;
@@ -68,7 +63,6 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.keyboard.FocusedItemDecorator;
 import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.RecyclerViewFastScroller;
@@ -89,11 +83,12 @@
     public static final float FLING_VELOCITY_MULTIPLIER = 1200f;
 
     private final Paint mHeaderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    private final Rect mInsets = new Rect();
 
     protected final BaseDraggingActivity mLauncher;
     protected final AdapterHolder[] mAH;
-    private final ItemInfoMatcher mPersonalMatcher = ItemInfoMatcher.ofUser(Process.myUserHandle());
-    private final ItemInfoMatcher mWorkMatcher = mPersonalMatcher.negate();
+    protected final ItemInfoMatcher mPersonalMatcher = ItemInfoMatcher.ofUser(
+            Process.myUserHandle());
     private final AllAppsStore mAllAppsStore = new AllAppsStore();
 
     private final RecyclerView.OnScrollListener mScrollListener =
@@ -103,6 +98,8 @@
                     updateHeaderScroll(((AllAppsRecyclerView) recyclerView).getCurrentScrollY());
                 }
             };
+    private final WorkProfileManager mWorkManager;
+
 
     private final Paint mNavBarScrimPaint;
     private int mNavBarScrimHeight = 0;
@@ -112,8 +109,6 @@
     private AllAppsPagedView mViewPager;
 
     protected FloatingHeaderView mHeader;
-    private float mHeaderTop;
-    private WorkModeSwitch mWorkModeSwitch;
 
 
     private SpannableStringBuilder mSearchQueryBuilder = null;
@@ -125,10 +120,7 @@
     protected RecyclerViewFastScroller mTouchHandler;
     protected final Point mFastScrollerOffset = new Point();
 
-    private Rect mInsets = new Rect();
-
     private SearchAdapterProvider mSearchAdapterProvider;
-    private WorkAdapterProvider mWorkAdapterProvider;
     private final int mScrimColor;
     private final int mHeaderProtectionColor;
     private final float mHeaderThreshold;
@@ -157,15 +149,11 @@
         mLauncher.addOnDeviceProfileChangeListener(this);
 
         mSearchAdapterProvider = mLauncher.createSearchAdapterProvider(this);
-        mSearchQueryBuilder = new SpannableStringBuilder();
-        Selection.setSelection(mSearchQueryBuilder, 0);
 
         mAH = new AdapterHolder[2];
-        mWorkAdapterProvider = new WorkAdapterProvider(mLauncher, () -> {
-            if (mAH[AdapterHolder.WORK] != null) {
-                mAH[AdapterHolder.WORK].appsList.updateAdapterItems();
-            }
-        });
+
+        mWorkManager = new WorkProfileManager(mLauncher.getSystemService(UserManager.class), this,
+                Utilities.getPrefs(mLauncher));
         mAH[AdapterHolder.MAIN] = new AdapterHolder(false /* isWork */);
         mAH[AdapterHolder.WORK] = new AdapterHolder(true /* isWork */);
 
@@ -221,8 +209,8 @@
         return mAllAppsStore;
     }
 
-    public WorkModeSwitch getWorkModeSwitch() {
-        return mWorkModeSwitch;
+    public WorkProfileManager getWorkManager() {
+        return mWorkManager;
     }
 
     @Override
@@ -241,7 +229,7 @@
     private void onAppsUpdated() {
         boolean hasWorkApps = false;
         for (AppInfo app : mAllAppsStore.getApps()) {
-            if (mWorkMatcher.matches(app, null)) {
+            if (mWorkManager.getMatcher().matches(app, null)) {
                 hasWorkApps = true;
                 break;
             }
@@ -249,20 +237,12 @@
         mHasWorkApps = hasWorkApps;
         if (!mAH[AdapterHolder.MAIN].appsList.hasFilter()) {
             rebindAdapters();
-            if (mHasWorkApps) {
-                resetWorkProfile();
+            if (hasWorkApps) {
+                mWorkManager.reset();
             }
         }
     }
 
-    private void resetWorkProfile() {
-        boolean isEnabled = !mAllAppsStore.hasModelFlag(FLAG_QUIET_MODE_ENABLED);
-        if (mWorkModeSwitch != null) {
-            mWorkModeSwitch.updateCurrentState(isEnabled);
-        }
-        mWorkAdapterProvider.updateCurrentState(isEnabled);
-    }
-
     /**
      * Returns whether the view itself will handle the touch event or not.
      */
@@ -401,12 +381,10 @@
     public void setInsets(Rect insets) {
         mInsets.set(insets);
         DeviceProfile grid = mLauncher.getDeviceProfile();
-        int leftRightPadding = grid.desiredWorkspaceLeftRightMarginPx
-                + grid.cellLayoutPaddingLeftRightPx;
 
         for (int i = 0; i < mAH.length; i++) {
             mAH[i].padding.bottom = insets.bottom;
-            mAH[i].padding.left = mAH[i].padding.right = leftRightPadding;
+            mAH[i].padding.left = mAH[i].padding.right = grid.allAppsLeftRightPadding;
             mAH[i].applyPadding();
         }
 
@@ -427,8 +405,7 @@
     @Override
     public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
         if (Utilities.ATLEAST_Q) {
-            mNavBarScrimHeight = insets.getTappableElementInsets().bottom
-                    - mLauncher.getDeviceProfile().nonOverlappingTaskbarInset;
+            mNavBarScrimHeight = insets.getTappableElementInsets().bottom;
         } else {
             mNavBarScrimHeight = insets.getStableInsetBottom();
         }
@@ -462,7 +439,7 @@
 
         if (mUsingTabs) {
             mAH[AdapterHolder.MAIN].setup(mViewPager.getChildAt(0), mPersonalMatcher);
-            mAH[AdapterHolder.WORK].setup(mViewPager.getChildAt(1), mWorkMatcher);
+            mAH[AdapterHolder.WORK].setup(mViewPager.getChildAt(1), mWorkManager.getMatcher());
             mAH[AdapterHolder.WORK].recyclerView.setId(R.id.apps_list_view_work);
             mViewPager.getPageIndicator().setActiveMarker(AdapterHolder.MAIN);
             findViewById(R.id.tab_personal)
@@ -490,34 +467,12 @@
         mAllAppsStore.registerIconContainer(mAH[AdapterHolder.WORK].recyclerView);
     }
 
-    private void setupWorkToggle() {
-        removeWorkToggle();
-        if (Utilities.ATLEAST_P) {
-            mWorkModeSwitch = (WorkModeSwitch) mLauncher.getLayoutInflater().inflate(
-                    R.layout.work_mode_fab, this, false);
-            this.addView(mWorkModeSwitch);
-            mWorkModeSwitch.setInsets(mInsets);
-            mWorkModeSwitch.post(() -> {
-                mAH[AdapterHolder.WORK].applyPadding();
-                resetWorkProfile();
-            });
-        }
-    }
-
-    private void removeWorkToggle() {
-        if (mWorkModeSwitch == null) return;
-        if (mWorkModeSwitch.getParent() == this) {
-            this.removeView(mWorkModeSwitch);
-        }
-        mWorkModeSwitch = null;
-    }
 
     private void replaceRVContainer(boolean showTabs) {
-        for (int i = 0; i < mAH.length; i++) {
-            AllAppsRecyclerView rv = mAH[i].recyclerView;
-            if (rv != null) {
-                rv.setLayoutManager(null);
-                rv.setAdapter(null);
+        for (AdapterHolder adapterHolder : mAH) {
+            if (adapterHolder.recyclerView != null) {
+                adapterHolder.recyclerView.setLayoutManager(null);
+                adapterHolder.recyclerView.setAdapter(null);
             }
         }
         View oldView = getRecyclerViewContainer();
@@ -526,18 +481,16 @@
         int layout = showTabs ? R.layout.all_apps_tabs : R.layout.all_apps_rv_layout;
         View newView = getLayoutInflater().inflate(layout, this, false);
         addView(newView, index);
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.WORK_PROFILE_REMOVED, "should show tabs:" + showTabs,
-                    new Exception());
-        }
         if (showTabs) {
             mViewPager = (AllAppsPagedView) newView;
             mViewPager.initParentViews(this);
             mViewPager.getPageIndicator().setOnActivePageChangedListener(this);
-            setupWorkToggle();
+            if (mWorkManager.attachWorkModeSwitch()) {
+                mWorkManager.getWorkModeSwitch().post(() -> mAH[AdapterHolder.WORK].applyPadding());
+            }
         } else {
+            mWorkManager.detachWorkModeSwitch();
             mViewPager = null;
-            removeWorkToggle();
         }
     }
 
@@ -552,11 +505,8 @@
             mAH[currentActivePage].recyclerView.bindFastScrollbar();
         }
         reset(true /* animate */);
-        if (mWorkModeSwitch != null) {
-            mWorkModeSwitch.setWorkTabVisible(currentActivePage == AdapterHolder.WORK
-                    && mAllAppsStore.hasModelFlag(
-                    FLAG_HAS_SHORTCUT_PERMISSION | FLAG_QUIET_MODE_CHANGE_PERMISSION));
-        }
+
+        mWorkManager.onActivePageChanged(currentActivePage);
     }
 
     // Used by tests only
@@ -624,8 +574,10 @@
         for (int i = 0; i < mAH.length; i++) {
             mAH[i].padding.top = padding;
             mAH[i].applyPadding();
+            if (mAH[i].recyclerView != null) {
+                mAH[i].recyclerView.scrollToTop();
+            }
         }
-        mHeaderTop = mHeader.getTop();
     }
 
     public void setLastSearchQuery(String query) {
@@ -639,8 +591,9 @@
 
     public void onClearSearchResult() {
         mIsSearching = false;
+        mHeader.setCollapsed(false);
         rebindAdapters();
-        getActiveRecyclerView().scrollToTop();
+        mHeader.reset(false);
     }
 
     public void onSearchResultsChanged() {
@@ -711,6 +664,7 @@
 
     @Override
     public void drawOnScrim(Canvas canvas) {
+        if (!mHeader.isHeaderProtectionSupported()) return;
         mHeaderPaint.setColor(mHeaderColor);
         mHeaderPaint.setAlpha((int) (getAlpha() * Color.alpha(mHeaderColor)));
         if (mHeaderPaint.getColor() != mScrimColor && mHeaderPaint.getColor() != 0) {
@@ -728,7 +682,6 @@
         public static final int MAIN = 0;
         public static final int WORK = 1;
 
-        private ItemInfoMatcher mInfoMatcher;
         private final boolean mIsWork;
         public final AllAppsGridAdapter adapter;
         final LinearLayoutManager layoutManager;
@@ -736,17 +689,16 @@
         final Rect padding = new Rect();
         AllAppsRecyclerView recyclerView;
         boolean verticalFadingEdge;
-        private View mOverlay;
 
-        boolean mWorkDisabled;
 
         AdapterHolder(boolean isWork) {
             mIsWork = isWork;
             appsList = new AlphabeticalAppsList(mLauncher, mAllAppsStore,
-                    isWork ? mWorkAdapterProvider : null);
+                    isWork ? mWorkManager.getAdapterProvider() : null);
 
             BaseAdapterProvider[] adapterProviders =
-                    isWork ? new BaseAdapterProvider[]{mSearchAdapterProvider, mWorkAdapterProvider}
+                    isWork ? new BaseAdapterProvider[]{mSearchAdapterProvider,
+                            mWorkManager.getAdapterProvider()}
                             : new BaseAdapterProvider[]{mSearchAdapterProvider};
 
             adapter = new AllAppsGridAdapter(mLauncher, getLayoutInflater(), appsList,
@@ -756,7 +708,6 @@
         }
 
         void setup(@NonNull View rv, @Nullable ItemInfoMatcher matcher) {
-            mInfoMatcher = matcher;
             appsList.updateItemFilter(matcher);
             recyclerView = (AllAppsRecyclerView) rv;
             recyclerView.setEdgeEffectFactory(createEdgeEffectFactory());
@@ -779,12 +730,10 @@
 
         void applyPadding() {
             if (recyclerView != null) {
-                Resources res = getResources();
-                int switchH = res.getDimensionPixelSize(R.dimen.work_profile_footer_padding) * 2
-                        + mInsets.bottom + Utilities.calculateTextHeight(
-                        res.getDimension(R.dimen.work_profile_footer_text_size));
-
-                int bottomOffset = mWorkModeSwitch != null && mIsWork ? switchH : 0;
+                int bottomOffset = 0;
+                if (mIsWork && mWorkManager.getWorkModeSwitch() != null) {
+                    bottomOffset = mInsets.bottom + mWorkManager.getWorkModeSwitch().getHeight();
+                }
                 recyclerView.setPadding(padding.left, padding.top, padding.right,
                         padding.bottom + bottomOffset);
             }
@@ -814,14 +763,13 @@
             invalidateHeader();
         }
         if (mSearchUiManager.getEditText() != null) {
-            ExtendedEditText editText = mSearchUiManager.getEditText();
-            boolean bgVisible = editText.getBackgroundVisibility();
+            boolean bgVisible = mSearchUiManager.getBackgroundVisibility();
             if (scrolledOffset == 0 && !mIsSearching) {
                 bgVisible = true;
             } else if (scrolledOffset > mHeaderThreshold) {
                 bgVisible = false;
             }
-            editText.setBackgroundVisibility(bgVisible, 1 - prog);
+            mSearchUiManager.setBackgroundVisibility(bgVisible, 1 - prog);
         }
     }
 
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index bddbbd0..bccd9b4 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -21,6 +21,7 @@
 
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END;
+import static com.android.launcher3.util.LogConfig.SEARCH_LOGGING;
 import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync;
 
 import android.content.Context;
@@ -40,6 +41,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.views.ActivityContext;
@@ -54,6 +56,7 @@
 public class AllAppsRecyclerView extends BaseRecyclerView {
     private static final String TAG = "AllAppsContainerView";
     private static final boolean DEBUG = false;
+    private static final boolean DEBUG_LATENCY = Utilities.isPropertyEnabled(SEARCH_LOGGING);
 
     private AlphabeticalAppsList mApps;
     private final int mNumAppsPerRow;
@@ -133,6 +136,10 @@
         if (DEBUG) {
             Log.d(TAG, "onDraw at = " + System.currentTimeMillis());
         }
+        if (DEBUG_LATENCY) {
+            Log.d(SEARCH_LOGGING,
+                    "-- Recycle view onDraw, time stamp = " + System.currentTimeMillis());
+        }
         super.onDraw(c);
     }
 
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index a0551f0..fc78bea 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
+import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
@@ -28,6 +29,7 @@
 import android.animation.Animator.AnimatorListener;
 import android.animation.ObjectAnimator;
 import android.util.FloatProperty;
+import android.view.HapticFeedbackConstants;
 import android.view.View;
 import android.view.animation.Interpolator;
 
@@ -37,7 +39,6 @@
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorListeners;
-import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.config.FeatureFlags;
@@ -59,7 +60,6 @@
         implements StateHandler<LauncherState>, OnDeviceProfileChangeListener {
     // This constant should match the second derivative of the animator interpolator.
     public static final float INTERP_COEFF = 1.7f;
-    private static final float CONTENT_VISIBLE_MAX_THRESHOLD = 0.5f;
 
     public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
             new FloatProperty<AllAppsTransitionController>("allAppsProgress") {
@@ -168,6 +168,11 @@
         builder.add(anim);
 
         setAlphas(toState, config, builder);
+
+        if (ALL_APPS.equals(toState) && mLauncher.isInState(NORMAL)) {
+            mLauncher.getAppsView().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+        }
     }
 
     public Animator createSpringAnimation(float... progressValues) {
@@ -181,8 +186,7 @@
         int visibleElements = state.getVisibleElements(mLauncher);
         boolean hasAllAppsContent = (visibleElements & ALL_APPS_CONTENT) != 0;
 
-        Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE,
-                Interpolators.clampToProgress(LINEAR, 0, CONTENT_VISIBLE_MAX_THRESHOLD));
+        Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
         setter.setViewAlpha(mAppsView, hasAllAppsContent ? 1 : 0, allAppsFade);
 
         boolean shouldProtectHeader =
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderRow.java b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
index 9bf6043..6ff2132 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderRow.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
@@ -47,6 +47,8 @@
 
     /**
      * Scrolls the content vertically.
+     * @param scroll scrolled distance in pixels for active recyclerview.
+     * @param isScrolledOut bool to determine if row is scrolled out of view
      */
     void setVerticalScroll(int scroll, boolean isScrolledOut);
 
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index debb5b2..3ca0303 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -47,6 +47,7 @@
         ValueAnimator.AnimatorUpdateListener, PluginListener<AllAppsRow>, Insettable,
         OnHeightUpdatedListener {
 
+    private static final long ALL_APPS_CONTENT_ANIM_DURATION = 150;
     private final Rect mRVClip = new Rect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
     private final Rect mHeaderClip = new Rect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
     private final ValueAnimator mAnimator = ValueAnimator.ofInt(0, 0);
@@ -108,6 +109,14 @@
     // enabled or disabled, and represent the current set of all rows.
     private FloatingHeaderRow[] mAllRows = FloatingHeaderRow.NO_ROWS;
 
+
+    // members for handling suggestion state
+    private final ValueAnimator mAllAppsContentAnimator = ValueAnimator.ofFloat(0, 0);
+    private View mAllAppsButton;
+    private int mAllAppsContentFadeInOffset;
+    private boolean mInSuggestionMode = false;
+
+
     public FloatingHeaderView(@NonNull Context context) {
         this(context, null);
     }
@@ -118,12 +127,20 @@
                 .getDimensionPixelSize(R.dimen.all_apps_header_top_padding);
         mHeaderProtectionSupported = context.getResources().getBoolean(
                 R.bool.config_header_protection_supported);
+        mAllAppsContentFadeInOffset = context.getResources()
+                .getDimensionPixelSize(R.dimen.all_apps_content_fade_in_offset);
+        mAllAppsContentAnimator.setDuration(ALL_APPS_CONTENT_ANIM_DURATION);
+        mAllAppsContentAnimator.addUpdateListener(this::onAllAppsContentAnimationUpdate);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         mTabLayout = findViewById(R.id.tabs);
+        mAllAppsButton = findViewById(R.id.all_apps_button);
+        if (mAllAppsButton != null) {
+            mAllAppsButton.setOnClickListener(this::onAllAppsButtonClicked);
+        }
 
         // Find all floating header rows.
         ArrayList<FloatingHeaderRow> rows = new ArrayList<>();
@@ -312,6 +329,7 @@
         }
 
         mTabLayout.setTranslationY(mTranslationY);
+        setSuggestionMode(false);
 
         int clipHeight = mHeaderTopPadding - getPaddingBottom();
         mRVClip.top = mTabsHidden ? clipHeight : 0;
@@ -347,6 +365,7 @@
             mTranslationY = 0;
             applyVerticalMove();
         }
+        setSuggestionMode(false);
         mHeaderCollapsed = false;
         mSnappedScrolledY = -mMaxTranslation;
         mCurrentRV.scrollToTop();
@@ -442,6 +461,38 @@
         }
         return Math.max(getHeight() - getPaddingTop() + mTranslationY, 0);
     }
+
+    /**
+     * When suggestion mode is enabled, hides AllApps content view and shows AllApps button.
+     */
+    public void setSuggestionMode(boolean isSuggestMode) {
+        if (mInSuggestionMode == isSuggestMode || mAllAppsButton == null) return;
+        if (!FeatureFlags.ENABLE_ONE_SEARCH.get()) return;
+        AllAppsContainerView allApps = (AllAppsContainerView) getParent();
+        mInSuggestionMode = isSuggestMode;
+        if (isSuggestMode) {
+            mTabLayout.setVisibility(GONE);
+            mAllAppsButton.setVisibility(VISIBLE);
+            allApps.getContentView().setVisibility(GONE);
+        } else {
+            mTabLayout.setVisibility(mTabsHidden ? GONE : VISIBLE);
+            mAllAppsButton.setVisibility(GONE);
+            allApps.getContentView().setVisibility(VISIBLE);
+        }
+    }
+
+    private void onAllAppsButtonClicked(View view) {
+        setSuggestionMode(false);
+        mAllAppsContentAnimator.start();
+    }
+
+    private void onAllAppsContentAnimationUpdate(ValueAnimator valueAnimator) {
+        float prog = valueAnimator.getAnimatedFraction();
+        View allAppsList = ((AllAppsContainerView) getParent()).getContentView();
+        allAppsList.setAlpha(255 * prog);
+        allAppsList.setTranslationY((1 - prog) * mAllAppsContentFadeInOffset);
+    }
+
 }
 
 
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index 924a392..7478b53 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -49,6 +49,19 @@
     ExtendedEditText getEditText();
 
     /**
+     * Sets whether EditText background should be visible
+     * @param maxAlpha defines the maximum alpha the background should animates to
+     */
+    default void setBackgroundVisibility(boolean visible, float maxAlpha) {}
+
+    /**
+     * Returns whether a visible background is set on EditText
+     */
+    default boolean getBackgroundVisibility() {
+        return false;
+    }
+
+    /**
      * sets highlight result's title
      */
     default void setFocusedResultTitle(@Nullable  CharSequence title) { }
diff --git a/src/com/android/launcher3/allapps/WorkAdapterProvider.java b/src/com/android/launcher3/allapps/WorkAdapterProvider.java
index 13444dd..331320d 100644
--- a/src/com/android/launcher3/allapps/WorkAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/WorkAdapterProvider.java
@@ -15,12 +15,11 @@
  */
 package com.android.launcher3.allapps;
 
+import android.content.SharedPreferences;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
 
-import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 
 import java.util.ArrayList;
 
@@ -33,13 +32,13 @@
 
     private static final int VIEW_TYPE_WORK_EDU_CARD = 1 << 20;
     private static final int VIEW_TYPE_WORK_DISABLED_CARD = 1 << 21;
-    private final Runnable mRefreshCB;
-    private final BaseDraggingActivity mLauncher;
-    private boolean mEnabled;
 
-    WorkAdapterProvider(BaseDraggingActivity launcher, Runnable refreshCallback) {
-        mLauncher = launcher;
-        mRefreshCB = refreshCallback;
+    @WorkProfileManager.WorkProfileState
+    private int mState;
+    private SharedPreferences mPreferences;
+
+    WorkAdapterProvider(SharedPreferences prefs) {
+        mPreferences = prefs;
     }
 
     @Override
@@ -61,19 +60,19 @@
      * returns whether or not work apps should be visible in work tab.
      */
     public boolean shouldShowWorkApps() {
-        return mEnabled;
+        return mState != WorkProfileManager.STATE_DISABLED;
     }
 
     /**
      * Adds work profile specific adapter items to adapterItems and returns number of items added
      */
     public int addWorkItems(ArrayList<AllAppsGridAdapter.AdapterItem> adapterItems) {
-        if (!mEnabled) {
+        if (mState == WorkProfileManager.STATE_DISABLED) {
             //add disabled card here.
             AllAppsGridAdapter.AdapterItem disabledCard = new AllAppsGridAdapter.AdapterItem();
             disabledCard.viewType = VIEW_TYPE_WORK_DISABLED_CARD;
             adapterItems.add(disabledCard);
-        } else if (!isEduSeen()) {
+        } else if (mState == WorkProfileManager.STATE_ENABLED && !isEduSeen()) {
             AllAppsGridAdapter.AdapterItem eduCard = new AllAppsGridAdapter.AdapterItem();
             eduCard.viewType = VIEW_TYPE_WORK_EDU_CARD;
             adapterItems.add(eduCard);
@@ -85,9 +84,8 @@
     /**
      * Sets the current state of work profile
      */
-    public void updateCurrentState(boolean isEnabled) {
-        mEnabled = isEnabled;
-        mRefreshCB.run();
+    public void updateCurrentState(@WorkProfileManager.WorkProfileState int state) {
+        mState = state;
     }
 
     @Override
@@ -101,6 +99,6 @@
     }
 
     private boolean isEduSeen() {
-        return Utilities.getPrefs(mLauncher).getInt(KEY_WORK_EDU_STEP, 0) != 0;
+        return mPreferences.getInt(KEY_WORK_EDU_STEP, 0) != 0;
     }
 }
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index 5d3af08..be01581 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -16,43 +16,41 @@
 package com.android.launcher3.allapps;
 
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TURN_OFF_WORK_APPS_TAP;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
 import android.content.Context;
 import android.graphics.Insets;
 import android.graphics.Rect;
-import android.os.Build;
-import android.os.Process;
-import android.os.UserHandle;
-import android.os.UserManager;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowInsets;
 import android.widget.Button;
 
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
-
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.KeyboardInsetAnimationCallback;
-import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip;
 
 /**
  * Work profile toggle switch shown at the bottom of AllApps work tab
  */
-public class WorkModeSwitch extends Button implements Insettable, View.OnClickListener {
+public class WorkModeSwitch extends Button implements Insettable, View.OnClickListener,
+        KeyboardInsetAnimationCallback.KeyboardInsetListener,
+        PersonalWorkSlidingTabStrip.OnActivePageChangedListener {
 
-    private Rect mInsets = new Rect();
+    private static final int FLAG_FADE_ONGOING = 1 << 1;
+    private static final int FLAG_TRANSLATION_ONGOING = 1 << 2;
+    private static final int FLAG_PROFILE_TOGGLE_ONGOING = 1 << 3;
+
+    private final Rect mInsets = new Rect();
+    private int mFlags;
     private boolean mWorkEnabled;
+    private boolean mOnWorkTab;
 
 
-    @Nullable
-    private KeyboardInsetAnimationCallback mKeyboardInsetAnimationCallback;
-    private boolean mWorkTabVisible;
-
     public WorkModeSwitch(Context context) {
         this(context, null, 0);
     }
@@ -71,9 +69,12 @@
         setSelected(true);
         setOnClickListener(this);
         if (Utilities.ATLEAST_R) {
-            mKeyboardInsetAnimationCallback = new KeyboardInsetAnimationCallback(this);
-            setWindowInsetsAnimationCallback(mKeyboardInsetAnimationCallback);
+            KeyboardInsetAnimationCallback keyboardInsetAnimationCallback =
+                    new KeyboardInsetAnimationCallback(this);
+            setWindowInsetsAnimationCallback(keyboardInsetAnimationCallback);
         }
+        DeviceProfile grid = BaseDraggingActivity.fromContext(getContext()).getDeviceProfile();
+        setInsets(grid.getInsets());
     }
 
     @Override
@@ -87,57 +88,58 @@
         }
     }
 
-    /**
-     * Animates in/out work profile toggle panel based on the tab user is on
-     */
-    public void setWorkTabVisible(boolean workTabVisible) {
-        clearAnimation();
-        mWorkTabVisible = workTabVisible;
-        if (workTabVisible && mWorkEnabled) {
-            setEnabled(true);
-            setVisibility(VISIBLE);
-            setAlpha(0);
-            animate().alpha(1).start();
-        } else {
-            animate().alpha(0).withEndAction(() -> this.setVisibility(GONE)).start();
-        }
+
+    @Override
+    public void onActivePageChanged(int page) {
+        mOnWorkTab = page == AllAppsContainerView.AdapterHolder.WORK;
+        updateVisibility();
     }
 
     @Override
     public void onClick(View view) {
-        if (Utilities.ATLEAST_P && mWorkTabVisible) {
-            setEnabled(false);
-            Launcher.fromContext(getContext()).getStatsLogManager().logger().log(
-                    LAUNCHER_TURN_OFF_WORK_APPS_TAP);
-            UI_HELPER_EXECUTOR.post(() -> setWorkProfileEnabled(getContext(), false));
+        if (Utilities.ATLEAST_P && isEnabled()) {
+            setFlag(FLAG_PROFILE_TOGGLE_ONGOING);
+            Launcher launcher = Launcher.getLauncher(getContext());
+            launcher.getStatsLogManager().logger().log(LAUNCHER_TURN_OFF_WORK_APPS_TAP);
+            launcher.getAppsView().getWorkManager().setWorkProfileEnabled(false);
         }
     }
 
+    @Override
+    public boolean isEnabled() {
+        return super.isEnabled() && getVisibility() == VISIBLE && mFlags == 0;
+    }
+
     /**
      * Sets the enabled or disabled state of the button
      */
-    public void updateCurrentState(boolean active) {
-        mWorkEnabled = active;
-        setEnabled(true);
-        setVisibility(active ? VISIBLE : GONE);
+    public void updateCurrentState(boolean isEnabled) {
+        removeFlag(FLAG_PROFILE_TOGGLE_ONGOING);
+        if (mWorkEnabled != isEnabled) {
+            mWorkEnabled = isEnabled;
+            updateVisibility();
+        }
     }
 
-    @RequiresApi(Build.VERSION_CODES.P)
-    public static Boolean setWorkProfileEnabled(Context context, boolean enabled) {
-        UserManager userManager = context.getSystemService(UserManager.class);
-        boolean showConfirm = false;
-        for (UserHandle userProfile : UserCache.INSTANCE.get(context).getUserProfiles()) {
-            if (Process.myUserHandle().equals(userProfile)) {
-                continue;
-            }
-            showConfirm |= !userManager.requestQuietModeEnabled(!enabled, userProfile);
+
+    private void updateVisibility() {
+        clearAnimation();
+        if (mWorkEnabled && mOnWorkTab) {
+            setFlag(FLAG_FADE_ONGOING);
+            setVisibility(VISIBLE);
+            animate().alpha(1).withEndAction(() -> removeFlag(FLAG_FADE_ONGOING)).start();
+        } else if (getVisibility() != GONE) {
+            setFlag(FLAG_FADE_ONGOING);
+            animate().alpha(0).withEndAction(() -> {
+                removeFlag(FLAG_FADE_ONGOING);
+                this.setVisibility(GONE);
+            }).start();
         }
-        return showConfirm;
     }
 
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        if (Utilities.ATLEAST_R && mWorkTabVisible) {
+        if (Utilities.ATLEAST_R && isEnabled()) {
             setTranslationY(0);
             if (insets.isVisible(WindowInsets.Type.ime())) {
                 Insets keyboardInsets = insets.getInsets(WindowInsets.Type.ime());
@@ -146,4 +148,22 @@
         }
         return insets;
     }
+
+    @Override
+    public void onTranslationStart() {
+        setFlag(FLAG_TRANSLATION_ONGOING);
+    }
+
+    @Override
+    public void onTranslationEnd() {
+        removeFlag(FLAG_TRANSLATION_ONGOING);
+    }
+
+    private void setFlag(int flag) {
+        mFlags |= flag;
+    }
+
+    private void removeFlag(int flag) {
+        mFlags &= ~flag;
+    }
 }
diff --git a/src/com/android/launcher3/allapps/WorkPausedCard.java b/src/com/android/launcher3/allapps/WorkPausedCard.java
index 7908b63..7593ca7 100644
--- a/src/com/android/launcher3/allapps/WorkPausedCard.java
+++ b/src/com/android/launcher3/allapps/WorkPausedCard.java
@@ -16,7 +16,6 @@
 package com.android.launcher3.allapps;
 
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TURN_ON_WORK_APPS_TAP;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -62,8 +61,8 @@
     public void onClick(View view) {
         if (Utilities.ATLEAST_P) {
             setEnabled(false);
+            mLauncher.getAppsView().getWorkManager().setWorkProfileEnabled(true);
             mLauncher.getStatsLogManager().logger().log(LAUNCHER_TURN_ON_WORK_APPS_TAP);
-            UI_HELPER_EXECUTOR.post(() -> WorkModeSwitch.setWorkProfileEnabled(getContext(), true));
         }
     }
 
diff --git a/src/com/android/launcher3/allapps/WorkProfileManager.java b/src/com/android/launcher3/allapps/WorkProfileManager.java
new file mode 100644
index 0000000..e223248
--- /dev/null
+++ b/src/com/android/launcher3/allapps/WorkProfileManager.java
@@ -0,0 +1,182 @@
+/*
+ * 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.allapps;
+
+import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
+import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
+import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import android.content.SharedPreferences;
+import android.os.Build;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Companion class for {@link AllAppsContainerView} to manage work tab and personal tab related
+ * logic based on {@link WorkProfileState}?
+ */
+public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActivePageChangedListener {
+    private static final String TAG = "WorkProfileManager";
+
+
+    public static final int STATE_ENABLED = 1;
+    public static final int STATE_DISABLED = 2;
+    public static final int STATE_TRANSITION = 3;
+
+
+    private final UserManager mUserManager;
+
+    /**
+     * Work profile manager states
+     */
+    @IntDef(value = {
+            STATE_ENABLED,
+            STATE_DISABLED,
+            STATE_TRANSITION
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface WorkProfileState {
+    }
+
+    private final AllAppsContainerView mAllApps;
+    private final WorkAdapterProvider mAdapterProvider;
+    private final ItemInfoMatcher mMatcher;
+
+    private WorkModeSwitch mWorkModeSwitch;
+
+    @WorkProfileState
+    private int mCurrentState;
+
+
+    public WorkProfileManager(UserManager userManager, AllAppsContainerView allApps,
+            SharedPreferences preferences) {
+        mUserManager = userManager;
+        mAllApps = allApps;
+        mAdapterProvider = new WorkAdapterProvider(preferences);
+        mMatcher = mAllApps.mPersonalMatcher.negate();
+    }
+
+    /**
+     * Posts quite mode enable/disable call for work profile user
+     */
+    @RequiresApi(Build.VERSION_CODES.P)
+    public void setWorkProfileEnabled(boolean enabled) {
+        updateCurrentState(STATE_TRANSITION);
+        UI_HELPER_EXECUTOR.post(() -> {
+            for (UserHandle userProfile : mUserManager.getUserProfiles()) {
+                if (Process.myUserHandle().equals(userProfile)) {
+                    continue;
+                }
+                mUserManager.requestQuietModeEnabled(!enabled, userProfile);
+            }
+        });
+    }
+
+    @Override
+    public void onActivePageChanged(int page) {
+        if (mWorkModeSwitch != null) {
+            mWorkModeSwitch.onActivePageChanged(page);
+        }
+    }
+
+    /**
+     * Requests work profile state from {@link AllAppsStore} and updates work profile related views
+     */
+    public void reset() {
+        boolean isEnabled = !mAllApps.getAppsStore().hasModelFlag(FLAG_QUIET_MODE_ENABLED);
+        updateCurrentState(isEnabled ? STATE_ENABLED : STATE_DISABLED);
+    }
+
+    private void updateCurrentState(@WorkProfileState int currentState) {
+        mCurrentState = currentState;
+        mAdapterProvider.updateCurrentState(currentState);
+        if (getAH() != null) {
+            getAH().appsList.updateAdapterItems();
+        }
+        if (mWorkModeSwitch != null) {
+            mWorkModeSwitch.updateCurrentState(currentState == STATE_ENABLED);
+        }
+    }
+
+    /**
+     * Creates and attaches for profile toggle button to {@link AllAppsContainerView}
+     */
+    public boolean attachWorkModeSwitch() {
+        if (!mAllApps.getAppsStore().hasModelFlag(
+                FLAG_HAS_SHORTCUT_PERMISSION | FLAG_QUIET_MODE_CHANGE_PERMISSION)) {
+            Log.e(TAG, "unable to attach work mode switch; Missing required permissions");
+            return false;
+        }
+        if (mWorkModeSwitch == null) {
+            mWorkModeSwitch = (WorkModeSwitch) mAllApps.getLayoutInflater().inflate(
+                    R.layout.work_mode_fab, mAllApps, false);
+        }
+        if (mWorkModeSwitch.getParent() != mAllApps) {
+            mAllApps.addView(mWorkModeSwitch);
+        }
+        if (getAH() != null) {
+            getAH().applyPadding();
+        }
+        mWorkModeSwitch.updateCurrentState(mCurrentState == STATE_ENABLED);
+        return true;
+    }
+
+    /**
+     * Removes work profile toggle button from {@link AllAppsContainerView}
+     */
+    public void detachWorkModeSwitch() {
+        if (mWorkModeSwitch != null && mWorkModeSwitch.getParent() == mAllApps) {
+            mAllApps.removeView(mWorkModeSwitch);
+        }
+        mWorkModeSwitch = null;
+    }
+
+
+    public WorkAdapterProvider getAdapterProvider() {
+        return mAdapterProvider;
+    }
+
+    public ItemInfoMatcher getMatcher() {
+        return mMatcher;
+    }
+
+    @Nullable
+    public WorkModeSwitch getWorkModeSwitch() {
+        return mWorkModeSwitch;
+    }
+
+    private AllAppsContainerView.AdapterHolder getAH() {
+        return mAllApps.mAH[AllAppsContainerView.AdapterHolder.WORK];
+    }
+
+    public int getCurrentState() {
+        return mCurrentState;
+    }
+}
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 2491217..4c5a9e6 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -105,8 +105,8 @@
         int rowWidth = myRequestedWidth - mAppsView.getActiveRecyclerView().getPaddingLeft()
                 - mAppsView.getActiveRecyclerView().getPaddingRight();
 
-        int cellWidth = DeviceProfile.calculateCellWidth(rowWidth, dp.cellLayoutBorderSpacingPx,
-                dp.numShownHotseatIcons);
+        int cellWidth = DeviceProfile.calculateCellWidth(rowWidth,
+                dp.cellLayoutBorderSpacePx.x, dp.numShownHotseatIcons);
         int iconVisibleSize = Math.round(ICON_VISIBLE_AREA_FACTOR * dp.iconSizePx);
         int iconPadding = cellWidth - iconVisibleSize;
 
diff --git a/src/com/android/launcher3/anim/FlingSpringAnim.java b/src/com/android/launcher3/anim/FlingSpringAnim.java
index 6ea38ec..51eab4c 100644
--- a/src/com/android/launcher3/anim/FlingSpringAnim.java
+++ b/src/com/android/launcher3/anim/FlingSpringAnim.java
@@ -40,8 +40,8 @@
     private float mTargetPosition;
 
     public <K> FlingSpringAnim(K object, Context context, FloatPropertyCompat<K> property,
-            float startPosition, float targetPosition, float startVelocity, float minVisChange,
-            float minValue, float maxValue, float springVelocityFactor,
+            float startPosition, float targetPosition, float startVelocityPxPerS,
+            float minVisChange, float minValue, float maxValue,
             OnAnimationEndListener onEndListener) {
         ResourceProvider rp = DynamicResource.provider(context);
         float damping = rp.getFloat(R.dimen.swipe_up_rect_xy_damping_ratio);
@@ -53,19 +53,19 @@
                 // Have the spring pull towards the target if we've slowed down too much before
                 // reaching it.
                 .setMinimumVisibleChange(minVisChange)
-                .setStartVelocity(startVelocity)
+                .setStartVelocity(startVelocityPxPerS)
                 .setMinValue(minValue)
                 .setMaxValue(maxValue);
         mTargetPosition = targetPosition;
 
         // We are already past the fling target, so skip it to avoid losing a frame of the spring.
-        mSkipFlingAnim = startPosition <= minValue && startVelocity < 0
-                || startPosition >= maxValue && startVelocity > 0;
+        mSkipFlingAnim = startPosition <= minValue && startVelocityPxPerS < 0
+                || startPosition >= maxValue && startVelocityPxPerS > 0;
 
         mFlingAnim.addEndListener(((animation, canceled, value, velocity) -> {
             mSpringAnim = new SpringAnimation(object, property)
                     .setStartValue(value)
-                    .setStartVelocity(velocity * springVelocityFactor)
+                    .setStartVelocity(velocity)
                     .setSpring(new SpringForce(mTargetPosition)
                             .setStiffness(stiffness)
                             .setDampingRatio(damping));
diff --git a/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java b/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java
index ef4ada3..9d96365 100644
--- a/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java
+++ b/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java
@@ -65,7 +65,32 @@
     public WindowInsetsAnimation.Bounds onStart(WindowInsetsAnimation animation,
             WindowInsetsAnimation.Bounds bounds) {
         mTerminalTranslation = mView.getTranslationY();
-        mView.setTranslationY(mInitialTranslation);
+        if (mView instanceof KeyboardInsetListener) {
+            ((KeyboardInsetListener) mView).onTranslationStart();
+        }
         return super.onStart(animation, bounds);
     }
+
+    @Override
+    public void onEnd(WindowInsetsAnimation animation) {
+        if (mView instanceof KeyboardInsetListener) {
+            ((KeyboardInsetListener) mView).onTranslationEnd();
+        }
+        super.onEnd(animation);
+    }
+
+    /**
+     * Interface Allowing views to listen for keyboard translation events
+     */
+    public interface KeyboardInsetListener {
+        /**
+         * Called from {@link KeyboardInsetAnimationCallback#onStart}
+         */
+        void onTranslationStart();
+
+        /**
+         * Called from {@link KeyboardInsetAnimationCallback#onEnd}
+         */
+        void onTranslationEnd();
+    }
 }
diff --git a/src/com/android/launcher3/anim/PendingAnimation.java b/src/com/android/launcher3/anim/PendingAnimation.java
index 01f7de6..3ab893b 100644
--- a/src/com/android/launcher3/anim/PendingAnimation.java
+++ b/src/com/android/launcher3/anim/PendingAnimation.java
@@ -56,6 +56,10 @@
         mAnim = new AnimatorSet();
     }
 
+    public long getDuration() {
+        return mDuration;
+    }
+
     /**
      * Utility method to sent an interpolator on an animation and add it to the list
      */
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index 30c3417..97052b2 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -89,6 +89,14 @@
         sendEventToTest(accessibilityManager, context, TestProtocol.PAUSE_DETECTED_MESSAGE, null);
     }
 
+    public static void sendDismissAnimationEndsEventToTest(Context context) {
+        final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
+        if (accessibilityManager == null) return;
+
+        sendEventToTest(accessibilityManager, context, TestProtocol.DISMISS_ANIMATION_ENDS_MESSAGE,
+                null);
+    }
+
     private static void sendEventToTest(
             AccessibilityManager accessibilityManager,
             Context context, String eventTag, Bundle data) {
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index e0769db..796c912 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -72,12 +72,6 @@
             "PROMISE_APPS_NEW_INSTALLS", true,
             "Adds a promise icon to the home screen for new install sessions.");
 
-    public static final BooleanFlag QUICKSTEP_SPRINGS = getDebugFlag(
-            "QUICKSTEP_SPRINGS", true, "Enable springs for quickstep animations");
-
-    public static final BooleanFlag UNSTABLE_SPRINGS = getDebugFlag(
-            "UNSTABLE_SPRINGS", false, "Enable unstable springs for quickstep animations");
-
     public static final BooleanFlag ENABLE_LOCAL_COLOR_POPUPS = getDebugFlag(
             "ENABLE_LOCAL_COLOR_POPUPS", false, "Enable local color extraction for popups.");
 
@@ -94,14 +88,12 @@
             "ENABLE_QUICKSTEP_WIDGET_APP_START", true,
             "Enable Quickstep animation when launching activities from an app widget");
 
-    // Keep as DeviceFlag to allow remote disable in emergency.
-    public static final BooleanFlag ENABLE_SUGGESTED_ACTIONS_OVERVIEW = new DeviceFlag(
-            "ENABLE_SUGGESTED_ACTIONS_OVERVIEW", false, "Show chip hints on the overview screen");
-
-
     public static final BooleanFlag ENABLE_DEVICE_SEARCH = new DeviceFlag(
             "ENABLE_DEVICE_SEARCH", true, "Allows on device search in all apps");
 
+    public static final BooleanFlag ENABLE_ONE_SEARCH = new DeviceFlag("ENABLE_ONE_SEARCH", false,
+            "Use homescreen search box to complete allApps searches");
+
     public static final BooleanFlag ENABLE_DEVICE_SEARCH_PERFORMANCE_LOGGING = new DeviceFlag(
             "ENABLE_DEVICE_SEARCH_PERFORMANCE_LOGGING", true,
             "Allows on device search in all apps logging");
@@ -124,16 +116,6 @@
     public static final BooleanFlag ENABLE_PREDICTION_DISMISS = getDebugFlag(
             "ENABLE_PREDICTION_DISMISS", true, "Allow option to dimiss apps from predicted list");
 
-    public static final BooleanFlag ENABLE_QUICK_CAPTURE_GESTURE = getDebugFlag(
-            "ENABLE_QUICK_CAPTURE_GESTURE", true, "Swipe from right to left to quick capture");
-
-    public static final BooleanFlag ENABLE_QUICK_CAPTURE_WINDOW = getDebugFlag(
-            "ENABLE_QUICK_CAPTURE_WINDOW", false, "Use window to host quick capture");
-
-    public static final BooleanFlag FORCE_LOCAL_OVERSCROLL_PLUGIN = getDebugFlag(
-            "FORCE_LOCAL_OVERSCROLL_PLUGIN", false,
-            "Use a launcher-provided OverscrollPlugin if available");
-
     public static final BooleanFlag ASSISTANT_GIVES_LAUNCHER_FOCUS = getDebugFlag(
             "ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
             "Allow Launcher to handle nav bar gestures while Assistant is running over it");
@@ -144,29 +126,30 @@
     public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag(
             "ENABLE_DEEP_SHORTCUT_ICON_CACHE", true, "R/W deep shortcut in IconCache");
 
-    public static final BooleanFlag MULTI_DB_GRID_MIRATION_ALGO = getDebugFlag(
-            "MULTI_DB_GRID_MIRATION_ALGO", true, "Use the multi-db grid migration algorithm");
-
     public static final BooleanFlag ENABLE_THEMED_ICONS = getDebugFlag(
             "ENABLE_THEMED_ICONS", true, "Enable themed icons on workspace");
 
+    public static final BooleanFlag ENABLE_BULK_WORKSPACE_ICON_LOADING = getDebugFlag(
+            "ENABLE_BULK_WORKSPACE_ICON_LOADING",
+            false,
+            "Enable loading workspace icons in bulk.");
+
+    public static final BooleanFlag ENABLE_BULK_ALL_APPS_ICON_LOADING = getDebugFlag(
+            "ENABLE_BULK_ALL_APPS_ICON_LOADING",
+            false,
+            "Enable loading all apps icons in bulk.");
+
     // Keep as DeviceFlag for remote disable in emergency.
     public static final BooleanFlag ENABLE_OVERVIEW_SELECTIONS = new DeviceFlag(
             "ENABLE_OVERVIEW_SELECTIONS", true, "Show Select Mode button in Overview Actions");
 
     public static final BooleanFlag ENABLE_WIDGETS_PICKER_AIAI_SEARCH = new DeviceFlag(
-            "ENABLE_WIDGETS_PICKER_AIAI_SEARCH", false, "Enable AiAi search in the widgets picker");
-
-    public static final BooleanFlag ENABLE_OVERVIEW_SHARE = getDebugFlag(
-            "ENABLE_OVERVIEW_SHARE", false, "Show Share button in Overview Actions");
+            "ENABLE_WIDGETS_PICKER_AIAI_SEARCH", true, "Enable AiAi search in the widgets picker");
 
     public static final BooleanFlag ENABLE_OVERVIEW_SHARING_TO_PEOPLE = getDebugFlag(
             "ENABLE_OVERVIEW_SHARING_TO_PEOPLE", true,
             "Show indicators for content on Overview to share with top people. ");
 
-    public static final BooleanFlag ENABLE_OVERVIEW_CONTENT_PUSH = getDebugFlag(
-            "ENABLE_OVERVIEW_CONTENT_PUSH", false, "Show Content Push button in Overview Actions");
-
     public static final BooleanFlag ENABLE_DATABASE_RESTORE = getDebugFlag(
             "ENABLE_DATABASE_RESTORE", false,
             "Enable database restore when new restore session is created");
@@ -180,10 +163,6 @@
             "Replace Smartspace with the enhanced version. "
                     + "Ignored if ENABLE_SMARTSPACE_UNIVERSAL is enabled.");
 
-    public static final BooleanFlag ENABLE_SMARTSPACE_FEEDBACK = getDebugFlag(
-            "ENABLE_SMARTSPACE_FEEDBACK", false,
-            "Adds a menu option to send feedback for Enhanced Smartspace.");
-
     public static final BooleanFlag ENABLE_SMARTSPACE_DISMISS = getDebugFlag(
             "ENABLE_SMARTSPACE_DISMISS", true,
             "Adds a menu option to dismiss the current Enhanced Smartspace card.");
@@ -220,22 +199,29 @@
             + "predictions to be updated while they are visible to the user.");
 
     public static final BooleanFlag ENABLE_TASKBAR = getDebugFlag(
-            "ENABLE_TASKBAR", false, "Allows a system Taskbar to be shown on larger devices.");
+            "ENABLE_TASKBAR", true, "Allows a system Taskbar to be shown on larger devices.");
+
+    public static final BooleanFlag ENABLE_TASKBAR_EDU = getDebugFlag("ENABLE_TASKBAR_EDU", true,
+            "Enables showing taskbar education the first time an app is opened.");
 
     public static final BooleanFlag ENABLE_OVERVIEW_GRID = getDebugFlag(
-            "ENABLE_OVERVIEW_GRID", false, "Uses grid overview layout. "
+            "ENABLE_OVERVIEW_GRID", true, "Uses grid overview layout. "
             + "Only applicable on large screen devices.");
 
     public static final BooleanFlag ENABLE_TWO_PANEL_HOME = getDebugFlag(
-            "ENABLE_TWO_PANEL_HOME", false,
+            "ENABLE_TWO_PANEL_HOME", true,
             "Uses two panel on home screen. Only applicable on large screen devices.");
 
+    public static final BooleanFlag ENABLE_TWO_PANEL_HOME_IN_PORTRAIT = getDebugFlag(
+            "ENABLE_TWO_PANEL_HOME_IN_PORTRAIT", true,
+            "Uses two panel on home screen in portrait if ENABLE_TWO_PANEL_HOME is enabled.");
+
     public static final BooleanFlag ENABLE_SCRIM_FOR_APP_LAUNCH = getDebugFlag(
             "ENABLE_SCRIM_FOR_APP_LAUNCH", false,
             "Enables scrim during app launch animation.");
 
     public static final BooleanFlag ENABLE_SPLIT_SELECT = getDebugFlag(
-            "ENABLE_SPLIT_SELECT", false, "Uses new split screen selection overview UI");
+            "ENABLE_SPLIT_SELECT", true, "Uses new split screen selection overview UI");
 
     public static final BooleanFlag ENABLE_ENFORCED_ROUNDED_CORNERS = new DeviceFlag(
             "ENABLE_ENFORCED_ROUNDED_CORNERS", true, "Enforce rounded corners on all App Widgets");
@@ -258,6 +244,18 @@
             "WIDGETS_IN_LAUNCHER_PREVIEW", true,
             "Enables widgets in Launcher preview for the Wallpaper app.");
 
+    public static final BooleanFlag QUICK_WALLPAPER_PICKER = getDebugFlag(
+            "QUICK_WALLPAPER_PICKER", true,
+            "Shows quick wallpaper picker in long-press menu");
+
+    public static final BooleanFlag ENABLE_BACK_SWIPE_HOME_ANIMATION = getDebugFlag(
+            "ENABLE_BACK_SWIPE_HOME_ANIMATION", true,
+            "Enables home animation to icon when user swipes back.");
+
+    public static final BooleanFlag ENABLE_ICON_LABEL_AUTO_SCALING = getDebugFlag(
+            "ENABLE_ICON_LABEL_AUTO_SCALING", true,
+            "Enables scaling/spacing for icon labels to make more characters visible");
+
     public static void initialize(Context context) {
         synchronized (sDebugFlags) {
             for (DebugFlag flag : sDebugFlags) {
@@ -295,7 +293,7 @@
     public static class BooleanFlag {
 
         public final String key;
-        public boolean defaultValue;
+        public final boolean defaultValue;
 
         public BooleanFlag(String key, boolean defaultValue) {
             this.key = key;
@@ -314,16 +312,12 @@
         protected StringBuilder appendProps(StringBuilder src) {
             return src.append(key).append(", defaultValue=").append(defaultValue);
         }
-
-        public void addChangeListener(Context context, Runnable r) { }
-
-        public void removeChangeListener(Runnable r) {}
     }
 
     public static class DebugFlag extends BooleanFlag {
 
         public final String description;
-        private boolean mCurrentValue;
+        protected boolean mCurrentValue;
 
         public DebugFlag(String key, boolean defaultValue, String description) {
             super(key, defaultValue);
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 55be4a4..466b268 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -189,10 +189,18 @@
         if (appWidgetHostView != null) {
             bounds = new Rect();
             appWidgetHostView.getSourceVisualDragBounds(bounds);
-            bounds.offset(appWidgetHostView.getLeft() - (int) mLastTouchPos.x,
-                    appWidgetHostView.getTop() - (int) mLastTouchPos.y);
-            listener = new PinItemDragListener(mRequest, bounds,
-                    appWidgetHostView.getMeasuredWidth(), appWidgetHostView.getMeasuredWidth());
+            float appWidgetHostViewScale = mWidgetCell.getAppWidgetHostViewScale();
+            int xOffset =
+                    appWidgetHostView.getLeft() - (int) (mLastTouchPos.x * appWidgetHostViewScale);
+            int yOffset =
+                    appWidgetHostView.getTop() - (int) (mLastTouchPos.y * appWidgetHostViewScale);
+            bounds.offset(xOffset, yOffset);
+            listener = new PinItemDragListener(
+                    mRequest,
+                    bounds,
+                    appWidgetHostView.getMeasuredWidth(),
+                    appWidgetHostView.getMeasuredWidth(),
+                    appWidgetHostViewScale);
         } else {
             bounds = img.getBitmapBounds();
             bounds.offset(img.getLeft() - (int) mLastTouchPos.x,
@@ -278,9 +286,7 @@
 
             @Override
             protected void onPostExecute(WidgetItem item) {
-                mWidgetCell.setPreviewSize(item);
-                mWidgetCell.applyFromCellItem(item, mApp.getWidgetCache());
-                mWidgetCell.ensurePreview();
+                mWidgetCell.applyFromCellItem(item);
             }
         }.executeOnExecutor(MODEL_EXECUTOR);
         // TODO: Create a worker looper executor and reuse that everywhere.
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 5731db4..fdb2799 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -22,6 +22,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.util.Log;
 import android.view.DragEvent;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -34,6 +35,7 @@
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.ActivityContext;
@@ -74,7 +76,7 @@
     /** Coordinate for last touch event **/
     protected final Point mLastTouch = new Point();
 
-    private final Point mTmpPoint = new Point();
+    protected final Point mTmpPoint = new Point();
 
     protected DropTarget.DragObject mDragObject;
 
@@ -146,6 +148,9 @@
             float initialDragViewScale,
             float dragViewScaleOnDrop,
             DragOptions options) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_DROP_TARGET, "4");
+        }
         return startDrag(drawable, /* view= */ null, originalView, dragLayerX, dragLayerY,
                 source, dragInfo, dragOffset, dragRegion, initialDragViewScale, dragViewScaleOnDrop,
                 options);
@@ -203,6 +208,9 @@
             DragOptions options);
 
     protected void callOnDragStart() {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_DROP_TARGET, "6");
+        }
         if (mOptions.preDragCondition != null) {
             mOptions.preDragCondition.onPreDragEnd(mDragObject, true /* dragStarted*/);
         }
@@ -317,7 +325,7 @@
         mDragObject.dragView.animateTo(mMotionDown.x, mMotionDown.y, onCompleteRunnable, duration);
     }
 
-    private void callOnDragEnd() {
+    protected void callOnDragEnd() {
         if (mIsInPreDrag && mOptions.preDragCondition != null) {
             mOptions.preDragCondition.onPreDragEnd(mDragObject, false /* dragStarted*/);
         }
@@ -343,7 +351,7 @@
     /**
      * Clamps the position to the drag layer bounds.
      */
-    private Point getClampedDragLayerPos(float x, float y) {
+    protected Point getClampedDragLayerPos(float x, float y) {
         mActivity.getDragLayer().getLocalVisibleRect(mRectTemp);
         mTmpPoint.x = (int) Math.max(mRectTemp.left, Math.min(x, mRectTemp.right - 1));
         mTmpPoint.y = (int) Math.max(mRectTemp.top, Math.min(y, mRectTemp.bottom - 1));
@@ -390,7 +398,7 @@
             return false;
         }
 
-        Point dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
+        Point dragLayerPos = getClampedDragLayerPos(getX(ev), getY(ev));
         mLastTouch.set(dragLayerPos.x,  dragLayerPos.y);
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             // Remember location of down touch
@@ -403,6 +411,14 @@
         return mDragDriver != null && mDragDriver.onInterceptTouchEvent(ev);
     }
 
+    protected float getX(MotionEvent ev) {
+        return ev.getX();
+    }
+
+    protected float getY(MotionEvent ev) {
+        return ev.getY();
+    }
+
     /**
      * Call this from a drag source view.
      */
diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java
index d4ce308..72e47e5 100644
--- a/src/com/android/launcher3/dragndrop/DragDriver.java
+++ b/src/com/android/launcher3/dragndrop/DragDriver.java
@@ -165,8 +165,11 @@
      * Class for driving an internal (i.e. not using framework) drag/drop operation.
      */
     static class InternalDragDriver extends DragDriver {
+        private final DragController mDragController;
+
         InternalDragDriver(DragController dragController, Consumer<MotionEvent> sec) {
             super(dragController, sec);
+            mDragController = dragController;
         }
 
         @Override
@@ -176,11 +179,14 @@
 
             switch (action) {
                 case MotionEvent.ACTION_MOVE:
-                    mEventListener.onDriverDragMove(ev.getX(), ev.getY());
+                    mEventListener.onDriverDragMove(mDragController.getX(ev),
+                            mDragController.getY(ev));
                     break;
                 case MotionEvent.ACTION_UP:
-                    mEventListener.onDriverDragMove(ev.getX(), ev.getY());
-                    mEventListener.onDriverDragEnd(ev.getX(), ev.getY());
+                    mEventListener.onDriverDragMove(mDragController.getX(ev),
+                            mDragController.getY(ev));
+                    mEventListener.onDriverDragEnd(mDragController.getX(ev),
+                            mDragController.getY(ev));
                     break;
                 case MotionEvent.ACTION_CANCEL:
                     mEventListener.onDriverDragCancel();
@@ -197,7 +203,8 @@
 
             switch (action) {
                 case MotionEvent.ACTION_UP:
-                    mEventListener.onDriverDragEnd(ev.getX(), ev.getY());
+                    mEventListener.onDriverDragEnd(mDragController.getX(ev),
+                            mDragController.getY(ev));
                     break;
                 case MotionEvent.ACTION_CANCEL:
                     mEventListener.onDriverDragCancel();
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 011325d..5ee4203 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -17,14 +17,19 @@
 
 package com.android.launcher3.dragndrop;
 
+import static android.animation.ObjectAnimator.ofFloat;
+
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
+import static com.android.launcher3.Utilities.mapRange;
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.animation.TypeEvaluator;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
@@ -44,10 +49,11 @@
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutAndWidgetContainer;
 import com.android.launcher3.Workspace;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.anim.SpringProperty;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.graphics.Scrim;
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
-import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.BaseDragLayer;
 
@@ -69,11 +75,9 @@
     private DragController mDragController;
 
     // Variables relating to animation of views after drop
-    private ValueAnimator mDropAnim = null;
+    private Animator mDropAnim = null;
 
-    @Thunk DragView mDropView = null;
-    @Thunk int mAnchorViewInitialScrollX = 0;
-    @Thunk View mAnchorView = null;
+    private DragView mDropView = null;
 
     private boolean mHoverPointClosesFolder = false;
 
@@ -220,12 +224,7 @@
     public void animateViewIntoPosition(DragView dragView, final int[] pos, float alpha,
             float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable,
             int duration) {
-        Rect r = new Rect();
-        getViewRectRelativeToSelf(dragView, r);
-        final int fromX = r.left;
-        final int fromY = r.top;
-
-        animateViewIntoPosition(dragView, fromX, fromY, pos[0], pos[1], alpha, 1, 1, scaleX, scaleY,
+        animateViewIntoPosition(dragView, pos[0], pos[1], alpha, scaleX, scaleY,
                 onFinishRunnable, animationEndStyle, duration, null);
     }
 
@@ -241,11 +240,6 @@
         parentChildren.measureChild(child);
         parentChildren.layoutChild(child);
 
-        Rect dragViewBounds = new Rect();
-        getViewRectRelativeToSelf(dragView, dragViewBounds);
-        final int fromX = dragViewBounds.left;
-        final int fromY = dragViewBounds.top;
-
         float coord[] = new float[2];
         float childScale = child.getScaleX();
 
@@ -288,51 +282,50 @@
 
         child.setVisibility(INVISIBLE);
         Runnable onCompleteRunnable = () -> child.setVisibility(VISIBLE);
-        animateViewIntoPosition(dragView, fromX, fromY, toX, toY, 1, 1, 1, toScale, toScale,
+        animateViewIntoPosition(dragView, toX, toY, 1, toScale, toScale,
                 onCompleteRunnable, ANIMATION_END_DISAPPEAR, duration, anchorView);
     }
 
-    public void animateViewIntoPosition(final DragView view, final int fromX, final int fromY,
-            final int toX, final int toY, float finalAlpha, float initScaleX, float initScaleY,
-            float finalScaleX, float finalScaleY, Runnable onCompleteRunnable,
-            int animationEndStyle, int duration, View anchorView) {
-        Rect from = new Rect(fromX, fromY, fromX +
-                view.getMeasuredWidth(), fromY + view.getMeasuredHeight());
-        Rect to = new Rect(toX, toY, toX + view.getMeasuredWidth(), toY + view.getMeasuredHeight());
-        animateView(view, from, to, finalAlpha, initScaleX, initScaleY, finalScaleX, finalScaleY, duration,
-                null, null, onCompleteRunnable, animationEndStyle, anchorView);
-    }
-
     /**
      * This method animates a view at the end of a drag and drop animation.
-     *
+     */
+    public void animateViewIntoPosition(final DragView view,
+            final int toX, final int toY, float finalAlpha,
+            float finalScaleX, float finalScaleY, Runnable onCompleteRunnable,
+            int animationEndStyle, int duration, View anchorView) {
+        Rect to = new Rect(toX, toY, toX + view.getMeasuredWidth(), toY + view.getMeasuredHeight());
+        animateView(view, to, finalAlpha, finalScaleX, finalScaleY, duration,
+                null, onCompleteRunnable, animationEndStyle, anchorView);
+    }
+
+    /**
+     * This method animates a view at the end of a drag and drop animation.
      * @param view The view to be animated. This view is drawn directly into DragLayer, and so
      *        doesn't need to be a child of DragLayer.
-     * @param from The initial location of the view. Only the left and top parameters are used.
      * @param to The final location of the view. Only the left and top parameters are used. This
-     *        location doesn't account for scaling, and so should be centered about the desired
-     *        final location (including scaling).
+*        location doesn't account for scaling, and so should be centered about the desired
+*        final location (including scaling).
      * @param finalAlpha The final alpha of the view, in case we want it to fade as it animates.
      * @param finalScaleX The final scale of the view. The view is scaled about its center.
      * @param finalScaleY The final scale of the view. The view is scaled about its center.
      * @param duration The duration of the animation.
      * @param motionInterpolator The interpolator to use for the location of the view.
-     * @param alphaInterpolator The interpolator to use for the alpha of the view.
      * @param onCompleteRunnable Optional runnable to run on animation completion.
      * @param animationEndStyle Whether or not to fade out the view once the animation completes.
-     *        {@link #ANIMATION_END_DISAPPEAR} or {@link #ANIMATION_END_REMAIN_VISIBLE}.
+*        {@link #ANIMATION_END_DISAPPEAR} or {@link #ANIMATION_END_REMAIN_VISIBLE}.
      * @param anchorView If not null, this represents the view which the animated view stays
-     *        anchored to in case scrolling is currently taking place. Note: currently this is
-     *        only used for the X dimension for the case of the workspace.
      */
-    public void animateView(final DragView view, final Rect from, final Rect to,
-            final float finalAlpha, final float initScaleX, final float initScaleY,
-            final float finalScaleX, final float finalScaleY, int duration,
-            final Interpolator motionInterpolator, final Interpolator alphaInterpolator,
-            final Runnable onCompleteRunnable, final int animationEndStyle, View anchorView) {
+    public void animateView(final DragView view, final Rect to,
+            final float finalAlpha, final float finalScaleX, final float finalScaleY, int duration,
+            final Interpolator motionInterpolator, final Runnable onCompleteRunnable,
+            final int animationEndStyle, View anchorView) {
+        view.cancelAnimation();
+        view.requestLayout();
+
+        final int[] from = getViewLocationRelativeToSelf(view);
 
         // Calculate the duration of the animation based on the object's distance
-        final float dist = (float) Math.hypot(to.left - from.left, to.top - from.top);
+        final float dist = (float) Math.hypot(to.left - from[0], to.top - from[1]);
         final Resources res = getResources();
         final float maxDist = (float) res.getInteger(R.integer.config_dropAnimMaxDist);
 
@@ -346,93 +339,45 @@
         }
 
         // Fall back to cubic ease out interpolator for the animation if none is specified
-        TimeInterpolator interpolator = null;
-        if (alphaInterpolator == null || motionInterpolator == null) {
-            interpolator = DEACCEL_1_5;
-        }
+        TimeInterpolator interpolator =
+                motionInterpolator == null ? DEACCEL_1_5 : motionInterpolator;
 
         // Animate the view
-        final float initAlpha = view.getAlpha();
-        final float dropViewScale = view.getScaleX();
-        AnimatorUpdateListener updateCb = new AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                final float percent = (Float) animation.getAnimatedValue();
-                final int width = view.getMeasuredWidth();
-                final int height = view.getMeasuredHeight();
+        PendingAnimation anim = new PendingAnimation(duration);
+        anim.add(ofFloat(view, View.SCALE_X, finalScaleX), interpolator, SpringProperty.DEFAULT);
+        anim.add(ofFloat(view, View.SCALE_Y, finalScaleY), interpolator, SpringProperty.DEFAULT);
+        anim.setViewAlpha(view, finalAlpha, interpolator);
+        anim.setFloat(view, VIEW_TRANSLATE_Y, to.top, interpolator);
 
-                float alphaPercent = alphaInterpolator == null ? percent :
-                        alphaInterpolator.getInterpolation(percent);
-                float motionPercent = motionInterpolator == null ? percent :
-                        motionInterpolator.getInterpolation(percent);
-
-                float initialScaleX = initScaleX * dropViewScale;
-                float initialScaleY = initScaleY * dropViewScale;
-                float scaleX = finalScaleX * percent + initialScaleX * (1 - percent);
-                float scaleY = finalScaleY * percent + initialScaleY * (1 - percent);
-                float alpha = finalAlpha * alphaPercent + initAlpha * (1 - alphaPercent);
-
-                float fromLeft = from.left + (initialScaleX - 1f) * width / 2;
-                float fromTop = from.top + (initialScaleY - 1f) * height / 2;
-
-                int x = (int) (fromLeft + Math.round(((to.left - fromLeft) * motionPercent)));
-                int y = (int) (fromTop + Math.round(((to.top - fromTop) * motionPercent)));
-
-                int anchorAdjust = mAnchorView == null ? 0 : (int) (mAnchorView.getScaleX() *
-                    (mAnchorViewInitialScrollX - mAnchorView.getScrollX()));
-
-                int xPos = x - mDropView.getScrollX() + anchorAdjust;
-                int yPos = y - mDropView.getScrollY();
-
-                mDropView.setTranslationX(xPos);
-                mDropView.setTranslationY(yPos);
-                mDropView.setScaleX(scaleX);
-                mDropView.setScaleY(scaleY);
-                mDropView.setAlpha(alpha);
-            }
-        };
-        animateView(view, updateCb, duration, interpolator, onCompleteRunnable, animationEndStyle,
-                anchorView);
+        ObjectAnimator xMotion = ofFloat(view, VIEW_TRANSLATE_X, to.left);
+        if (anchorView != null) {
+            final int startScroll = anchorView.getScrollX();
+            TypeEvaluator<Float> evaluator = (f, s, e) -> mapRange(f, s, e)
+                    + (anchorView.getScaleX() * (startScroll - anchorView.getScrollX()));
+            xMotion.setEvaluator(evaluator);
+        }
+        anim.add(xMotion, interpolator, SpringProperty.DEFAULT);
+        if (onCompleteRunnable != null) {
+            anim.addListener(forEndCallback(onCompleteRunnable));
+        }
+        playDropAnimation(view, anim.buildAnim(), animationEndStyle);
     }
 
-    public void animateView(final DragView view, AnimatorUpdateListener updateCb, int duration,
-            TimeInterpolator interpolator, final Runnable onCompleteRunnable,
-            final int animationEndStyle, View anchorView) {
+    /**
+     * Runs a previously constructed drop animation
+     */
+    public void playDropAnimation(final DragView view, Animator animator, int animationEndStyle) {
         // Clean up the previous animations
         if (mDropAnim != null) mDropAnim.cancel();
 
         // Show the drop view if it was previously hidden
         mDropView = view;
-        mDropView.cancelAnimation();
-        mDropView.requestLayout();
-
-        // Set the anchor view if the page is scrolling
-        if (anchorView != null) {
-            mAnchorViewInitialScrollX = anchorView.getScrollX();
-        }
-        mAnchorView = anchorView;
-
         // Create and start the animation
-        mDropAnim = new ValueAnimator();
-        mDropAnim.setInterpolator(interpolator);
-        mDropAnim.setDuration(duration);
-        mDropAnim.setFloatValues(0f, 1f);
-        mDropAnim.addUpdateListener(updateCb);
-        mDropAnim.addListener(new AnimatorListenerAdapter() {
-            public void onAnimationEnd(Animator animation) {
-                if (onCompleteRunnable != null) {
-                    onCompleteRunnable.run();
-                }
-                switch (animationEndStyle) {
-                case ANIMATION_END_DISAPPEAR:
-                    clearAnimatedView();
-                    break;
-                case ANIMATION_END_REMAIN_VISIBLE:
-                    break;
-                }
-                mDropAnim = null;
-            }
-        });
+        mDropAnim = animator;
+        mDropAnim.addListener(forEndCallback(() -> mDropAnim = null));
+        if (animationEndStyle == ANIMATION_END_DISAPPEAR) {
+            mDropAnim.addListener(forEndCallback(this::clearAnimatedView));
+        }
         mDropAnim.start();
     }
 
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 3fdb256..fa65945 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -23,6 +23,8 @@
 import static com.android.launcher3.Utilities.getBadge;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
@@ -53,23 +55,19 @@
 import androidx.dynamicanimation.animation.SpringAnimation;
 import androidx.dynamicanimation.animation.SpringForce;
 
-import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.util.RunnableList;
-import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.BaseDragLayer;
-import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
 /** A custom view for rendering an icon, folder, shortcut or widget during drag-n-drop. */
-public class DragView extends FrameLayout implements StateListener<LauncherState> {
+public abstract class DragView<T extends Context & ActivityContext> extends FrameLayout {
 
     public static final int VIEW_ZOOM_DURATION = 150;
 
@@ -82,22 +80,23 @@
     private final int mHeight;
 
     private final int mBlurSizeOutline;
-    private final int mRegistrationX;
-    private final int mRegistrationY;
+    protected final int mRegistrationX;
+    protected final int mRegistrationY;
     private final float mInitialScale;
-    private final float mScaleOnDrop;
-    private final int[] mTempLoc = new int[2];
+    protected final float mScaleOnDrop;
+    protected final int[] mTempLoc = new int[2];
 
     private final RunnableList mOnDragStartCallback = new RunnableList();
 
     private Point mDragVisualizeOffset = null;
     private Rect mDragRegion = null;
-    private final Launcher mLauncher;
-    private final DragLayer mDragLayer;
-    @Thunk final DragController mDragController;
+    protected final T mActivity;
+    private final BaseDragLayer<T> mDragLayer;
     private boolean mHasDrawn = false;
 
     final ValueAnimator mAnim;
+    // Whether mAnim has started. Unlike mAnim.isStarted(), this is true even after mAnim ends.
+    private boolean mAnimStarted;
 
     private int mLastTouchX;
     private int mLastTouchY;
@@ -110,7 +109,7 @@
     private Path mScaledMaskPath;
     private Drawable mBadge;
 
-    public DragView(Launcher launcher, Drawable drawable, int registrationX,
+    public DragView(T launcher, Drawable drawable, int registrationX,
             int registrationY, final float initialScale, final float scaleOnDrop,
             final float finalScaleDps) {
         this(launcher, getViewFromDrawable(launcher, drawable),
@@ -123,7 +122,7 @@
      * <p>
      * The registration point is the point inside our view that the touch events should
      * be centered upon.
-     * @param launcher The Launcher instance
+     * @param activity The Launcher instance/ActivityContext this DragView is in.
      * @param content the view content that is attached to the drag view.
      * @param width the width of the dragView
      * @param height the height of the dragView
@@ -133,13 +132,12 @@
      * @param scaleOnDrop the scale used in the drop animation.
      * @param finalScaleDps the scale used in the zoom out animation when the drag view is shown.
      */
-    public DragView(Launcher launcher, View content, int width, int height, int registrationX,
+    public DragView(T activity, View content, int width, int height, int registrationX,
             int registrationY, final float initialScale, final float scaleOnDrop,
             final float finalScaleDps) {
-        super(launcher);
-        mLauncher = launcher;
-        mDragLayer = launcher.getDragLayer();
-        mDragController = launcher.getDragController();
+        super(activity);
+        mActivity = activity;
+        mDragLayer = activity.getDragLayer();
 
         mContent = content;
         mWidth = width;
@@ -153,6 +151,12 @@
 
         addView(content, new LayoutParams(width, height));
 
+        // If there is already a scale set on the content, we don't want to clip the children.
+        if (content.getScaleX() != 1 || content.getScaleY() != 1) {
+            setClipChildren(false);
+            setClipToPadding(false);
+        }
+
         final float scale = (width + finalScaleDps) / width;
 
         // Set the initial scale to avoid any jumps
@@ -170,6 +174,12 @@
                 animation.cancel();
             }
         });
+        mAnim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mAnimStarted = true;
+            }
+        });
 
         setDragRegion(new Rect(0, 0, width, height));
 
@@ -188,24 +198,6 @@
         setWillNotDraw(false);
     }
 
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mLauncher.getStateManager().addStateListener(this);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mLauncher.getStateManager().removeStateListener(this);
-    }
-
-    @Override
-    public void onStateTransitionComplete(LauncherState finalState) {
-        setVisibility((finalState == LauncherState.NORMAL
-                || finalState == LauncherState.SPRING_LOADED) ? VISIBLE : INVISIBLE);
-    }
-
     /**
      * Initialize {@code #mIconDrawable} if the item can be represented using
      * an {@link AdaptiveIconDrawable} or {@link FolderAdaptiveIcon}.
@@ -213,6 +205,7 @@
     @TargetApi(Build.VERSION_CODES.O)
     public void setItemInfo(final ItemInfo info) {
         if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+                && info.itemType != LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION
                 && info.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
                 && info.itemType != LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
             return;
@@ -222,10 +215,10 @@
             Object[] outObj = new Object[1];
             int w = mWidth;
             int h = mHeight;
-            Drawable dr = Utilities.getFullDrawable(mLauncher, info, w, h, outObj);
+            Drawable dr = Utilities.getFullDrawable(mActivity, info, w, h, outObj);
 
             if (dr instanceof AdaptiveIconDrawable) {
-                int blurMargin = (int) mLauncher.getResources()
+                int blurMargin = (int) mActivity.getResources()
                         .getDimension(R.dimen.blur_size_medium_outline) / 2;
 
                 Rect bounds = new Rect(0, 0, w, h);
@@ -233,13 +226,13 @@
                 // Badge is applied after icon normalization so the bounds for badge should not
                 // be scaled down due to icon normalization.
                 Rect badgeBounds = new Rect(bounds);
-                mBadge = getBadge(mLauncher, info, outObj[0]);
+                mBadge = getBadge(mActivity, info, outObj[0]);
                 mBadge.setBounds(badgeBounds);
 
                 // Do not draw the background in case of folder as its translucent
                 final boolean shouldDrawBackground = !(dr instanceof FolderAdaptiveIcon);
 
-                try (LauncherIcons li = LauncherIcons.obtain(mLauncher)) {
+                try (LauncherIcons li = LauncherIcons.obtain(mActivity)) {
                     Drawable nDr; // drawable to be normalized
                     if (shouldDrawBackground) {
                         nDr = dr;
@@ -306,16 +299,6 @@
         mOnDragStartCallback.executeAllAndDestroy();
     }
 
-    // TODO(b/183609936): This is only for LauncherAppWidgetHostView that is rendered in a drawable.
-    // Once LauncherAppWidgetHostView is directly rendered in this view, removes this method.
-    @Override
-    public void invalidate() {
-        super.invalidate();
-        if (mContent instanceof ImageView) {
-            mContent.invalidate();
-        }
-    }
-
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(makeMeasureSpec(mWidth, EXACTLY), makeMeasureSpec(mHeight, EXACTLY));
@@ -413,6 +396,10 @@
         }
     }
 
+    public boolean isAnimationFinished() {
+        return mAnimStarted && !mAnim.isRunning();
+    }
+
     /**
      * Move the window containing this view.
      *
@@ -430,12 +417,11 @@
         applyTranslation();
     }
 
-    public void animateTo(int toTouchX, int toTouchY, Runnable onCompleteRunnable, int duration) {
-        mTempLoc[0] = toTouchX - mRegistrationX;
-        mTempLoc[1] = toTouchY - mRegistrationY;
-        mDragLayer.animateViewIntoPosition(this, mTempLoc, 1f, mScaleOnDrop, mScaleOnDrop,
-                DragLayer.ANIMATION_END_DISAPPEAR, onCompleteRunnable, duration);
-    }
+    /**
+     * Animate this DragView to the given DragLayer coordinates and then remove it.
+     */
+    public abstract void animateTo(int toTouchX, int toTouchY, Runnable onCompleteRunnable,
+            int duration);
 
     public void animateShift(final int shiftX, final int shiftY) {
         if (mAnim.isStarted()) {
@@ -471,7 +457,7 @@
             Picture picture = new Picture();
             mContent.draw(picture.beginRecording(mWidth, mHeight));
             picture.endRecording();
-            View view = new View(mLauncher);
+            View view = new View(mActivity);
             view.setBackground(new PictureDrawable(picture));
             view.measure(makeMeasureSpec(mWidth, EXACTLY), makeMeasureSpec(mHeight, EXACTLY));
             view.layout(mContent.getLeft(), mContent.getTop(),
@@ -492,24 +478,6 @@
     }
 
     /**
-     * If the drag view uses color extraction, block it.
-     */
-    public void disableColorExtraction() {
-        if (mContent instanceof LauncherAppWidgetHostView) {
-            ((LauncherAppWidgetHostView) mContent).disableColorExtraction();
-        }
-    }
-
-    /**
-     * If the drag view uses color extraction, restores it.
-     */
-    public void resumeColorExtraction() {
-        if (mContent instanceof LauncherAppWidgetHostView) {
-            ((LauncherAppWidgetHostView) mContent).enableColorExtraction(/* updateColors= */ false);
-        }
-    }
-
-    /**
      * Removes this view from the {@link DragLayer}.
      *
      * <p>If the drag content is a {@link #mContent}, this call doesn't reattach the
diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
index 98c0cfc..74d9a22 100644
--- a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
+++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
@@ -32,12 +32,12 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.Launcher;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.folder.PreviewBackground;
 import com.android.launcher3.graphics.ShiftedBitmapDrawable;
 import com.android.launcher3.icons.BitmapRenderer;
 import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.views.ActivityContext;
 
 /**
  * {@link AdaptiveIconDrawable} representation of a {@link FolderIcon}
@@ -70,14 +70,14 @@
     }
 
     public static @Nullable FolderAdaptiveIcon createFolderAdaptiveIcon(
-            Launcher launcher, int folderId, Point dragViewSize) {
+            ActivityContext activity, int folderId, Point dragViewSize) {
         Preconditions.assertNonUiThread();
 
         // Create the actual drawable on the UI thread to avoid race conditions with
         // FolderIcon draw pass
         try {
             return MAIN_EXECUTOR.submit(() -> {
-                FolderIcon icon = launcher.findFolderIcon(folderId);
+                FolderIcon icon = activity.findFolderIcon(folderId);
                 return icon == null ? null : createDrawableOnUiThread(icon, dragViewSize);
 
             }).get();
diff --git a/src/com/android/launcher3/dragndrop/LauncherDragController.java b/src/com/android/launcher3/dragndrop/LauncherDragController.java
index a98d70c..dcbfa50 100644
--- a/src/com/android/launcher3/dragndrop/LauncherDragController.java
+++ b/src/com/android/launcher3/dragndrop/LauncherDragController.java
@@ -24,6 +24,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.util.Log;
 import android.view.HapticFeedbackConstants;
 import android.view.View;
 
@@ -36,6 +37,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.testing.TestProtocol;
 
 /**
  * Drag controller for Launcher activity
@@ -65,6 +67,9 @@
             float initialDragViewScale,
             float dragViewScaleOnDrop,
             DragOptions options) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_DROP_TARGET, "5");
+        }
         if (PROFILE_DRAWING_DURING_DRAG) {
             android.os.Debug.startMethodTracing("Launcher");
         }
@@ -96,7 +101,7 @@
         final float scaleDps = mIsInPreDrag
                 ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
         final DragView dragView = mDragObject.dragView = drawable != null
-                ? new DragView(
+                ? new LauncherDragView(
                 mActivity,
                 drawable,
                 registrationX,
@@ -104,7 +109,7 @@
                 initialDragViewScale,
                 dragViewScaleOnDrop,
                 scaleDps)
-                : new DragView(
+                : new LauncherDragView(
                         mActivity,
                         view,
                         view.getMeasuredWidth(),
diff --git a/src/com/android/launcher3/dragndrop/LauncherDragView.java b/src/com/android/launcher3/dragndrop/LauncherDragView.java
new file mode 100644
index 0000000..cc68e2e
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/LauncherDragView.java
@@ -0,0 +1,70 @@
+/*
+ * 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.dragndrop;
+
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.statemanager.StateManager;
+
+/**
+ * A DragView drawn/used by the Launcher activity.
+ */
+public class LauncherDragView extends DragView<Launcher>
+        implements StateManager.StateListener<LauncherState> {
+
+
+    public LauncherDragView(Launcher launcher, Drawable drawable, int registrationX,
+            int registrationY, float initialScale, float scaleOnDrop, float finalScaleDps) {
+        super(launcher, drawable, registrationX, registrationY, initialScale, scaleOnDrop,
+                finalScaleDps);
+    }
+
+    public LauncherDragView(Launcher launcher, View content, int width, int height,
+            int registrationX, int registrationY, float initialScale, float scaleOnDrop,
+            float finalScaleDps) {
+        super(launcher, content, width, height, registrationX, registrationY, initialScale,
+                scaleOnDrop, finalScaleDps);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mActivity.getStateManager().addStateListener(this);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mActivity.getStateManager().removeStateListener(this);
+    }
+
+    @Override
+    public void onStateTransitionComplete(LauncherState finalState) {
+        setVisibility((finalState == LauncherState.NORMAL
+                || finalState == LauncherState.SPRING_LOADED) ? VISIBLE : INVISIBLE);
+    }
+
+    @Override
+    public void animateTo(int toTouchX, int toTouchY, Runnable onCompleteRunnable, int duration) {
+        mTempLoc[0] = toTouchX - mRegistrationX;
+        mTempLoc[1] = toTouchY - mRegistrationY;
+        mActivity.getDragLayer().animateViewIntoPosition(this, mTempLoc, 1f, mScaleOnDrop,
+                mScaleOnDrop, DragLayer.ANIMATION_END_DISAPPEAR, onCompleteRunnable, duration);
+    }
+}
diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
index 2bdf8a0..af43ae8 100644
--- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
@@ -48,12 +48,19 @@
 
     private final PinItemRequest mRequest;
     private final CancellationSignal mCancelSignal;
+    private final float mPreviewScale;
 
     public PinItemDragListener(PinItemRequest request, Rect previewRect,
             int previewBitmapWidth, int previewViewWidth) {
+        this(request, previewRect, previewBitmapWidth, previewViewWidth, /* previewScale= */ 1f);
+    }
+
+    public PinItemDragListener(PinItemRequest request, Rect previewRect,
+            int previewBitmapWidth, int previewViewWidth, float previewScale) {
         super(previewRect, previewBitmapWidth, previewViewWidth);
         mRequest = request;
         mCancelSignal = new CancellationSignal();
+        mPreviewScale = previewScale;
     }
 
     @Override
@@ -98,7 +105,7 @@
 
         PendingItemDragHelper dragHelper = new PendingItemDragHelper(view);
         if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_APPWIDGET) {
-            dragHelper.setRemoteViewsPreview(getPreview(mRequest));
+            dragHelper.setRemoteViewsPreview(getPreview(mRequest), mPreviewScale);
         }
         return dragHelper;
     }
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 22bb56c..879739f 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -75,7 +75,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutAndWidgetContainer;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace.ItemOperator;
 import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
 import com.android.launcher3.accessibility.FolderAccessibilityHelper;
 import com.android.launcher3.anim.KeyboardInsetAnimationCallback;
@@ -94,6 +93,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.ItemOperator;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.BaseDragLayer;
@@ -276,15 +276,19 @@
         mPageIndicator = findViewById(R.id.folder_page_indicator);
         mFolderName = findViewById(R.id.folder_name);
         mFolderName.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp.folderLabelTextSizePx);
-        mFolderName.setOnBackKeyListener(this);
-        mFolderName.setOnFocusChangeListener(this);
-        mFolderName.setOnEditorActionListener(this);
-        mFolderName.setSelectAllOnFocus(true);
-        mFolderName.setInputType(mFolderName.getInputType()
-                & ~InputType.TYPE_TEXT_FLAG_AUTO_CORRECT
-                | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
-                | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
-        mFolderName.forceDisableSuggestions(true);
+        if (mActivityContext.supportsIme()) {
+            mFolderName.setOnBackKeyListener(this);
+            mFolderName.setOnFocusChangeListener(this);
+            mFolderName.setOnEditorActionListener(this);
+            mFolderName.setSelectAllOnFocus(true);
+            mFolderName.setInputType(mFolderName.getInputType()
+                    & ~InputType.TYPE_TEXT_FLAG_AUTO_CORRECT
+                    | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
+                    | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
+            mFolderName.forceDisableSuggestions(true);
+        } else {
+            mFolderName.setEnabled(false);
+        }
 
         mFooter = findViewById(R.id.folder_footer);
         mFooterHeight = getResources().getDimensionPixelSize(R.dimen.folder_label_height);
@@ -1196,8 +1200,7 @@
     }
 
     void replaceFolderWithFinalItem() {
-        mLauncherDelegate.replaceFolderWithFinalItem(this);
-        mDestroyed = true;
+        mDestroyed = mLauncherDelegate.replaceFolderWithFinalItem(this);
     }
 
     public boolean isDestroyed() {
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index cb3884d..61ffd9d 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -22,7 +22,6 @@
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
 import static com.android.launcher3.graphics.IconShape.getShape;
-import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -175,15 +174,9 @@
         final float yDistance = initialY - lp.y;
 
         // Set up the Folder background.
-        final int finalColor;
-        int folderFillColor = Themes.getAttrColor(mContext, R.attr.folderFillColor);
-        if (mIsOpening) {
-            finalColor = folderFillColor;
-        } else {
-            finalColor = mFolderBackground.getColor().getDefaultColor();
-        }
-        final int initialColor = setColorAlphaBound(
-                folderFillColor, mPreviewBackground.getBackgroundAlpha());
+        final int initialColor = Themes.getAttrColor(mContext, R.attr.folderPreviewColor);
+        final int finalColor = Themes.getAttrColor(mContext, R.attr.folderBackgroundColor);
+
         mFolderBackground.mutate();
         mFolderBackground.setColor(mIsOpening ? initialColor : finalColor);
 
@@ -239,9 +232,9 @@
                 mFolder, startRect, endRect, finalRadius, !mIsOpening));
 
         // Create reveal animator for the folder content (capture the top 4 icons 2x2)
-        int width = mDeviceProfile.folderCellLayoutBorderSpacingPx
+        int width = mDeviceProfile.folderCellLayoutBorderSpacePx.x
                 + mDeviceProfile.folderCellWidthPx * 2;
-        int height = mDeviceProfile.folderCellLayoutBorderSpacingPx
+        int height = mDeviceProfile.folderCellLayoutBorderSpacePx.y
                 + mDeviceProfile.folderCellHeightPx * 2;
         int page = mIsOpening ? mContent.getCurrentPage() : mContent.getDestinationPage();
         int left = mContent.getPaddingLeft() + page * lp.width;
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 96030f9..439df80 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -132,6 +132,9 @@
 
     private Rect mTouchArea = new Rect();
 
+    private final PointF mTranslationForMoveFromCenterAnimation = new PointF(0, 0);
+    private float mTranslationXForTaskbarAlignmentAnimation = 0f;
+
     private final PointF mTranslationForReorderBounce = new PointF(0, 0);
     private final PointF mTranslationForReorderPreview = new PointF(0, 0);
     private float mScaleForReorderBounce = 1f;
@@ -336,8 +339,6 @@
         if (animateView != null && mActivity instanceof Launcher) {
             final Launcher launcher = (Launcher) mActivity;
             DragLayer dragLayer = launcher.getDragLayer();
-            Rect from = new Rect();
-            dragLayer.getViewRectRelativeToSelf(animateView, from);
             Rect to = finalRect;
             if (to == null) {
                 to = new Rect();
@@ -403,13 +404,14 @@
             }
 
             final int finalIndex = index;
-            dragLayer.animateView(animateView, from, to, finalAlpha,
-                    1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION,
-                    Interpolators.DEACCEL_2, Interpolators.ACCEL_2,
+            dragLayer.animateView(animateView, to, finalAlpha,
+                    finalScale, finalScale, DROP_IN_ANIMATION_DURATION,
+                    Interpolators.DEACCEL_2,
                     () -> {
                         mPreviewItemManager.hidePreviewItem(finalIndex, false);
                         mFolder.showItem(item);
-                    }, DragLayer.ANIMATION_END_DISAPPEAR, null);
+                    }, 
+                    DragLayer.ANIMATION_END_DISAPPEAR, null);
 
             mFolder.hideItem(item);
 
@@ -683,6 +685,7 @@
 
     @Override
     public void onAdd(WorkspaceItemInfo item, int rank) {
+        updatePreviewItems(false);
         boolean wasDotted = mDotInfo.hasDot();
         mDotInfo.addDotInfo(mActivity.getDotInfoForItem(item));
         boolean isDotted = mDotInfo.hasDot();
@@ -694,6 +697,7 @@
 
     @Override
     public void onRemove(List<WorkspaceItemInfo> items) {
+        updatePreviewItems(false);
         boolean wasDotted = mDotInfo.hasDot();
         items.stream().map(mActivity::getDotInfoForItem).forEach(mDotInfo::subtractDotInfo);
         boolean isDotted = mDotInfo.hasDot();
@@ -764,8 +768,11 @@
     }
 
     private void updateTranslation() {
-        super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x);
-        super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y);
+        super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x
+                + mTranslationForMoveFromCenterAnimation.x
+                + mTranslationXForTaskbarAlignmentAnimation);
+        super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y
+                + mTranslationForMoveFromCenterAnimation.y);
     }
 
     public void setReorderBounceOffset(float x, float y) {
@@ -777,6 +784,29 @@
         offset.set(mTranslationForReorderBounce);
     }
 
+    /**
+     * Sets translationX value for taskbar to launcher alignment animation
+     */
+    public void setTranslationForTaskbarAlignmentAnimation(float translationX) {
+        mTranslationXForTaskbarAlignmentAnimation = translationX;
+        updateTranslation();
+    }
+
+    /**
+     * Returns translation values for taskbar to launcher alignment animation
+     */
+    public float getTranslationXForTaskbarAlignmentAnimation() {
+        return mTranslationXForTaskbarAlignmentAnimation;
+    }
+
+    /**
+     * Sets translation values for move from center animation
+     */
+    public void setTranslationForMoveFromCenterAnimation(float x, float y) {
+        mTranslationForMoveFromCenterAnimation.set(x, y);
+        updateTranslation();
+    }
+
     @Override
     public void setReorderPreviewOffset(float x, float y) {
         mTranslationForReorderPreview.set(x, y);
diff --git a/src/com/android/launcher3/folder/FolderNameEditText.java b/src/com/android/launcher3/folder/FolderNameEditText.java
index 6038a05..7c657f0 100644
--- a/src/com/android/launcher3/folder/FolderNameEditText.java
+++ b/src/com/android/launcher3/folder/FolderNameEditText.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.View;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
@@ -117,4 +118,16 @@
             return super.setComposingText(cs, newCursorPos);
         }
     }
+
+    @Override
+    public void reset() {
+        super.reset();
+        if (isFocused()) {
+            View nextFocus = focusSearch(View.FOCUS_DOWN);
+            if (nextFocus != null) {
+                nextFocus.requestFocus();
+            }
+        }
+        hideKeyboard();
+    }
 }
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 3d2884a..65991e4 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -41,12 +41,12 @@
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutAndWidgetContainer;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace.ItemOperator;
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pageindicators.PageIndicatorDots;
 import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.ViewCache;
 import com.android.launcher3.views.ActivityContext;
diff --git a/src/com/android/launcher3/folder/LauncherDelegate.java b/src/com/android/launcher3/folder/LauncherDelegate.java
index f7d8e8c..c5b3913 100644
--- a/src/com/android/launcher3/folder/LauncherDelegate.java
+++ b/src/com/android/launcher3/folder/LauncherDelegate.java
@@ -18,8 +18,6 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_CONVERTED_TO_ICON;
 
 import android.content.Context;
-import android.graphics.Rect;
-import android.graphics.RectF;
 import android.view.MotionEvent;
 import android.view.View;
 
@@ -38,10 +36,7 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.BaseDragLayer;
-import com.android.launcher3.views.BaseDragLayer.LayoutParams;
-import com.android.launcher3.widget.LocalColorExtractor;
 
-import java.util.Arrays;
 import java.util.Optional;
 import java.util.function.Consumer;
 
@@ -51,8 +46,6 @@
 public class LauncherDelegate {
 
     private final Launcher mLauncher;
-    private final Rect mTempRect = new Rect();
-    private final RectF mTempRectF = new RectF();
 
     private LauncherDelegate(Launcher launcher) {
         mLauncher = launcher;
@@ -84,16 +77,7 @@
         return mLauncher;
     }
 
-    void addRectForColorExtraction(BaseDragLayer.LayoutParams lp, LocalColorExtractor target) {
-        mTempRect.set(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height);
-        target.getExtractedRectForViewRect(mLauncher,
-                mLauncher.getWorkspace().getCurrentPage(), mTempRect, mTempRectF);
-        if (!mTempRectF.isEmpty()) {
-            target.addLocation(Arrays.asList(mTempRectF));
-        }
-    }
-
-    void replaceFolderWithFinalItem(Folder folder) {
+    boolean replaceFolderWithFinalItem(Folder folder) {
         // Add the last remaining child to the workspace in place of the folder
         Runnable onCompleteRunnable = new Runnable() {
             @Override
@@ -147,6 +131,7 @@
         } else {
             onCompleteRunnable.run();
         }
+        return true;
     }
 
 
@@ -191,7 +176,7 @@
         ModelWriter getModelWriter() {
             if (mWriter == null) {
                 mWriter = LauncherAppState.getInstance((Context) mContext).getModel()
-                        .getWriter(false, false);
+                        .getWriter(false, false, null);
             }
             return mWriter;
         }
@@ -205,16 +190,15 @@
         }
 
         @Override
-        void replaceFolderWithFinalItem(Folder folder) { }
+        boolean replaceFolderWithFinalItem(Folder folder) {
+            return false;
+        }
 
         @Override
         boolean interceptOutsideTouch(MotionEvent ev, BaseDragLayer dl, Folder folder) {
             folder.close(true);
             return true;
         }
-
-        @Override
-        void addRectForColorExtraction(LayoutParams lp, LocalColorExtractor target) { }
     }
 
     static LauncherDelegate from(ActivityContext context) {
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index 18d0b10..8f9fa8a 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -66,7 +66,6 @@
     private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 
     float mScale = 1f;
-    private float mColorMultiplier = 1f;
     private int mBgColor;
     private int mStrokeColor;
     private int mDotColor;
@@ -87,7 +86,6 @@
 
     // Drawing / animation configurations
     private static final float ACCEPT_SCALE_FACTOR = 1.20f;
-    private static final float ACCEPT_COLOR_MULTIPLIER = 1.5f;
 
     // Expressed on a scale from 0 to 255.
     private static final int BG_OPACITY = 255;
@@ -154,7 +152,7 @@
         TypedArray ta = context.getTheme().obtainStyledAttributes(R.styleable.FolderIconPreview);
         mDotColor = ta.getColor(R.styleable.FolderIconPreview_folderDotColor, 0);
         mStrokeColor = ta.getColor(R.styleable.FolderIconPreview_folderIconBorderColor, 0);
-        mBgColor = ta.getColor(R.styleable.FolderIconPreview_folderFillColor, 0);
+        mBgColor = ta.getColor(R.styleable.FolderIconPreview_folderPreviewColor, 0);
         ta.recycle();
 
         DeviceProfile grid = activity.getDeviceProfile();
@@ -227,8 +225,7 @@
     }
 
     public int getBgColor() {
-        int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
-        return setColorAlphaBound(mBgColor, alpha);
+        return mBgColor;
     }
 
     public int getDotColor() {
@@ -384,14 +381,10 @@
         return mDrawingDelegate != null;
     }
 
-    private void animateScale(float finalScale, float finalMultiplier,
-                              final Runnable onStart, final Runnable onEnd) {
+    private void animateScale(float finalScale, final Runnable onStart, final Runnable onEnd) {
         final float scale0 = mScale;
         final float scale1 = finalScale;
 
-        final float bgMultiplier0 = mColorMultiplier;
-        final float bgMultiplier1 = finalMultiplier;
-
         if (mScaleAnimator != null) {
             mScaleAnimator.cancel();
         }
@@ -403,7 +396,6 @@
             public void onAnimationUpdate(ValueAnimator animation) {
                 float prog = animation.getAnimatedFraction();
                 mScale = prog * scale1 + (1 - prog) * scale0;
-                mColorMultiplier = prog * bgMultiplier1 + (1 - prog) * bgMultiplier0;
                 invalidate();
             }
         });
@@ -429,8 +421,7 @@
     }
 
     public void animateToAccept(CellLayout cl, int cellX, int cellY) {
-        animateScale(ACCEPT_SCALE_FACTOR, ACCEPT_COLOR_MULTIPLIER,
-                () -> delegateDrawing(cl, cellX, cellY), null);
+        animateScale(ACCEPT_SCALE_FACTOR, () -> delegateDrawing(cl, cellX, cellY), null);
     }
 
     public void animateToRest() {
@@ -440,11 +431,7 @@
         CellLayout cl = mDrawingDelegate;
         int cellX = mDelegateCellX;
         int cellY = mDelegateCellY;
-        animateScale(1f, 1f, () -> delegateDrawing(cl, cellX, cellY), this::clearDrawingDelegate);
-    }
-
-    public int getBackgroundAlpha() {
-        return (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
+        animateScale(1f, () -> delegateDrawing(cl, cellX, cellY), this::clearDrawingDelegate);
     }
 
     public float getStrokeWidth() {
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index a549750..f027b33 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -32,13 +32,13 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DraggableView;
 import com.android.launcher3.icons.BitmapRenderer;
 import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.util.SafeCloseable;
+import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
 import java.nio.ByteBuffer;
@@ -150,7 +150,7 @@
     }
 
     public float getScaleAndPosition(Drawable preview, int[] outPos) {
-        float scale = Launcher.getLauncher(mView.getContext())
+        float scale = ActivityContext.lookupContext(mView.getContext())
                 .getDragLayer().getLocationInDragLayer(mView, outPos);
         if (mView instanceof LauncherAppWidgetHostView) {
             // App widgets are technically scaled, but are drawn at their expected size -- so the
@@ -167,7 +167,7 @@
 
     /** Returns the scale and position of a given view for drag-n-drop. */
     public float getScaleAndPosition(View view, int[] outPos) {
-        float scale = Launcher.getLauncher(mView.getContext())
+        float scale = ActivityContext.lookupContext(mView.getContext())
                 .getDragLayer().getLocationInDragLayer(mView, outPos);
         if (mView instanceof LauncherAppWidgetHostView) {
             // App widgets are technically scaled, but are drawn at their expected size -- so the
@@ -201,7 +201,7 @@
         public void run() {
             Bitmap preview = convertPreviewToAlphaBitmap(mPreviewSnapshot);
             if (mIsIcon) {
-                int size = Launcher.getLauncher(mContext).getDeviceProfile().iconSizePx;
+                int size = ActivityContext.lookupContext(mContext).getDeviceProfile().iconSizePx;
                 preview = Bitmap.createScaledBitmap(preview, size, size, false);
             }
             //else case covers AppWidgetHost (doesn't drag/drop across different device profiles)
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index e4f5539..fc8d855 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -9,7 +9,6 @@
 import android.content.ContentProvider;
 import android.content.ContentValues;
 import android.content.pm.PackageManager;
-import android.content.res.XmlResourceParser;
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.net.Uri;
@@ -23,23 +22,13 @@
 import android.os.Messenger;
 import android.util.ArrayMap;
 import android.util.Log;
-import android.util.Xml;
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile.GridOption;
-import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.Executors;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
 /**
  * Exposes various launcher grid options and allows the caller to change them.
  * APIs:
@@ -94,7 +83,7 @@
                 MatrixCursor cursor = new MatrixCursor(new String[] {
                         KEY_NAME, KEY_ROWS, KEY_COLS, KEY_PREVIEW_COUNT, KEY_IS_DEFAULT});
                 InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(getContext());
-                for (GridOption gridOption : parseAllGridOptions()) {
+                for (GridOption gridOption : idp.parseAllGridOptions(getContext())) {
                     cursor.newRow()
                             .add(KEY_NAME, gridOption.name)
                             .add(KEY_ROWS, gridOption.numRows)
@@ -116,25 +105,6 @@
         }
     }
 
-    private List<GridOption> parseAllGridOptions() {
-        List<GridOption> result = new ArrayList<>();
-        try (XmlResourceParser parser = getContext().getResources().getXml(R.xml.device_profiles)) {
-            final int depth = parser.getDepth();
-            int type;
-            while (((type = parser.next()) != XmlPullParser.END_TAG ||
-                    parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
-                if ((type == XmlPullParser.START_TAG)
-                        && GridOption.TAG_NAME.equals(parser.getName())) {
-                    result.add(new GridOption(getContext(), Xml.asAttributeSet(parser)));
-                }
-            }
-        } catch (IOException | XmlPullParserException e) {
-            Log.e(TAG, "Error parsing device profile", e);
-            return Collections.emptyList();
-        }
-        return result;
-    }
-
     @Override
     public String getType(Uri uri) {
         return "vnd.android.cursor.dir/launcher_grid";
@@ -155,9 +125,10 @@
         switch (uri.getPath()) {
             case KEY_DEFAULT_GRID: {
                 String gridName = values.getAsString(KEY_NAME);
+                InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(getContext());
                 // Verify that this is a valid grid option
                 GridOption match = null;
-                for (GridOption option : parseAllGridOptions()) {
+                for (GridOption option : idp.parseAllGridOptions(getContext())) {
                     if (option.name.equals(gridName)) {
                         match = option;
                         break;
@@ -167,8 +138,7 @@
                     return 0;
                 }
 
-                InvariantDeviceProfile.INSTANCE.get(getContext())
-                        .setCurrentGrid(getContext(), gridName);
+                idp.setCurrentGrid(getContext(), gridName);
                 return 1;
             }
             case ICON_THEMED:
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index fb25954..73e18f4 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -65,6 +65,7 @@
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
 import com.android.launcher3.WorkspaceLayoutManager;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.FolderIcon;
@@ -85,7 +86,8 @@
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.launcher3.widget.BaseLauncherAppWidgetHostView;
@@ -96,13 +98,10 @@
 import com.android.launcher3.widget.custom.CustomWidgetManager;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.concurrent.ConcurrentLinkedQueue;
 
 /**
@@ -121,22 +120,16 @@
      * Context used just for preview. It also provides a few objects (e.g. UserCache) just for
      * preview purposes.
      */
-    public static class PreviewContext extends ContextWrapper {
-
-        private final Set<MainThreadInitializedObject> mAllowedObjects = new HashSet<>(
-                Arrays.asList(UserCache.INSTANCE, InstallSessionHelper.INSTANCE,
-                        LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
-                        CustomWidgetManager.INSTANCE, PluginManagerWrapper.INSTANCE));
+    public static class PreviewContext extends SandboxContext {
 
         private final InvariantDeviceProfile mIdp;
-        private final Map<MainThreadInitializedObject, Object> mObjectMap = new HashMap<>();
         private final ConcurrentLinkedQueue<LauncherIconsForPreview> mIconPool =
                 new ConcurrentLinkedQueue<>();
 
-        private boolean mDestroyed = false;
-
         public PreviewContext(Context base, InvariantDeviceProfile idp) {
-            super(base);
+            super(base, UserCache.INSTANCE, InstallSessionHelper.INSTANCE,
+                    LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
+                    CustomWidgetManager.INSTANCE, PluginManagerWrapper.INSTANCE);
             mIdp = idp;
             mObjectMap.put(InvariantDeviceProfile.INSTANCE, idp);
             mObjectMap.put(LauncherAppState.INSTANCE,
@@ -144,37 +137,6 @@
 
         }
 
-        @Override
-        public Context getApplicationContext() {
-            return this;
-        }
-
-        public void onDestroy() {
-            CustomWidgetManager.INSTANCE.get(this).onDestroy();
-            LauncherAppState.INSTANCE.get(this).onTerminate();
-            mDestroyed = true;
-        }
-
-        /**
-         * Find a cached object from mObjectMap if we have already created one. If not, generate
-         * an object using the provider.
-         */
-        public <T> T getObject(MainThreadInitializedObject<T> mainThreadInitializedObject,
-                MainThreadInitializedObject.ObjectProvider<T> provider) {
-            if (FeatureFlags.IS_STUDIO_BUILD && mDestroyed) {
-                throw new RuntimeException("Context already destroyed");
-            }
-            if (!mAllowedObjects.contains(mainThreadInitializedObject)) {
-                throw new IllegalStateException("Leaking unknown objects");
-            }
-            if (mObjectMap.containsKey(mainThreadInitializedObject)) {
-                return (T) mObjectMap.get(mainThreadInitializedObject);
-            }
-            T t = provider.get(this);
-            mObjectMap.put(mainThreadInitializedObject, t);
-            return t;
-        }
-
         public LauncherIcons newLauncherIcons(Context context, boolean shapeDetection) {
             LauncherIconsForPreview launcherIconsForPreview = mIconPool.poll();
             if (launcherIconsForPreview != null) {
@@ -209,9 +171,9 @@
     private final LayoutInflater mHomeElementInflater;
     private final InsettableFrameLayout mRootView;
     private final Hotseat mHotseat;
-    private final CellLayout mWorkspace;
-    private final SparseIntArray mWallpaperColorResources;
+    private final Map<Integer, CellLayout> mWorkspaceScreens = new HashMap<>();
     private final AppWidgetHost mAppWidgetHost;
+    private final SparseIntArray mWallpaperColorResources;
 
     public LauncherPreviewRenderer(Context context,
             InvariantDeviceProfile idp,
@@ -255,19 +217,35 @@
                 new ContextThemeWrapper(this, R.style.HomeScreenElementTheme));
         mHomeElementInflater.setFactory2(this);
 
+        int layoutRes = mDp.isTwoPanels ? R.layout.launcher_preview_two_panel_layout
+                : R.layout.launcher_preview_layout;
         mRootView = (InsettableFrameLayout) mHomeElementInflater.inflate(
-                R.layout.launcher_preview_layout, null, false);
+                layoutRes, null, false);
         mRootView.setInsets(mInsets);
         measureView(mRootView, mDp.widthPx, mDp.heightPx);
 
         mHotseat = mRootView.findViewById(R.id.hotseat);
         mHotseat.resetLayout(false);
 
-        mWorkspace = mRootView.findViewById(R.id.workspace);
-        mWorkspace.setPadding(mDp.workspacePadding.left + mDp.cellLayoutPaddingLeftRightPx,
+        CellLayout firstScreen = mRootView.findViewById(R.id.workspace);
+        firstScreen.setPadding(mDp.workspacePadding.left + mDp.cellLayoutPaddingLeftRightPx,
                 mDp.workspacePadding.top,
-                mDp.workspacePadding.right + mDp.cellLayoutPaddingLeftRightPx,
-                mDp.workspacePadding.bottom);
+                (mDp.isTwoPanels ? mDp.cellLayoutBorderSpacePx.x / 2
+                        : mDp.workspacePadding.right) + mDp.cellLayoutPaddingLeftRightPx,
+                mDp.workspacePadding.bottom
+        );
+        mWorkspaceScreens.put(FIRST_SCREEN_ID, firstScreen);
+
+        if (mDp.isTwoPanels) {
+            CellLayout rightPanel = mRootView.findViewById(R.id.workspace_right);
+            rightPanel.setPadding(
+                    mDp.cellLayoutBorderSpacePx.x / 2 + mDp.cellLayoutPaddingLeftRightPx,
+                    mDp.workspacePadding.top,
+                    mDp.workspacePadding.right + mDp.cellLayoutPaddingLeftRightPx,
+                    mDp.workspacePadding.bottom
+            );
+            mWorkspaceScreens.put(Workspace.SECOND_SCREEN_ID, rightPanel);
+        }
 
         if (Utilities.ATLEAST_S) {
             WallpaperColors wallpaperColors = wallpaperColorsOverride != null
@@ -338,18 +316,22 @@
 
     @Override
     public CellLayout getScreenWithId(int screenId) {
-        return mWorkspace;
+        return mWorkspaceScreens.get(screenId);
     }
 
     private void inflateAndAddIcon(WorkspaceItemInfo info) {
+        CellLayout screen = mWorkspaceScreens.get(info.screenId);
         BubbleTextView icon = (BubbleTextView) mHomeElementInflater.inflate(
-                R.layout.app_icon, mWorkspace, false);
+                R.layout.app_icon, screen, false);
         icon.applyFromWorkspaceItem(info);
         addInScreenFromBind(icon, info);
     }
 
     private void inflateAndAddFolder(FolderInfo info) {
-        FolderIcon folderIcon = FolderIcon.inflateIcon(R.layout.folder_icon, this, mWorkspace,
+        CellLayout screen = info.container == Favorites.CONTAINER_DESKTOP
+                ? mWorkspaceScreens.get(info.screenId)
+                : mHotseat;
+        FolderIcon folderIcon = FolderIcon.inflateIcon(R.layout.folder_icon, this, screen,
                 info);
         addInScreenFromBind(folderIcon, info);
     }
@@ -371,7 +353,7 @@
 
     private void inflateAndAddWidgets(LauncherAppWidgetInfo info, WidgetsModel widgetsModel) {
         WidgetItem widgetItem = widgetsModel.getWidgetProviderInfoByProviderName(
-                info.providerName);
+                info.providerName, info.user);
         if (widgetItem == null) {
             return;
         }
@@ -394,17 +376,17 @@
             view.updateAppWidget(null);
         }
 
-        view.setTag(info);
-
         if (mWallpaperColorResources != null) {
             view.setColorResources(mWallpaperColorResources);
         }
 
+        view.setTag(info);
         addInScreenFromBind(view, info);
     }
 
     private void inflateAndAddPredictedIcon(WorkspaceItemInfo info) {
-        View view = PredictedAppIconInflater.inflate(mHomeElementInflater, mWorkspace, info);
+        CellLayout screen = mWorkspaceScreens.get(info.screenId);
+        View view = PredictedAppIconInflater.inflate(mHomeElementInflater, screen, info);
         if (view != null) {
             addInScreenFromBind(view, info);
         }
@@ -435,11 +417,13 @@
         ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
         ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
         ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
-        filterCurrentWorkspaceItems(0 /* currentScreenId */,
-                dataModel.workspaceItems, currentWorkspaceItems,
-                otherWorkspaceItems);
-        filterCurrentWorkspaceItems(0 /* currentScreenId */, dataModel.appWidgets,
-                currentAppWidgets, otherAppWidgets);
+
+        IntSet currentScreenIds = IntSet.wrap(mWorkspaceScreens.keySet());
+        filterCurrentWorkspaceItems(currentScreenIds, dataModel.workspaceItems,
+                currentWorkspaceItems, otherWorkspaceItems);
+        filterCurrentWorkspaceItems(currentScreenIds, dataModel.appWidgets, currentAppWidgets,
+                otherAppWidgets);
+
         sortWorkspaceItemsSpatially(mIdp, currentWorkspaceItems);
         for (ItemInfo itemInfo : currentWorkspaceItems) {
             switch (itemInfo.itemType) {
@@ -492,12 +476,13 @@
 
         // Add first page QSB
         if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
-            View qsb = mHomeElementInflater.inflate(
-                    R.layout.search_container_workspace, mWorkspace, false);
+            CellLayout firstScreen = mWorkspaceScreens.get(FIRST_SCREEN_ID);
+            View qsb = mHomeElementInflater.inflate(R.layout.qsb_preview, firstScreen,
+                    false);
             CellLayout.LayoutParams lp =
-                    new CellLayout.LayoutParams(0, 0, mWorkspace.getCountX(), 1);
+                    new CellLayout.LayoutParams(0, 0, firstScreen.getCountX(), 1);
             lp.canReorder = false;
-            mWorkspace.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true);
+            firstScreen.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true);
         }
 
         measureView(mRootView, mDp.widthPx, mDp.heightPx);
@@ -512,18 +497,6 @@
         view.layout(0, 0, width, height);
     }
 
-    /** Root layout for launcher preview that intercepts all touch events. */
-    public static class LauncherPreviewLayout extends InsettableFrameLayout {
-        public LauncherPreviewLayout(Context context, AttributeSet attrs) {
-            super(context, attrs);
-        }
-
-        @Override
-        public boolean onInterceptTouchEvent(MotionEvent ev) {
-            return true;
-        }
-    }
-
     private class LauncherPreviewAppWidgetHost extends AppWidgetHost {
 
         private LauncherPreviewAppWidgetHost(Context context) {
@@ -540,7 +513,6 @@
     }
 
     private static class LauncherPreviewAppWidgetHostView extends BaseLauncherAppWidgetHostView {
-
         private LauncherPreviewAppWidgetHostView(Context context) {
             super(context);
         }
@@ -550,4 +522,16 @@
             return false;
         }
     }
+
+    /** Root layout for launcher preview that intercepts all touch events. */
+    public static class LauncherPreviewLayout extends InsettableFrameLayout {
+        public LauncherPreviewLayout(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+
+        @Override
+        public boolean onInterceptTouchEvent(MotionEvent ev) {
+            return true;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index e45b8f7..24d6fe5 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -142,7 +142,7 @@
         mSystemBackgroundColor = preloadColors[PRELOAD_BACKGROUND_COLOR_INDEX];
         mIsDarkMode = isDarkMode;
 
-        setInternalProgress(info.getProgressLevel());
+        setLevel(info.getProgressLevel());
         setIsStartable(info.isAppStartable());
     }
 
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 3b140a0..2f3d5d8 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3.graphics;
 
-import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
@@ -38,14 +37,14 @@
 import androidx.annotation.UiThread;
 import androidx.annotation.WorkerThread;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
 import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext;
 import com.android.launcher3.model.BgDataModel;
-import com.android.launcher3.model.GridSizeMigrationTask;
 import com.android.launcher3.model.GridSizeMigrationTaskV2;
 import com.android.launcher3.model.LoaderTask;
 import com.android.launcher3.model.ModelDelegate;
@@ -149,7 +148,8 @@
             inflationContext = new ContextThemeWrapper(context,
                     Themes.getActivityThemeRes(context, mWallpaperColors.getColorHints()));
         } else {
-            inflationContext = new ContextThemeWrapper(mContext,  R.style.AppTheme);
+            inflationContext = new ContextThemeWrapper(mContext,
+                    Themes.getActivityThemeRes(mContext));
         }
 
         if (migrated) {
@@ -162,10 +162,18 @@
 
                 @Override
                 public void run() {
+                    DeviceProfile deviceProfile = mIdp.getDeviceProfile(previewContext);
+                    String query =
+                            LauncherSettings.Favorites.SCREEN + " = " + Workspace.FIRST_SCREEN_ID
+                            + " or " + LauncherSettings.Favorites.CONTAINER + " = "
+                            + LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+                    if (deviceProfile.isTwoPanels) {
+                        query += " or " + LauncherSettings.Favorites.SCREEN + " = "
+                                + Workspace.SECOND_SCREEN_ID;
+                    }
                     loadWorkspace(new ArrayList<>(), LauncherSettings.Favorites.PREVIEW_CONTENT_URI,
-                            LauncherSettings.Favorites.SCREEN + " = 0 or "
-                                    + LauncherSettings.Favorites.CONTAINER + " = "
-                                    + LauncherSettings.Favorites.CONTAINER_HOTSEAT);
+                            query);
+
                     MAIN_EXECUTOR.execute(() -> {
                         renderView(previewContext, mBgDataModel, mWidgetProvidersMap);
                         mOnDestroyCallbacks.add(previewContext::onDestroy);
@@ -185,16 +193,10 @@
 
     @WorkerThread
     private boolean doGridMigrationIfNecessary() {
-        boolean needsToMigrate =
-                MULTI_DB_GRID_MIRATION_ALGO.get()
-                        ? GridSizeMigrationTaskV2.needsToMigrate(mContext, mIdp)
-                        : GridSizeMigrationTask.needsToMigrate(mContext, mIdp);
-        if (!needsToMigrate) {
+        if (!GridSizeMigrationTaskV2.needsToMigrate(mContext, mIdp)) {
             return false;
         }
-        return MULTI_DB_GRID_MIRATION_ALGO.get()
-                ? GridSizeMigrationTaskV2.migrateGridIfNeeded(mContext, mIdp)
-                : GridSizeMigrationTask.migrateGridIfNeeded(mContext, mIdp);
+        return GridSizeMigrationTaskV2.migrateGridIfNeeded(mContext, mIdp);
     }
 
     @UiThread
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index cd13cd0..936eeb9 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -18,6 +18,9 @@
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
+
+import static java.util.stream.Collectors.groupingBy;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -30,16 +33,20 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ShortcutInfo;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
 import android.graphics.drawable.Drawable;
 import android.os.Process;
+import android.os.Trace;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 
 import androidx.annotation.NonNull;
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherFiles;
-import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
@@ -47,6 +54,7 @@
 import com.android.launcher3.icons.cache.CachingLogic;
 import com.android.launcher3.icons.cache.HandlerRunnable;
 import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.IconRequestInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -55,9 +63,16 @@
 import com.android.launcher3.util.InstantAppResolver;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.widget.WidgetSections;
+import com.android.launcher3.widget.WidgetSections.WidgetSection;
 
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
+import java.util.stream.Stream;
 
 /**
  * Cache of application icons.  Icons can be made from any thread.
@@ -134,6 +149,9 @@
      * Closes the cache DB. This will clear any in-memory cache.
      */
     public void close() {
+        // This will clear all pending updates
+        getUpdateHandler();
+
         mIconDb.close();
     }
 
@@ -259,7 +277,8 @@
             getTitleAndIcon(appInfo, false);
             return appInfo.bitmap;
         } else {
-            PackageItemInfo pkgInfo = new PackageItemInfo(shortcutInfo.getPackage());
+            PackageItemInfo pkgInfo = new PackageItemInfo(shortcutInfo.getPackage(),
+                    shortcutInfo.getUserHandle());
             getTitleAndIconForApp(pkgInfo, false);
             return pkgInfo.bitmap;
         }
@@ -303,6 +322,87 @@
         applyCacheEntry(entry, infoInOut);
     }
 
+    /**
+     * Creates an sql cursor for a query of a set of ItemInfoWithIcon icons and titles.
+     *
+     * @param iconRequestInfos List of IconRequestInfos representing titles and icons to query.
+     * @param user UserHandle all the given iconRequestInfos share
+     * @param useLowResIcons whether we should exclude the icon column from the sql results.
+     */
+    private <T extends ItemInfoWithIcon> Cursor createBulkQueryCursor(
+            List<IconRequestInfo<T>> iconRequestInfos, UserHandle user, boolean useLowResIcons)
+            throws SQLiteException {
+        String[] queryParams = Stream.concat(
+                iconRequestInfos.stream()
+                        .map(r -> r.itemInfo.getTargetComponent())
+                        .filter(Objects::nonNull)
+                        .distinct()
+                        .map(ComponentName::flattenToString),
+                Stream.of(Long.toString(getSerialNumberForUser(user)))).toArray(String[]::new);
+        String componentNameQuery = TextUtils.join(
+                ",", Collections.nCopies(queryParams.length - 1, "?"));
+
+        return mIconDb.query(
+                useLowResIcons ? IconDB.COLUMNS_LOW_RES : IconDB.COLUMNS_HIGH_RES,
+                IconDB.COLUMN_COMPONENT
+                        + " IN ( " + componentNameQuery + " )"
+                        + " AND " + IconDB.COLUMN_USER + " = ?",
+                queryParams);
+    }
+
+    /**
+     * Load and fill icons requested in iconRequestInfos using a single bulk sql query.
+     */
+    public synchronized <T extends ItemInfoWithIcon> void getTitlesAndIconsInBulk(
+            List<IconRequestInfo<T>> iconRequestInfos) {
+        Map<Pair<UserHandle, Boolean>, List<IconRequestInfo<T>>> iconLoadSubsectionsMap =
+                iconRequestInfos.stream()
+                        .collect(groupingBy(iconRequest ->
+                                Pair.create(iconRequest.itemInfo.user, iconRequest.useLowResIcon)));
+
+        Trace.beginSection("loadIconsInBulk");
+        iconLoadSubsectionsMap.forEach((sectionKey, filteredList) -> {
+            Map<ComponentName, List<IconRequestInfo<T>>> duplicateIconRequestsMap =
+                    filteredList.stream()
+                            .collect(groupingBy(iconRequest ->
+                                    iconRequest.itemInfo.getTargetComponent()));
+
+            Trace.beginSection("loadIconSubsectionInBulk");
+            try (Cursor c = createBulkQueryCursor(
+                    filteredList,
+                    /* user = */ sectionKey.first,
+                    /* useLowResIcons = */ sectionKey.second)) {
+                int componentNameColumnIndex = c.getColumnIndexOrThrow(IconDB.COLUMN_COMPONENT);
+                while (c.moveToNext()) {
+                    ComponentName cn = ComponentName.unflattenFromString(
+                            c.getString(componentNameColumnIndex));
+                    List<IconRequestInfo<T>> duplicateIconRequests =
+                            duplicateIconRequestsMap.get(cn);
+
+                    if (cn != null) {
+                        CacheEntry entry = cacheLocked(
+                                cn,
+                                /* user = */ sectionKey.first,
+                                () -> duplicateIconRequests.get(0).launcherActivityInfo,
+                                mLauncherActivityInfoCachingLogic,
+                                c,
+                                /* usePackageIcon= */ false,
+                                /* useLowResIcons = */ sectionKey.second);
+
+                        for (IconRequestInfo<T> iconRequest : duplicateIconRequests) {
+                            applyCacheEntry(entry, iconRequest.itemInfo);
+                        }
+                    }
+                }
+            } catch (SQLiteException e) {
+                Log.d(TAG, "Error reading icon cache", e);
+            } finally {
+                Trace.endSection();
+            }
+        });
+        Trace.endSection();
+    }
+
 
     /**
      * Fill in {@param infoInOut} with the corresponding icon and label.
@@ -312,8 +412,10 @@
         CacheEntry entry = getEntryForPackageLocked(
                 infoInOut.packageName, infoInOut.user, useLowResIcon);
         applyCacheEntry(entry, infoInOut);
-        if (infoInOut.category == PackageItemInfo.CONVERSATIONS) {
-            infoInOut.title = mContext.getString(R.string.widget_category_conversations);
+        if (infoInOut.widgetCategory != NO_CATEGORY) {
+            WidgetSection widgetSection = WidgetSections.getWidgetSections(mContext)
+                    .get(infoInOut.widgetCategory);
+            infoInOut.title = mContext.getString(widgetSection.mSectionTitle);
             infoInOut.contentDescription = mPackageManager.getUserBadgedLabel(
                     infoInOut.title, infoInOut.user);
         }
diff --git a/src/com/android/launcher3/logging/InstanceId.java b/src/com/android/launcher3/logging/InstanceId.java
index e720d75..3c4a644 100644
--- a/src/com/android/launcher3/logging/InstanceId.java
+++ b/src/com/android/launcher3/logging/InstanceId.java
@@ -36,10 +36,10 @@
  */
 public final class InstanceId implements Parcelable {
     // At most 20 bits: ~1m possibilities, ~0.5% probability of collision in 100 values
-    static final int INSTANCE_ID_MAX = 1 << 20;
+    public static final int INSTANCE_ID_MAX = 1 << 20;
 
     private final int mId;
-    InstanceId(int id) {
+    public InstanceId(int id) {
         mId = min(max(0, id), INSTANCE_ID_MAX);
     }
 
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 79e5b5d..d987212 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.logger.LauncherAtom.ToState;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.ResourceBasedOverride;
+import com.android.launcher3.views.ActivityContext;
 
 /**
  * Handles the user event logging in R+.
@@ -53,6 +54,9 @@
     public static final int LAUNCHER_STATE_UNCHANGED = 5;
 
     private InstanceId mInstanceId;
+
+    protected @Nullable ActivityContext mActivityContext = null;
+
     /**
      * Returns event enum based on the two state transition information when swipe
      * gesture happens(to be removed during UserEventDispatcher cleanup).
@@ -76,6 +80,22 @@
     }
 
     public interface EventEnum {
+
+        /**
+         * Tag used to request new UI Event IDs via presubmit analysis.
+         *
+         * <p>Use RESERVE_NEW_UI_EVENT_ID as the constructor parameter for a new {@link EventEnum}
+         * to signal the presubmit analyzer to reserve a new ID for the event. The new ID will be
+         * returned as a Gerrit presubmit finding.  Do not submit {@code RESERVE_NEW_UI_EVENT_ID} as
+         * the constructor parameter for any event.
+         *
+         * <pre>
+         * &#064;UiEvent(doc = "Briefly describe the interaction when this event will be logged")
+         * UNIQUE_EVENT_NAME(RESERVE_NEW_UI_EVENT_ID);
+         * </pre>
+         */
+        int RESERVE_NEW_UI_EVENT_ID = Integer.MIN_VALUE; // Negative IDs are ignored by the logger.
+
         int getId();
     }
 
@@ -265,6 +285,9 @@
         @UiEvent(doc = "User tapped on the share button on overview")
         LAUNCHER_OVERVIEW_ACTIONS_SHARE(582),
 
+        @UiEvent(doc = "User tapped on the split screen button on overview")
+        LAUNCHER_OVERVIEW_ACTIONS_SPLIT(895),
+
         @UiEvent(doc = "User tapped on the close button in select mode")
         LAUNCHER_SELECT_MODE_CLOSE(583),
 
@@ -486,8 +509,16 @@
         LAUNCHER_TURN_ON_WORK_APPS_TAP(838),
 
         @UiEvent(doc = "User tapped on 'Turn off work apps' button in all apps window.")
-        LAUNCHER_TURN_OFF_WORK_APPS_TAP(839)
-        ;
+        LAUNCHER_TURN_OFF_WORK_APPS_TAP(839),
+
+        @UiEvent(doc = "Launcher item drop failed since there was not enough room on the screen.")
+        LAUNCHER_ITEM_DROP_FAILED_INSUFFICIENT_SPACE(872),
+
+        @UiEvent(doc = "User long pressed on the taskbar background to hide the taskbar")
+        LAUNCHER_TASKBAR_LONGPRESS_HIDE(896),
+
+        @UiEvent(doc = "User long pressed on the taskbar gesture handle to show the taskbar")
+        LAUNCHER_TASKBAR_LONGPRESS_SHOW(897);
 
         // ADD MORE
 
@@ -627,7 +658,7 @@
     public StatsLogger logger() {
         StatsLogger logger = createLogger();
         if (mInstanceId != null) {
-            return logger.withInstanceId(mInstanceId);
+            logger.withInstanceId(mInstanceId);
         }
         return logger;
     }
@@ -650,7 +681,9 @@
      * Creates a new instance of {@link StatsLogManager} based on provided context.
      */
     public static StatsLogManager newInstance(Context context) {
-        return Overrides.getObject(StatsLogManager.class,
+        StatsLogManager manager = Overrides.getObject(StatsLogManager.class,
                 context.getApplicationContext(), R.string.stats_log_manager_class);
+        manager.mActivityContext = ActivityContext.lookupContextNoThrow(context);
+        return manager;
     }
 }
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 01b3e6e..fea15c4 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -15,6 +15,9 @@
  */
 package com.android.launcher3.model;
 
+import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
+import static com.android.launcher3.WorkspaceLayoutManager.SECOND_SCREEN_ID;
+
 import android.content.Intent;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
@@ -27,6 +30,7 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel.CallbackTask;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.data.AppInfo;
@@ -38,6 +42,7 @@
 import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.PackageManagerHelper;
 
 import java.util.ArrayList;
@@ -287,28 +292,28 @@
 
         // Find appropriate space for the item.
         int screenId = 0;
-        int[] cordinates = new int[2];
+        int[] coordinates = new int[2];
         boolean found = false;
 
         int screenCount = workspaceScreens.size();
         // First check the preferred screen.
-        int preferredScreenIndex = workspaceScreens.isEmpty() ? 0 : 1;
-        if (preferredScreenIndex < screenCount) {
-            screenId = workspaceScreens.get(preferredScreenIndex);
-            found = findNextAvailableIconSpaceInScreen(
-                    app, screenItems.get(screenId), cordinates, spanX, spanY);
+        IntSet screensToExclude = new IntSet();
+        if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
+            screensToExclude.add(FIRST_SCREEN_ID);
+
+            // On split display we don't want to add the new items onto the second screen.
+            if (app.getInvariantDeviceProfile().isSplitDisplay) {
+                screensToExclude.add(SECOND_SCREEN_ID);
+            }
         }
 
-        if (!found) {
-            // Search on any of the screens starting from the first screen.
-            for (int screen = 1; screen < screenCount; screen++) {
-                screenId = workspaceScreens.get(screen);
-                if (findNextAvailableIconSpaceInScreen(
-                        app, screenItems.get(screenId), cordinates, spanX, spanY)) {
-                    // We found a space for it
-                    found = true;
-                    break;
-                }
+        for (int screen = 0; screen < screenCount; screen++) {
+            screenId = workspaceScreens.get(screen);
+            if (!screensToExclude.contains(screenId) && findNextAvailableIconSpaceInScreen(
+                    app, screenItems.get(screenId), coordinates, spanX, spanY)) {
+                // We found a space for it
+                found = true;
+                break;
             }
         }
 
@@ -324,11 +329,11 @@
 
             // If we still can't find an empty space, then God help us all!!!
             if (!findNextAvailableIconSpaceInScreen(
-                    app, screenItems.get(screenId), cordinates, spanX, spanY)) {
+                    app, screenItems.get(screenId), coordinates, spanX, spanY)) {
                 throw new RuntimeException("Can't find space to add the item");
             }
         }
-        return new int[] {screenId, cordinates[0], cordinates[1]};
+        return new int[] {screenId, coordinates[0], coordinates[1]};
     }
 
     private boolean findNextAvailableIconSpaceInScreen(
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index 92b5885..dbed9a9 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -130,30 +130,54 @@
      * If the app is already in the list, doesn't add it.
      */
     public void add(AppInfo info, LauncherActivityInfo activityInfo) {
+        add(info, activityInfo, true);
+    }
+
+    public void add(AppInfo info, LauncherActivityInfo activityInfo, boolean loadIcon) {
         if (!mAppFilter.shouldShowApp(info.componentName)) {
             return;
         }
         if (findAppInfo(info.componentName, info.user) != null) {
             return;
         }
-        mIconCache.getTitleAndIcon(info, activityInfo, false /* useLowResIcon */);
-        info.sectionName = mIndex.computeSectionName(info.title);
+        if (loadIcon) {
+            mIconCache.getTitleAndIcon(info, activityInfo, false /* useLowResIcon */);
+            info.sectionName = mIndex.computeSectionName(info.title);
+        }
 
         data.add(info);
         mDataChanged = true;
     }
 
-    public void addPromiseApp(Context context, PackageInstallInfo installInfo) {
-        // only if not yet installed
-        if (!new PackageManagerHelper(context)
-                .isAppInstalled(installInfo.packageName, installInfo.user)) {
-            AppInfo info = new AppInfo(installInfo);
-            mIconCache.getTitleAndIcon(info, info.usingLowResIcon());
-            info.sectionName = mIndex.computeSectionName(info.title);
+    @Nullable
+    public AppInfo addPromiseApp(Context context, PackageInstallInfo installInfo) {
+        return addPromiseApp(context, installInfo, true);
+    }
 
-            data.add(info);
-            mDataChanged = true;
+    @Nullable
+    public AppInfo addPromiseApp(
+            Context context, PackageInstallInfo installInfo, boolean loadIcon) {
+        // only if not yet installed
+        if (new PackageManagerHelper(context)
+                .isAppInstalled(installInfo.packageName, installInfo.user)) {
+            return null;
         }
+        AppInfo promiseAppInfo = new AppInfo(installInfo);
+
+        if (loadIcon) {
+            mIconCache.getTitleAndIcon(promiseAppInfo, promiseAppInfo.usingLowResIcon());
+            promiseAppInfo.sectionName = mIndex.computeSectionName(promiseAppInfo.title);
+        }
+
+        data.add(promiseAppInfo);
+        mDataChanged = true;
+
+        return promiseAppInfo;
+    }
+
+    public void updateSectionName(AppInfo appInfo) {
+        appInfo.sectionName = mIndex.computeSectionName(appInfo.title);
+
     }
 
     /** Updates the given PackageInstallInfo's associated AppInfo's installation info. */
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index 5c85bab..d270cc5 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -16,24 +16,28 @@
 
 package com.android.launcher3.model;
 
+import static com.android.launcher3.model.ItemInstallQueue.FLAG_LOADER_RUNNING;
 import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
 import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
+import android.os.Process;
 import android.util.Log;
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.PagedView;
 import com.android.launcher3.model.BgDataModel.Callbacks;
 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.LauncherAppWidgetInfo;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.LooperExecutor;
 import com.android.launcher3.util.LooperIdleLock;
-import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.launcher3.util.RunnableList;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -71,7 +75,7 @@
     /**
      * Binds all loaded data to actual views on the main thread.
      */
-    public void bindWorkspace() {
+    public void bindWorkspace(boolean incrementBindId) {
         // Save a copy of all the bg-thread collections
         ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
         ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
@@ -83,7 +87,9 @@
             appWidgets.addAll(mBgDataModel.appWidgets);
             orderedScreenIds.addAll(mBgDataModel.collectWorkspaceScreens());
             mBgDataModel.extraItems.forEach(extraItems::add);
-            mBgDataModel.lastBindId++;
+            if (incrementBindId) {
+                mBgDataModel.lastBindId++;
+            }
             mMyBindingId = mBgDataModel.lastBindId;
         }
 
@@ -160,20 +166,7 @@
         }
 
         private void bind() {
-            final int currentScreen;
-            {
-                // Create an anonymous scope to calculate currentScreen as it has to be a
-                // final variable.
-                int currScreen = mCallbacks.getPageToBindSynchronously();
-                if (currScreen >= mOrderedScreenIds.size()) {
-                    // There may be no workspace screens (just hotseat items and an empty page).
-                    currScreen = PagedView.INVALID_PAGE;
-                }
-                currentScreen = currScreen;
-            }
-            final boolean validFirstPage = currentScreen >= 0;
-            final int currentScreenId =
-                    validFirstPage ? mOrderedScreenIds.get(currentScreen) : INVALID_SCREEN_ID;
+            IntSet currentScreenIds = mCallbacks.getPagesToBindSynchronously(mOrderedScreenIds);
 
             // Separate the items that are on the current screen, and all the other remaining items
             ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
@@ -181,9 +174,21 @@
             ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
             ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
 
-            filterCurrentWorkspaceItems(currentScreenId, mWorkspaceItems, currentWorkspaceItems,
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.NULL_INT_SET, "bind (1) currentScreenIds: "
+                        + currentScreenIds
+                        + ", pointer: "
+                        + mCallbacks
+                        + ", name: "
+                        + mCallbacks.getClass().getName());
+            }
+            filterCurrentWorkspaceItems(currentScreenIds, mWorkspaceItems, currentWorkspaceItems,
                     otherWorkspaceItems);
-            filterCurrentWorkspaceItems(currentScreenId, mAppWidgets, currentAppWidgets,
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.NULL_INT_SET, "bind (2) currentScreenIds: "
+                        + currentScreenIds);
+            }
+            filterCurrentWorkspaceItems(currentScreenIds, mAppWidgets, currentAppWidgets,
                     otherAppWidgets);
             final InvariantDeviceProfile idp = mApp.getInvariantDeviceProfile();
             sortWorkspaceItemsSpatially(idp, currentWorkspaceItems);
@@ -198,40 +203,29 @@
             // Bind workspace screens
             executeCallbacksTask(c -> c.bindScreens(mOrderedScreenIds), mUiExecutor);
 
-            Executor mainExecutor = mUiExecutor;
             // Load items on the current page.
-            bindWorkspaceItems(currentWorkspaceItems, mainExecutor);
-            bindAppWidgets(currentAppWidgets, mainExecutor);
+            bindWorkspaceItems(currentWorkspaceItems, mUiExecutor);
+            bindAppWidgets(currentAppWidgets, mUiExecutor);
             mExtraItems.forEach(item ->
-                    executeCallbacksTask(c -> c.bindExtraContainerItems(item), mainExecutor));
+                    executeCallbacksTask(c -> c.bindExtraContainerItems(item), mUiExecutor));
 
-            // In case of validFirstPage, only bind the first screen, and defer binding the
-            // remaining screens after first onDraw (and an optional the fade animation whichever
-            // happens later).
-            // This ensures that the first screen is immediately visible (eg. during rotation)
-            // In case of !validFirstPage, bind all pages one after other.
+            RunnableList pendingTasks = new RunnableList();
+            Executor pendingExecutor = pendingTasks::add;
+            bindWorkspaceItems(otherWorkspaceItems, pendingExecutor);
+            bindAppWidgets(otherAppWidgets, pendingExecutor);
+            executeCallbacksTask(c -> c.finishBindingItems(currentScreenIds), pendingExecutor);
+            pendingExecutor.execute(
+                    () -> {
+                        MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+                        ItemInstallQueue.INSTANCE.get(mApp.getContext())
+                                .resumeModelPush(FLAG_LOADER_RUNNING);
+                    });
 
-            final Executor deferredExecutor =
-                    validFirstPage ? new ViewOnDrawExecutor() : mainExecutor;
-
-            executeCallbacksTask(c -> c.finishFirstPageBind(
-                    validFirstPage ? (ViewOnDrawExecutor) deferredExecutor : null), mainExecutor);
-
-            bindWorkspaceItems(otherWorkspaceItems, deferredExecutor);
-            bindAppWidgets(otherAppWidgets, deferredExecutor);
-            // Tell the workspace that we're done binding items
-            executeCallbacksTask(c -> c.finishBindingItems(currentScreen), deferredExecutor);
-
-            if (validFirstPage) {
-                executeCallbacksTask(c -> {
-                    // We are loading synchronously, which means, some of the pages will be
-                    // bound after first draw. Inform the mCallbacks that page binding is
-                    // not complete, and schedule the remaining pages.
-                    c.onPageBoundSynchronously(currentScreen);
-                    c.executeOnNextDraw((ViewOnDrawExecutor) deferredExecutor);
-
-                }, mUiExecutor);
-            }
+            executeCallbacksTask(
+                    c -> {
+                        MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+                        c.onInitialBindComplete(currentScreenIds, pendingTasks);
+                    }, mUiExecutor);
         }
 
         private void bindWorkspaceItems(
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index ad553d5..a3a4717 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -90,7 +90,7 @@
     public ModelWriter getModelWriter() {
         // Updates from model task, do not deal with icon position in hotseat. Also no need to
         // verify changes as the ModelTasks always push the changes to callbacks
-        return mModel.getWriter(false /* hasVerticalHotseat */, false /* verifyChanges */);
+        return mModel.getWriter(false /* hasVerticalHotseat */, false /* verifyChanges */, null);
     }
 
     public void bindUpdatedWorkspaceItems(List<WorkspaceItemInfo> allUpdates) {
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 1d7d1a2..13ad90e 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -49,7 +49,7 @@
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.IntSparseArrayMap;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 
 import java.io.FileDescriptor;
@@ -446,37 +446,50 @@
         int FLAG_QUIET_MODE_CHANGE_PERMISSION = 1 << 2;
 
         /**
-         * Returns the page number to bind first, synchronously if possible or -1
+         * Returns an IntSet of page ids to bind first, synchronously if possible
+         * or an empty IntSet
+         * @param orderedScreenIds All the page ids to be bound
          */
-        int getPageToBindSynchronously();
-        void clearPendingBinds();
-        void startBinding();
-        void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons);
-        void bindScreens(IntArray orderedScreenIds);
-        void finishFirstPageBind(ViewOnDrawExecutor executor);
-        void finishBindingItems(int pageBoundFirst);
-        void preAddApps();
-        void bindAppsAdded(IntArray newScreens,
-                ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated);
+        default IntSet getPagesToBindSynchronously(IntArray orderedScreenIds) {
+            return new IntSet();
+        }
+
+        default void clearPendingBinds() { }
+        default void startBinding() { }
+
+        default void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons) { }
+        default void bindScreens(IntArray orderedScreenIds) { }
+        default void finishBindingItems(IntSet pagesBoundFirst) { }
+        default void preAddApps() { }
+        default void bindAppsAdded(IntArray newScreens,
+                ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated) { }
+
+        /**
+         * Called when some persistent property of an item is modified
+         */
+        default void bindItemsModified(List<ItemInfo> items) { }
 
         /**
          * Binds updated incremental download progress
          */
-        void bindIncrementalDownloadProgressUpdated(AppInfo app);
-        void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated);
-        void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
-        void bindRestoreItemsChange(HashSet<ItemInfo> updates);
-        void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher);
-        void bindAllWidgets(List<WidgetsListBaseEntry> widgets);
-        void onPageBoundSynchronously(int page);
-        void executeOnNextDraw(ViewOnDrawExecutor executor);
-        void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap);
+        default void bindIncrementalDownloadProgressUpdated(AppInfo app) { }
+        default void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) { }
+        default void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets) { }
+        default void bindRestoreItemsChange(HashSet<ItemInfo> updates) { }
+        default void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher) { }
+        default void bindAllWidgets(List<WidgetsListBaseEntry> widgets) { }
+
+        default void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks) {
+            pendingTasks.executeAllAndDestroy();
+        }
+
+        default void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap) { }
 
         /**
          * Binds extra item provided any external source
          */
         default void bindExtraContainerItems(FixedContainerItems item) { }
 
-        void bindAllApplications(AppInfo[] apps, int flags);
+        default void bindAllApplications(AppInfo[] apps, int flags) { }
     }
 }
diff --git a/src/com/android/launcher3/model/DeviceGridState.java b/src/com/android/launcher3/model/DeviceGridState.java
new file mode 100644
index 0000000..0fc4c2d
--- /dev/null
+++ b/src/com/android/launcher3/model/DeviceGridState.java
@@ -0,0 +1,141 @@
+/*
+ * 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.model;
+
+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 android.content.Context;
+import android.content.SharedPreferences;
+import android.text.TextUtils;
+
+import androidx.annotation.IntDef;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
+import com.android.launcher3.util.IntSet;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * Utility class representing persisted grid properties.
+ */
+public class DeviceGridState {
+
+    public static final String KEY_WORKSPACE_SIZE = "migration_src_workspace_size";
+    public static final String KEY_HOTSEAT_COUNT = "migration_src_hotseat_count";
+    public static final String KEY_DEVICE_TYPE = "migration_src_device_type";
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({TYPE_PHONE, TYPE_MULTI_DISPLAY, TYPE_TABLET})
+    public @interface DeviceType{}
+    public static final int TYPE_PHONE = 0;
+    public static final int TYPE_MULTI_DISPLAY = 1;
+    public static final int TYPE_TABLET = 2;
+
+    private static final IntSet COMPATIBLE_TYPES = IntSet.wrap(TYPE_PHONE, TYPE_MULTI_DISPLAY);
+
+    public static boolean deviceTypeCompatible(@DeviceType int typeA, @DeviceType int typeB) {
+        return typeA == typeB
+                || (COMPATIBLE_TYPES.contains(typeA) && COMPATIBLE_TYPES.contains(typeB));
+    }
+
+    private final String mGridSizeString;
+    private final int mNumHotseat;
+    private final @DeviceType int mDeviceType;
+
+    public DeviceGridState(InvariantDeviceProfile idp) {
+        mGridSizeString = String.format(Locale.ENGLISH, "%d,%d", idp.numColumns, idp.numRows);
+        mNumHotseat = idp.numDatabaseHotseatIcons;
+        mDeviceType = idp.supportedProfiles.size() > 2
+                ? TYPE_MULTI_DISPLAY
+                : idp.supportedProfiles.stream().allMatch(dp -> dp.isTablet)
+                        ? TYPE_TABLET
+                        : TYPE_PHONE;
+    }
+
+    public DeviceGridState(Context context) {
+        SharedPreferences prefs = Utilities.getPrefs(context);
+        mGridSizeString = prefs.getString(KEY_WORKSPACE_SIZE, "");
+        mNumHotseat = prefs.getInt(KEY_HOTSEAT_COUNT, -1);
+        mDeviceType = prefs.getInt(KEY_DEVICE_TYPE, TYPE_PHONE);
+    }
+
+    /**
+     * Returns the device type for the grid
+     */
+    public @DeviceType int getDeviceType() {
+        return mDeviceType;
+    }
+
+    /**
+     * Stores the device state to shared preferences
+     */
+    public void writeToPrefs(Context context) {
+        Utilities.getPrefs(context).edit()
+                .putString(KEY_WORKSPACE_SIZE, mGridSizeString)
+                .putInt(KEY_HOTSEAT_COUNT, mNumHotseat)
+                .putInt(KEY_DEVICE_TYPE, mDeviceType)
+                .apply();
+    }
+
+    /**
+     * Returns the logging event corresponding to the grid state
+     */
+    public LauncherEvent getWorkspaceSizeEvent() {
+        if (!TextUtils.isEmpty(mGridSizeString)) {
+            switch (mGridSizeString.charAt(0)) {
+                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;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        return "DeviceGridState{"
+                + "mGridSizeString='" + mGridSizeString + '\''
+                + ", mNumHotseat=" + mNumHotseat
+                + ", mDeviceType=" + mDeviceType
+                + '}';
+    }
+
+    /**
+     * Returns true if the database from another DeviceGridState can be loaded into the current
+     * DeviceGridState without migration, or false otherwise.
+     */
+    public boolean isCompatible(DeviceGridState other) {
+        if (this == other) return true;
+        if (other == null) return false;
+        return mNumHotseat == other.mNumHotseat
+                && deviceTypeCompatible(mDeviceType, other.mDeviceType)
+                && Objects.equals(mGridSizeString, other.mGridSizeString);
+    }
+}
diff --git a/src/com/android/launcher3/model/GridBackupTable.java b/src/com/android/launcher3/model/GridBackupTable.java
index acfc339..51cbf4b 100644
--- a/src/com/android/launcher3/model/GridBackupTable.java
+++ b/src/com/android/launcher3/model/GridBackupTable.java
@@ -23,14 +23,12 @@
 import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Point;
 import android.os.Process;
 import android.util.Log;
 
 import androidx.annotation.IntDef;
 
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.LauncherSettings.Settings;
 import com.android.launcher3.pm.UserCache;
 
 /**
@@ -85,49 +83,6 @@
     }
 
     /**
-     * Create a backup from current workspace layout if one isn't created already (Note backup
-     * created this way is always sanitized). Otherwise restore from the backup instead.
-     */
-    public boolean backupOrRestoreAsNeeded() {
-        // Check if backup table exists
-        if (!tableExists(mDb, BACKUP_TABLE_NAME)) {
-            if (Settings.call(mContext.getContentResolver(), Settings.METHOD_WAS_EMPTY_DB_CREATED)
-                    .getBoolean(Settings.EXTRA_VALUE, false)) {
-                // No need to copy if empty DB was created.
-                return false;
-            }
-            doBackup(UserCache.INSTANCE.get(mContext).getSerialNumberForUser(
-                    Process.myUserHandle()), 0);
-            return false;
-        }
-        return restoreIfBackupExists(Favorites.TABLE_NAME);
-    }
-
-    public boolean restoreToPreviewIfBackupExists() {
-        if (!tableExists(mDb, BACKUP_TABLE_NAME)) {
-            return false;
-        }
-
-        return restoreIfBackupExists(Favorites.PREVIEW_TABLE_NAME);
-    }
-
-    private boolean restoreIfBackupExists(String toTableName) {
-        if (loadDBProperties() != STATE_SANITIZED) {
-            return false;
-        }
-        long userSerial = UserCache.INSTANCE.get(mContext).getSerialNumberForUser(
-                Process.myUserHandle());
-        copyTable(mDb, BACKUP_TABLE_NAME, toTableName, userSerial);
-        Log.d(TAG, "Backup table found");
-        return true;
-    }
-
-    public int getRestoreHotseatAndGridSize(Point outGridSize) {
-        outGridSize.set(mRestoredGridX, mRestoredGridY);
-        return mRestoredHotseatSize;
-    }
-
-    /**
      * Creates a new table and populates with copy of Favorites.TABLE_NAME
      */
     public void createCustomBackupTable(String tableName) {
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
deleted file mode 100644
index 7b3e509..0000000
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ /dev/null
@@ -1,1098 +0,0 @@
-package com.android.launcher3.model;
-
-import static com.android.launcher3.InvariantDeviceProfile.KEY_MIGRATION_SRC_HOTSEAT_COUNT;
-import static com.android.launcher3.InvariantDeviceProfile.KEY_MIGRATION_SRC_WORKSPACE_SIZE;
-import static com.android.launcher3.LauncherSettings.Settings.EXTRA_VALUE;
-import static com.android.launcher3.Utilities.getPointString;
-import static com.android.launcher3.Utilities.parsePoint;
-import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
-
-import android.content.ComponentName;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Point;
-import android.os.SystemClock;
-import android.util.Log;
-import android.util.SparseArray;
-
-import androidx.annotation.VisibleForTesting;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.LauncherSettings.Settings;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.graphics.LauncherPreviewRenderer;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.pm.InstallSessionHelper;
-import com.android.launcher3.provider.LauncherDbUtils;
-import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
-import com.android.launcher3.util.GridOccupancy;
-import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.IntSparseArrayMap;
-import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.widget.WidgetManagerHelper;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-
-/**
- * This class takes care of shrinking the workspace (by maximum of one row and one column), as a
- * result of restoring from a larger device or device density change.
- */
-public class GridSizeMigrationTask {
-
-    private static final String TAG = "GridSizeMigrationTask";
-    private static final boolean DEBUG = false;
-
-    // These are carefully selected weights for various item types (Math.random?), to allow for
-    // the least absurd migration experience.
-    private static final float WT_SHORTCUT = 1;
-    private static final float WT_APPLICATION = 0.8f;
-    private static final float WT_WIDGET_MIN = 2;
-    private static final float WT_WIDGET_FACTOR = 0.6f;
-    private static final float WT_FOLDER_FACTOR = 0.5f;
-
-    protected final SQLiteDatabase mDb;
-    protected final Context mContext;
-
-    protected final IntArray mEntryToRemove = new IntArray();
-    protected final ArrayList<DbEntry> mCarryOver = new ArrayList<>();
-
-    private final SparseArray<ContentValues> mUpdateOperations = new SparseArray<>();
-    private final HashSet<String> mValidPackages;
-    private final String mTableName;
-
-    private final int mSrcX, mSrcY;
-    private final int mTrgX, mTrgY;
-    private final boolean mShouldRemoveX, mShouldRemoveY;
-
-    private final int mSrcHotseatSize;
-    private final int mDestHotseatSize;
-
-    protected GridSizeMigrationTask(Context context, SQLiteDatabase db,
-            HashSet<String> validPackages, boolean usePreviewTable, Point sourceSize,
-            Point targetSize) {
-        mContext = context;
-        mDb = db;
-        mValidPackages = validPackages;
-        mTableName = usePreviewTable ? Favorites.PREVIEW_TABLE_NAME : Favorites.TABLE_NAME;
-
-        mSrcX = sourceSize.x;
-        mSrcY = sourceSize.y;
-
-        mTrgX = targetSize.x;
-        mTrgY = targetSize.y;
-
-        mShouldRemoveX = mTrgX < mSrcX;
-        mShouldRemoveY = mTrgY < mSrcY;
-
-        // Non-used variables
-        mSrcHotseatSize = mDestHotseatSize = -1;
-    }
-
-    protected GridSizeMigrationTask(Context context, SQLiteDatabase db,
-            HashSet<String> validPackages, boolean usePreviewTable, int srcHotseatSize,
-            int destHotseatSize) {
-        mContext = context;
-        mDb = db;
-        mValidPackages = validPackages;
-        mTableName = usePreviewTable ? Favorites.PREVIEW_TABLE_NAME : Favorites.TABLE_NAME;
-
-        mSrcHotseatSize = srcHotseatSize;
-
-        mDestHotseatSize = destHotseatSize;
-
-        // Non-used variables
-        mSrcX = mSrcY = mTrgX = mTrgY = -1;
-        mShouldRemoveX = mShouldRemoveY = false;
-    }
-
-    /**
-     * Applied all the pending DB operations
-     *
-     * @return true if any DB operation was commited.
-     */
-    private boolean applyOperations() throws Exception {
-        // Update items
-        int updateCount = mUpdateOperations.size();
-        for (int i = 0; i < updateCount; i++) {
-            mDb.update(mTableName, mUpdateOperations.valueAt(i),
-                    "_id=" + mUpdateOperations.keyAt(i), null);
-        }
-
-        if (!mEntryToRemove.isEmpty()) {
-            if (DEBUG) {
-                Log.d(TAG, "Removing items: " + mEntryToRemove.toConcatString());
-            }
-            mDb.delete(mTableName, Utilities.createDbSelectionQuery(Favorites._ID, mEntryToRemove),
-                    null);
-        }
-
-        return updateCount > 0 || !mEntryToRemove.isEmpty();
-    }
-
-    /**
-     * To migrate hotseat, we load all the entries in order (LTR or RTL) and arrange them
-     * in the order in the new hotseat while keeping an empty space for all-apps. If the number of
-     * entries is more than what can fit in the new hotseat, we drop the entries with least weight.
-     * For weight calculation {@see #WT_SHORTCUT}, {@see #WT_APPLICATION}
-     * & {@see #WT_FOLDER_FACTOR}.
-     *
-     * @return true if any DB change was made
-     */
-    protected boolean migrateHotseat() throws Exception {
-        ArrayList<DbEntry> items = loadHotseatEntries();
-        while (items.size() > mDestHotseatSize) {
-            // Pick the center item by default.
-            DbEntry toRemove = items.get(items.size() / 2);
-
-            // Find the item with least weight.
-            for (DbEntry entry : items) {
-                if (entry.weight < toRemove.weight) {
-                    toRemove = entry;
-                }
-            }
-
-            mEntryToRemove.add(toRemove.id);
-            items.remove(toRemove);
-        }
-
-        // Update screen IDS
-        int newScreenId = 0;
-        for (DbEntry entry : items) {
-            if (entry.screenId != newScreenId) {
-                entry.screenId = newScreenId;
-
-                // These values does not affect the item position, but we should set them
-                // to something other than -1.
-                entry.cellX = newScreenId;
-                entry.cellY = 0;
-
-                update(entry);
-            }
-
-            newScreenId++;
-        }
-
-        return applyOperations();
-    }
-
-    @VisibleForTesting
-    static IntArray getWorkspaceScreenIds(SQLiteDatabase db, String tableName) {
-        return LauncherDbUtils.queryIntArray(db, tableName, Favorites.SCREEN,
-                Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP,
-                Favorites.SCREEN, Favorites.SCREEN);
-    }
-
-    /**
-     * @return true if any DB change was made
-     */
-    protected boolean migrateWorkspace() throws Exception {
-        IntArray allScreens = getWorkspaceScreenIds(mDb, mTableName);
-        if (allScreens.isEmpty()) {
-            throw new Exception("Unable to get workspace screens");
-        }
-
-        for (int i = 0; i < allScreens.size(); i++) {
-            int screenId = allScreens.get(i);
-            if (DEBUG) {
-                Log.d(TAG, "Migrating " + screenId);
-            }
-            migrateScreen(screenId);
-        }
-
-        if (!mCarryOver.isEmpty()) {
-            IntSparseArrayMap<DbEntry> itemMap = new IntSparseArrayMap<>();
-            for (DbEntry e : mCarryOver) {
-                itemMap.put(e.id, e);
-            }
-
-            do {
-                // Some items are still remaining. Try adding a few new screens.
-
-                // At every iteration, make sure that at least one item is removed from
-                // {@link #mCarryOver}, to prevent an infinite loop. If no item could be removed,
-                // break the loop and abort migration by throwing an exception.
-                OptimalPlacementSolution placement = new OptimalPlacementSolution(
-                        new GridOccupancy(mTrgX, mTrgY), deepCopy(mCarryOver), 0, true);
-                placement.find();
-                if (placement.finalPlacedItems.size() > 0) {
-                    int newScreenId = LauncherSettings.Settings.call(
-                            mContext.getContentResolver(),
-                            LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
-                            .getInt(EXTRA_VALUE);
-                    for (DbEntry item : placement.finalPlacedItems) {
-                        if (!mCarryOver.remove(itemMap.get(item.id))) {
-                            throw new Exception("Unable to find matching items");
-                        }
-                        item.screenId = newScreenId;
-                        update(item);
-                    }
-                } else {
-                    throw new Exception("None of the items can be placed on an empty screen");
-                }
-
-            } while (!mCarryOver.isEmpty());
-        }
-        return applyOperations();
-    }
-
-    /**
-     * Migrate a particular screen id.
-     * Strategy:
-     *  1) For all possible combinations of row and column, pick the one which causes the least
-     *    data loss: {@link #tryRemove(int, int, int, ArrayList, float[])}
-     *  2) Maintain a list of all lost items before this screen, and add any new item lost from
-     *    this screen to that list as well.
-     *  3) If all those items from the above list can be placed on this screen, place them
-     *    (otherwise they are placed on a new screen).
-     */
-    protected void migrateScreen(int screenId) {
-        // If we are migrating the first screen, do not touch the first row.
-        int startY = (FeatureFlags.QSB_ON_FIRST_SCREEN && screenId == Workspace.FIRST_SCREEN_ID)
-                ? 1 : 0;
-
-        ArrayList<DbEntry> items = loadWorkspaceEntries(screenId);
-
-        int removedCol = Integer.MAX_VALUE;
-        int removedRow = Integer.MAX_VALUE;
-
-        // removeWt represents the cost function for loss of items during migration, and moveWt
-        // represents the cost function for repositioning the items. moveWt is only considered if
-        // removeWt is same for two different configurations.
-        // Start with Float.MAX_VALUE (assuming full data) and pick the configuration with least
-        // cost.
-        float removeWt = Float.MAX_VALUE;
-        float moveWt = Float.MAX_VALUE;
-        float[] outLoss = new float[2];
-        ArrayList<DbEntry> finalItems = null;
-
-        // Try removing all possible combinations
-        for (int x = 0; x < mSrcX; x++) {
-            // Try removing the rows first from bottom. This keeps the workspace
-            // nicely aligned with hotseat.
-            for (int y = mSrcY - 1; y >= startY; y--) {
-                // Use a deep copy when trying out a particular combination as it can change
-                // the underlying object.
-                ArrayList<DbEntry> itemsOnScreen = tryRemove(x, y, startY, deepCopy(items),
-                        outLoss);
-
-                if ((outLoss[0] < removeWt) || ((outLoss[0] == removeWt) && (outLoss[1]
-                        < moveWt))) {
-                    removeWt = outLoss[0];
-                    moveWt = outLoss[1];
-                    removedCol = mShouldRemoveX ? x : removedCol;
-                    removedRow = mShouldRemoveY ? y : removedRow;
-                    finalItems = itemsOnScreen;
-                }
-
-                // No need to loop over all rows, if a row removal is not needed.
-                if (!mShouldRemoveY) {
-                    break;
-                }
-            }
-
-            if (!mShouldRemoveX) {
-                break;
-            }
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, String.format("Removing row %d, column %d on screen %d",
-                    removedRow, removedCol, screenId));
-        }
-
-        IntSparseArrayMap<DbEntry> itemMap = new IntSparseArrayMap<>();
-        for (DbEntry e : deepCopy(items)) {
-            itemMap.put(e.id, e);
-        }
-
-        for (DbEntry item : finalItems) {
-            DbEntry org = itemMap.get(item.id);
-            itemMap.remove(item.id);
-
-            // Check if update is required
-            if (!item.columnsSame(org)) {
-                update(item);
-            }
-        }
-
-        // The remaining items in {@link #itemMap} are those which didn't get placed.
-        for (DbEntry item : itemMap) {
-            mCarryOver.add(item);
-        }
-
-        if (!mCarryOver.isEmpty() && removeWt == 0) {
-            // No new items were removed in this step. Try placing all the items on this screen.
-            GridOccupancy occupied = new GridOccupancy(mTrgX, mTrgY);
-            occupied.markCells(0, 0, mTrgX, startY, true);
-            for (DbEntry item : finalItems) {
-                occupied.markCells(item, true);
-            }
-
-            OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied,
-                    deepCopy(mCarryOver), startY, true);
-            placement.find();
-            if (placement.lowestWeightLoss == 0) {
-                // All items got placed
-
-                for (DbEntry item : placement.finalPlacedItems) {
-                    item.screenId = screenId;
-                    update(item);
-                }
-
-                mCarryOver.clear();
-            }
-        }
-    }
-
-    /**
-     * Updates an item in the DB.
-     */
-    protected void update(DbEntry item) {
-        ContentValues values = new ContentValues();
-        item.addToContentValues(values);
-        mUpdateOperations.put(item.id, values);
-    }
-
-    /**
-     * Tries the remove the provided row and column.
-     *
-     * @param items   all the items on the screen under operation
-     * @param outLoss array of size 2. The first entry is filled with weight loss, and the second
-     *                with the overall item movement.
-     */
-    private ArrayList<DbEntry> tryRemove(int col, int row, int startY,
-            ArrayList<DbEntry> items, float[] outLoss) {
-        GridOccupancy occupied = new GridOccupancy(mTrgX, mTrgY);
-        occupied.markCells(0, 0, mTrgX, startY, true);
-
-        col = mShouldRemoveX ? col : Integer.MAX_VALUE;
-        row = mShouldRemoveY ? row : Integer.MAX_VALUE;
-
-        ArrayList<DbEntry> finalItems = new ArrayList<>();
-        ArrayList<DbEntry> removedItems = new ArrayList<>();
-
-        for (DbEntry item : items) {
-            if ((item.cellX <= col && (item.spanX + item.cellX) > col)
-                    || (item.cellY <= row && (item.spanY + item.cellY) > row)) {
-                removedItems.add(item);
-                if (item.cellX >= col) item.cellX--;
-                if (item.cellY >= row) item.cellY--;
-            } else {
-                if (item.cellX > col) item.cellX--;
-                if (item.cellY > row) item.cellY--;
-                finalItems.add(item);
-                occupied.markCells(item, true);
-            }
-        }
-
-        OptimalPlacementSolution placement =
-                new OptimalPlacementSolution(occupied, removedItems, startY);
-        placement.find();
-        finalItems.addAll(placement.finalPlacedItems);
-        outLoss[0] = placement.lowestWeightLoss;
-        outLoss[1] = placement.lowestMoveCost;
-        return finalItems;
-    }
-
-    private class OptimalPlacementSolution {
-        private final ArrayList<DbEntry> itemsToPlace;
-        private final GridOccupancy occupied;
-
-        // If set to true, item movement are not considered in move cost, leading to a more
-        // linear placement.
-        private final boolean ignoreMove;
-
-        // The first row in the grid from where the placement should start.
-        private final int startY;
-
-        float lowestWeightLoss = Float.MAX_VALUE;
-        float lowestMoveCost = Float.MAX_VALUE;
-        ArrayList<DbEntry> finalPlacedItems;
-
-        public OptimalPlacementSolution(
-                GridOccupancy occupied, ArrayList<DbEntry> itemsToPlace, int startY) {
-            this(occupied, itemsToPlace, startY, false);
-        }
-
-        public OptimalPlacementSolution(GridOccupancy occupied, ArrayList<DbEntry> itemsToPlace,
-                int startY, boolean ignoreMove) {
-            this.occupied = occupied;
-            this.itemsToPlace = itemsToPlace;
-            this.ignoreMove = ignoreMove;
-            this.startY = startY;
-
-            // Sort the items such that larger widgets appear first followed by 1x1 items
-            Collections.sort(this.itemsToPlace);
-        }
-
-        public void find() {
-            find(0, 0, 0, new ArrayList<DbEntry>());
-        }
-
-        /**
-         * Recursively finds a placement for the provided items.
-         *
-         * @param index       the position in {@link #itemsToPlace} to start looking at.
-         * @param weightLoss  total weight loss upto this point
-         * @param moveCost    total move cost upto this point
-         * @param itemsPlaced all the items already placed upto this point
-         */
-        public void find(int index, float weightLoss, float moveCost,
-                ArrayList<DbEntry> itemsPlaced) {
-            if ((weightLoss >= lowestWeightLoss) ||
-                    ((weightLoss == lowestWeightLoss) && (moveCost >= lowestMoveCost))) {
-                // Abort, as we already have a better solution.
-                return;
-
-            } else if (index >= itemsToPlace.size()) {
-                // End loop.
-                lowestWeightLoss = weightLoss;
-                lowestMoveCost = moveCost;
-
-                // Keep a deep copy of current configuration as it can change during recursion.
-                finalPlacedItems = deepCopy(itemsPlaced);
-                return;
-            }
-
-            DbEntry me = itemsToPlace.get(index);
-            int myX = me.cellX;
-            int myY = me.cellY;
-
-            // List of items to pass over if this item was placed.
-            ArrayList<DbEntry> itemsIncludingMe = new ArrayList<>(itemsPlaced.size() + 1);
-            itemsIncludingMe.addAll(itemsPlaced);
-            itemsIncludingMe.add(me);
-
-            if (me.spanX > 1 || me.spanY > 1) {
-                // If the current item is a widget (and it greater than 1x1), try to place it at
-                // all possible positions. This is because a widget placed at one position can
-                // affect the placement of a different widget.
-                int myW = me.spanX;
-                int myH = me.spanY;
-
-                for (int y = startY; y < mTrgY; y++) {
-                    for (int x = 0; x < mTrgX; x++) {
-                        float newMoveCost = moveCost;
-                        if (x != myX) {
-                            me.cellX = x;
-                            newMoveCost++;
-                        }
-                        if (y != myY) {
-                            me.cellY = y;
-                            newMoveCost++;
-                        }
-                        if (ignoreMove) {
-                            newMoveCost = moveCost;
-                        }
-
-                        if (occupied.isRegionVacant(x, y, myW, myH)) {
-                            // place at this position and continue search.
-                            occupied.markCells(me, true);
-                            find(index + 1, weightLoss, newMoveCost, itemsIncludingMe);
-                            occupied.markCells(me, false);
-                        }
-
-                        // Try resizing horizontally
-                        if (myW > me.minSpanX && occupied.isRegionVacant(x, y, myW - 1, myH)) {
-                            me.spanX--;
-                            occupied.markCells(me, true);
-                            // 1 extra move cost
-                            find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
-                            occupied.markCells(me, false);
-                            me.spanX++;
-                        }
-
-                        // Try resizing vertically
-                        if (myH > me.minSpanY && occupied.isRegionVacant(x, y, myW, myH - 1)) {
-                            me.spanY--;
-                            occupied.markCells(me, true);
-                            // 1 extra move cost
-                            find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
-                            occupied.markCells(me, false);
-                            me.spanY++;
-                        }
-
-                        // Try resizing horizontally & vertically
-                        if (myH > me.minSpanY && myW > me.minSpanX &&
-                                occupied.isRegionVacant(x, y, myW - 1, myH - 1)) {
-                            me.spanX--;
-                            me.spanY--;
-                            occupied.markCells(me, true);
-                            // 2 extra move cost
-                            find(index + 1, weightLoss, newMoveCost + 2, itemsIncludingMe);
-                            occupied.markCells(me, false);
-                            me.spanX++;
-                            me.spanY++;
-                        }
-                        me.cellX = myX;
-                        me.cellY = myY;
-                    }
-                }
-
-                // Finally also try a solution when this item is not included. Trying it in the end
-                // causes it to get skipped in most cases due to higher weight loss, and prevents
-                // unnecessary deep copies of various configurations.
-                find(index + 1, weightLoss + me.weight, moveCost, itemsPlaced);
-            } else {
-                // Since this is a 1x1 item and all the following items are also 1x1, just place
-                // it at 'the most appropriate position' and hope for the best.
-                // The most appropriate position: one with lease straight line distance
-                int newDistance = Integer.MAX_VALUE;
-                int newX = Integer.MAX_VALUE, newY = Integer.MAX_VALUE;
-
-                for (int y = startY; y < mTrgY; y++) {
-                    for (int x = 0; x < mTrgX; x++) {
-                        if (!occupied.cells[x][y]) {
-                            int dist = ignoreMove ? 0 :
-                                    ((me.cellX - x) * (me.cellX - x) + (me.cellY - y) * (me.cellY
-                                            - y));
-                            if (dist < newDistance) {
-                                newX = x;
-                                newY = y;
-                                newDistance = dist;
-                            }
-                        }
-                    }
-                }
-
-                if (newX < mTrgX && newY < mTrgY) {
-                    float newMoveCost = moveCost;
-                    if (newX != myX) {
-                        me.cellX = newX;
-                        newMoveCost++;
-                    }
-                    if (newY != myY) {
-                        me.cellY = newY;
-                        newMoveCost++;
-                    }
-                    if (ignoreMove) {
-                        newMoveCost = moveCost;
-                    }
-                    occupied.markCells(me, true);
-                    find(index + 1, weightLoss, newMoveCost, itemsIncludingMe);
-                    occupied.markCells(me, false);
-                    me.cellX = myX;
-                    me.cellY = myY;
-
-                    // Try to find a solution without this item, only if
-                    //  1) there was at least one space, i.e., we were able to place this item
-                    //  2) if the next item has the same weight (all items are already sorted), as
-                    //     if it has lower weight, that solution will automatically get discarded.
-                    //  3) ignoreMove false otherwise, move cost is ignored and the weight will
-                    //      anyway be same.
-                    if (index + 1 < itemsToPlace.size()
-                            && itemsToPlace.get(index + 1).weight >= me.weight && !ignoreMove) {
-                        find(index + 1, weightLoss + me.weight, moveCost, itemsPlaced);
-                    }
-                } else {
-                    // No more space. Jump to the end.
-                    for (int i = index + 1; i < itemsToPlace.size(); i++) {
-                        weightLoss += itemsToPlace.get(i).weight;
-                    }
-                    find(itemsToPlace.size(), weightLoss + me.weight, moveCost, itemsPlaced);
-                }
-            }
-        }
-    }
-
-    private ArrayList<DbEntry> loadHotseatEntries() {
-        Cursor c = queryWorkspace(
-                new String[]{
-                        Favorites._ID,                  // 0
-                        Favorites.ITEM_TYPE,            // 1
-                        Favorites.INTENT,               // 2
-                        Favorites.SCREEN},              // 3
-                Favorites.CONTAINER + " = " + Favorites.CONTAINER_HOTSEAT);
-
-        final int indexId = c.getColumnIndexOrThrow(Favorites._ID);
-        final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE);
-        final int indexIntent = c.getColumnIndexOrThrow(Favorites.INTENT);
-        final int indexScreen = c.getColumnIndexOrThrow(Favorites.SCREEN);
-
-        ArrayList<DbEntry> entries = new ArrayList<>();
-        while (c.moveToNext()) {
-            DbEntry entry = new DbEntry();
-            entry.id = c.getInt(indexId);
-            entry.itemType = c.getInt(indexItemType);
-            entry.screenId = c.getInt(indexScreen);
-
-            if (entry.screenId >= mSrcHotseatSize) {
-                mEntryToRemove.add(entry.id);
-                continue;
-            }
-
-            try {
-                // calculate weight
-                switch (entry.itemType) {
-                    case Favorites.ITEM_TYPE_SHORTCUT:
-                    case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
-                    case Favorites.ITEM_TYPE_APPLICATION: {
-                        verifyIntent(c.getString(indexIntent));
-                        entry.weight = entry.itemType == Favorites.ITEM_TYPE_APPLICATION ?
-                                WT_APPLICATION : WT_SHORTCUT;
-                        break;
-                    }
-                    case Favorites.ITEM_TYPE_FOLDER: {
-                        int total = getFolderItemsCount(entry.id);
-                        if (total == 0) {
-                            throw new Exception("Folder is empty");
-                        }
-                        entry.weight = WT_FOLDER_FACTOR * total;
-                        break;
-                    }
-                    default:
-                        throw new Exception("Invalid item type");
-                }
-            } catch (Exception e) {
-                if (DEBUG) {
-                    Log.d(TAG, "Removing item " + entry.id, e);
-                }
-                mEntryToRemove.add(entry.id);
-                continue;
-            }
-            entries.add(entry);
-        }
-        c.close();
-        return entries;
-    }
-
-
-    /**
-     * Loads entries for a particular screen id.
-     */
-    protected ArrayList<DbEntry> loadWorkspaceEntries(int screen) {
-        Cursor c = queryWorkspace(
-                new String[]{
-                        Favorites._ID,                  // 0
-                        Favorites.ITEM_TYPE,            // 1
-                        Favorites.CELLX,                // 2
-                        Favorites.CELLY,                // 3
-                        Favorites.SPANX,                // 4
-                        Favorites.SPANY,                // 5
-                        Favorites.INTENT,               // 6
-                        Favorites.APPWIDGET_PROVIDER,   // 7
-                        Favorites.APPWIDGET_ID},        // 8
-                Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP
-                        + " AND " + Favorites.SCREEN + " = " + screen);
-
-        final int indexId = c.getColumnIndexOrThrow(Favorites._ID);
-        final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE);
-        final int indexCellX = c.getColumnIndexOrThrow(Favorites.CELLX);
-        final int indexCellY = c.getColumnIndexOrThrow(Favorites.CELLY);
-        final int indexSpanX = c.getColumnIndexOrThrow(Favorites.SPANX);
-        final int indexSpanY = c.getColumnIndexOrThrow(Favorites.SPANY);
-        final int indexIntent = c.getColumnIndexOrThrow(Favorites.INTENT);
-        final int indexAppWidgetProvider = c.getColumnIndexOrThrow(Favorites.APPWIDGET_PROVIDER);
-        final int indexAppWidgetId = c.getColumnIndexOrThrow(Favorites.APPWIDGET_ID);
-
-        ArrayList<DbEntry> entries = new ArrayList<>();
-        WidgetManagerHelper widgetManagerHelper = new WidgetManagerHelper(mContext);
-        while (c.moveToNext()) {
-            DbEntry entry = new DbEntry();
-            entry.id = c.getInt(indexId);
-            entry.itemType = c.getInt(indexItemType);
-            entry.cellX = c.getInt(indexCellX);
-            entry.cellY = c.getInt(indexCellY);
-            entry.spanX = c.getInt(indexSpanX);
-            entry.spanY = c.getInt(indexSpanY);
-            entry.screenId = screen;
-
-            try {
-                // calculate weight
-                switch (entry.itemType) {
-                    case Favorites.ITEM_TYPE_SHORTCUT:
-                    case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
-                    case Favorites.ITEM_TYPE_APPLICATION: {
-                        verifyIntent(c.getString(indexIntent));
-                        entry.weight = entry.itemType == Favorites.ITEM_TYPE_APPLICATION ?
-                                WT_APPLICATION : WT_SHORTCUT;
-                        break;
-                    }
-                    case Favorites.ITEM_TYPE_APPWIDGET: {
-                        String provider = c.getString(indexAppWidgetProvider);
-                        ComponentName cn = ComponentName.unflattenFromString(provider);
-                        verifyPackage(cn.getPackageName());
-                        entry.weight = Math.max(WT_WIDGET_MIN, WT_WIDGET_FACTOR
-                                * entry.spanX * entry.spanY);
-
-                        int widgetId = c.getInt(indexAppWidgetId);
-                        LauncherAppWidgetProviderInfo pInfo =
-                                widgetManagerHelper.getLauncherAppWidgetInfo(widgetId);
-                        Point spans = null;
-                        if (pInfo != null) {
-                            spans = pInfo.getMinSpans();
-                        }
-                        if (spans != null) {
-                            entry.minSpanX = spans.x > 0 ? spans.x : entry.spanX;
-                            entry.minSpanY = spans.y > 0 ? spans.y : entry.spanY;
-                        } else {
-                            // Assume that the widget be resized down to 2x2
-                            entry.minSpanX = entry.minSpanY = 2;
-                        }
-
-                        if (entry.minSpanX > mTrgX || entry.minSpanY > mTrgY) {
-                            throw new Exception("Widget can't be resized down to fit the grid");
-                        }
-                        break;
-                    }
-                    case Favorites.ITEM_TYPE_FOLDER: {
-                        int total = getFolderItemsCount(entry.id);
-                        if (total == 0) {
-                            throw new Exception("Folder is empty");
-                        }
-                        entry.weight = WT_FOLDER_FACTOR * total;
-                        break;
-                    }
-                    default:
-                        throw new Exception("Invalid item type");
-                }
-            } catch (Exception e) {
-                if (DEBUG) {
-                    Log.d(TAG, "Removing item " + entry.id, e);
-                }
-                mEntryToRemove.add(entry.id);
-                continue;
-            }
-            entries.add(entry);
-        }
-        c.close();
-        return entries;
-    }
-
-    /**
-     * @return the number of valid items in the folder.
-     */
-    private int getFolderItemsCount(int folderId) {
-        Cursor c = queryWorkspace(
-                new String[]{Favorites._ID, Favorites.INTENT},
-                Favorites.CONTAINER + " = " + folderId);
-
-        int total = 0;
-        while (c.moveToNext()) {
-            try {
-                verifyIntent(c.getString(1));
-                total++;
-            } catch (Exception e) {
-                mEntryToRemove.add(c.getInt(0));
-            }
-        }
-        c.close();
-        return total;
-    }
-
-    protected Cursor queryWorkspace(String[] columns, String where) {
-        return mDb.query(mTableName, columns, where, null, null, null, null);
-    }
-
-    /**
-     * Verifies if the intent should be restored.
-     */
-    private void verifyIntent(String intentStr) throws Exception {
-        Intent intent = Intent.parseUri(intentStr, 0);
-        if (intent.getComponent() != null) {
-            verifyPackage(intent.getComponent().getPackageName());
-        } else if (intent.getPackage() != null) {
-            // Only verify package if the component was null.
-            verifyPackage(intent.getPackage());
-        }
-    }
-
-    /**
-     * Verifies if the package should be restored
-     */
-    private void verifyPackage(String packageName) throws Exception {
-        if (!mValidPackages.contains(packageName)) {
-            throw new Exception("Package not available");
-        }
-    }
-
-    protected static class DbEntry extends ItemInfo implements Comparable<DbEntry> {
-
-        public float weight;
-
-        public DbEntry() {
-        }
-
-        public DbEntry copy() {
-            DbEntry entry = new DbEntry();
-            entry.copyFrom(this);
-            entry.weight = weight;
-            entry.minSpanX = minSpanX;
-            entry.minSpanY = minSpanY;
-            return entry;
-        }
-
-        /**
-         * Comparator such that larger widgets come first,  followed by all 1x1 items
-         * based on their weights.
-         */
-        @Override
-        public int compareTo(DbEntry another) {
-            if (itemType == Favorites.ITEM_TYPE_APPWIDGET) {
-                if (another.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
-                    return another.spanY * another.spanX - spanX * spanY;
-                } else {
-                    return -1;
-                }
-            } else if (another.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
-                return 1;
-            } else {
-                // Place higher weight before lower weight.
-                return Float.compare(another.weight, weight);
-            }
-        }
-
-        public boolean columnsSame(DbEntry org) {
-            return org.cellX == cellX && org.cellY == cellY && org.spanX == spanX &&
-                    org.spanY == spanY && org.screenId == screenId;
-        }
-
-        public void addToContentValues(ContentValues values) {
-            values.put(Favorites.SCREEN, screenId);
-            values.put(Favorites.CELLX, cellX);
-            values.put(Favorites.CELLY, cellY);
-            values.put(Favorites.SPANX, spanX);
-            values.put(Favorites.SPANY, spanY);
-        }
-    }
-
-    private static ArrayList<DbEntry> deepCopy(ArrayList<DbEntry> src) {
-        ArrayList<DbEntry> dup = new ArrayList<>(src.size());
-        for (DbEntry e : src) {
-            dup.add(e.copy());
-        }
-        return dup;
-    }
-
-    public static void markForMigration(
-            Context context, int gridX, int gridY, int hotseatSize) {
-        Utilities.getPrefs(context).edit()
-                .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, getPointString(gridX, gridY))
-                .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, hotseatSize)
-                .apply();
-    }
-
-    /**
-     * Check given a new IDP, if migration is necessary.
-     */
-    public static boolean needsToMigrate(Context context, InvariantDeviceProfile idp) {
-        SharedPreferences prefs = Utilities.getPrefs(context);
-        String gridSizeString = getPointString(idp.numColumns, idp.numRows);
-
-        return !gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, ""))
-                || idp.numDatabaseHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1);
-    }
-
-    /** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
-    public static boolean migrateGridIfNeeded(Context context) {
-        if (context instanceof LauncherPreviewRenderer.PreviewContext) {
-            return true;
-        }
-        return migrateGridIfNeeded(context, null);
-    }
-
-    /**
-     * Run the migration algorithm if needed. For preview, we provide the intended idp because it
-     * has not been changed. If idp is null, we read it from the context, for actual grid migration.
-     *
-     * @return false if the migration failed.
-     */
-    public static boolean migrateGridIfNeeded(Context context, InvariantDeviceProfile idp) {
-        boolean migrateForPreview = idp != null;
-        if (!migrateForPreview) {
-            idp = LauncherAppState.getIDP(context);
-        }
-
-        if (!needsToMigrate(context, idp)) {
-            return true;
-        }
-
-        SharedPreferences prefs = Utilities.getPrefs(context);
-        String gridSizeString = getPointString(idp.numColumns, idp.numRows);
-        long migrationStartTime = SystemClock.elapsedRealtime();
-        try (SQLiteTransaction transaction = (SQLiteTransaction) Settings.call(
-                context.getContentResolver(), Settings.METHOD_NEW_TRANSACTION)
-                .getBinder(Settings.EXTRA_VALUE)) {
-
-            int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
-                    idp.numDatabaseHotseatIcons);
-            Point sourceSize = parsePoint(prefs.getString(
-                    KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString));
-
-            boolean dbChanged = false;
-            if (migrateForPreview) {
-                copyTable(transaction.getDb(), Favorites.TABLE_NAME, transaction.getDb(),
-                        Favorites.PREVIEW_TABLE_NAME, context);
-            }
-
-            GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb(),
-                    srcHotseatCount, sourceSize.x, sourceSize.y);
-            if (migrateForPreview ? backupTable.restoreToPreviewIfBackupExists()
-                    : backupTable.backupOrRestoreAsNeeded()) {
-                dbChanged = true;
-                srcHotseatCount = backupTable.getRestoreHotseatAndGridSize(sourceSize);
-            }
-
-            HashSet<String> validPackages = getValidPackages(context);
-            // Hotseat.
-            if (srcHotseatCount != idp.numDatabaseHotseatIcons
-                    && new GridSizeMigrationTask(context, transaction.getDb(), validPackages,
-                            migrateForPreview, srcHotseatCount,
-                            idp.numDatabaseHotseatIcons).migrateHotseat()) {
-                dbChanged = true;
-            }
-
-            // Grid size
-            Point targetSize = new Point(idp.numColumns, idp.numRows);
-            if (new MultiStepMigrationTask(validPackages, context, transaction.getDb(),
-                    migrateForPreview).migrate(sourceSize, targetSize)) {
-                dbChanged = true;
-            }
-
-            if (dbChanged) {
-                // Make sure we haven't removed everything.
-                final Cursor c = context.getContentResolver().query(
-                        migrateForPreview ? Favorites.PREVIEW_CONTENT_URI : Favorites.CONTENT_URI,
-                        null, null, null, null);
-                boolean hasData = c.moveToNext();
-                c.close();
-                if (!hasData) {
-                    throw new Exception("Removed every thing during grid resize");
-                }
-            }
-
-            transaction.commit();
-            if (!migrateForPreview) {
-                Settings.call(context.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
-            }
-            return true;
-        } catch (Exception e) {
-            Log.e(TAG, "Error during preview grid migration", e);
-
-            return false;
-        } finally {
-            Log.v(TAG, "Preview workspace migration completed in "
-                    + (SystemClock.elapsedRealtime() - migrationStartTime));
-
-            if (!migrateForPreview) {
-                // Save current configuration, so that the migration does not run again.
-                prefs.edit()
-                        .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
-                        .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numDatabaseHotseatIcons)
-                        .apply();
-            }
-        }
-    }
-
-    protected static HashSet<String> getValidPackages(Context context) {
-        // Initialize list of valid packages. This contain all the packages which are already on
-        // the device and packages which are being installed. Any item which doesn't belong to
-        // this set is removed.
-        // Since the loader removes such items anyway, removing these items here doesn't cause
-        // any extra data loss and gives us more free space on the grid for better migration.
-        HashSet<String> validPackages = new HashSet<>();
-        for (PackageInfo info : context.getPackageManager()
-                .getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) {
-            validPackages.add(info.packageName);
-        }
-        InstallSessionHelper.INSTANCE.get(context)
-                .getActiveSessions().keySet()
-                .forEach(packageUserKey -> validPackages.add(packageUserKey.mPackageName));
-        return validPackages;
-    }
-
-    /**
-     * Removes any broken item from the hotseat.
-     *
-     * @return a map with occupied hotseat position set to non-null value.
-     */
-    public static IntSparseArrayMap<Object> removeBrokenHotseatItems(Context context)
-            throws Exception {
-        try (SQLiteTransaction transaction = (SQLiteTransaction) Settings.call(
-                context.getContentResolver(), Settings.METHOD_NEW_TRANSACTION)
-                .getBinder(Settings.EXTRA_VALUE)) {
-            GridSizeMigrationTask task = new GridSizeMigrationTask(
-                    context, transaction.getDb(), getValidPackages(context),
-                    false /* usePreviewTable */, Integer.MAX_VALUE, Integer.MAX_VALUE);
-
-            // Load all the valid entries
-            ArrayList<DbEntry> items = task.loadHotseatEntries();
-            // Delete any entry marked for deletion by above load.
-            task.applyOperations();
-            IntSparseArrayMap<Object> positions = new IntSparseArrayMap<>();
-            for (DbEntry item : items) {
-                positions.put(item.screenId, item);
-            }
-            transaction.commit();
-            return positions;
-        }
-    }
-
-    /**
-     * Task to run grid migration in multiple steps when the size difference is more than 1.
-     */
-    protected static class MultiStepMigrationTask {
-        private final HashSet<String> mValidPackages;
-        private final Context mContext;
-        private final SQLiteDatabase mDb;
-        private final boolean mUsePreviewTable;
-
-        public MultiStepMigrationTask(HashSet<String> validPackages, Context context,
-                SQLiteDatabase db, boolean usePreviewTable) {
-            mValidPackages = validPackages;
-            mContext = context;
-            mDb = db;
-            mUsePreviewTable = usePreviewTable;
-        }
-
-        public boolean migrate(Point sourceSize, Point targetSize) throws Exception {
-            boolean dbChanged = false;
-            if (!targetSize.equals(sourceSize)) {
-                if (sourceSize.x < targetSize.x) {
-                    // Source is smaller that target, just expand the grid without actual migration.
-                    sourceSize.x = targetSize.x;
-                }
-                if (sourceSize.y < targetSize.y) {
-                    // Source is smaller that target, just expand the grid without actual migration.
-                    sourceSize.y = targetSize.y;
-                }
-
-                // Migrate the workspace grid, such that the points differ by max 1 in x and y
-                // each on every step.
-                while (!targetSize.equals(sourceSize)) {
-                    // Get the next size, such that the points differ by max 1 in x and y each
-                    Point nextSize = new Point(sourceSize);
-                    if (targetSize.x < nextSize.x) {
-                        nextSize.x--;
-                    }
-                    if (targetSize.y < nextSize.y) {
-                        nextSize.y--;
-                    }
-                    if (runStepTask(sourceSize, nextSize)) {
-                        dbChanged = true;
-                    }
-                    sourceSize.set(nextSize.x, nextSize.y);
-                }
-            }
-            return dbChanged;
-        }
-
-        protected boolean runStepTask(Point sourceSize, Point nextSize) throws Exception {
-            return new GridSizeMigrationTask(mContext, mDb, mValidPackages, mUsePreviewTable,
-                    sourceSize, nextSize).migrateWorkspace();
-        }
-    }
-}
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index 8a1d73e..ca680b7 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -16,9 +16,6 @@
 
 package com.android.launcher3.model;
 
-import static com.android.launcher3.InvariantDeviceProfile.KEY_MIGRATION_SRC_HOTSEAT_COUNT;
-import static com.android.launcher3.InvariantDeviceProfile.KEY_MIGRATION_SRC_WORKSPACE_SIZE;
-import static com.android.launcher3.Utilities.getPointString;
 import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
 
 import android.content.ComponentName;
@@ -106,11 +103,15 @@
      * Check given a new IDP, if migration is necessary.
      */
     public static boolean needsToMigrate(Context context, InvariantDeviceProfile idp) {
-        SharedPreferences prefs = Utilities.getPrefs(context);
-        String gridSizeString = getPointString(idp.numColumns, idp.numRows);
-
-        return !gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, ""))
-                || idp.numDatabaseHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1);
+        DeviceGridState idpGridState = new DeviceGridState(idp);
+        DeviceGridState contextGridState = new DeviceGridState(context);
+        boolean needsToMigrate = !idpGridState.isCompatible(contextGridState);
+        // TODO(b/198965093): Revert this change after bug is fixed
+        if (needsToMigrate) {
+            Log.d("b/198965093", "Migration is needed. idpGridState: " + idpGridState
+                    + ", contextGridState: " + contextGridState);
+        }
+        return needsToMigrate;
     }
 
     /** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
@@ -123,15 +124,15 @@
 
     /**
      * When migrating the grid for preview, we copy the table
-     * {@link LauncherSettings.Favorites.TABLE_NAME} into
-     * {@link LauncherSettings.Favorites.PREVIEW_TABLE_NAME}, run grid size migration from the
+     * {@link LauncherSettings.Favorites#TABLE_NAME} into
+     * {@link LauncherSettings.Favorites#PREVIEW_TABLE_NAME}, run grid size migration from the
      * former to the later, then use the later table for preview.
      *
      * Similarly when doing the actual grid migration, the former grid option's table
-     * {@link LauncherSettings.Favorites.TABLE_NAME} is copied into the new grid option's
-     * {@link LauncherSettings.Favorites.TMP_TABLE}, we then run the grid size migration algorithm
+     * {@link LauncherSettings.Favorites#TABLE_NAME} is copied into the new grid option's
+     * {@link LauncherSettings.Favorites#TMP_TABLE}, we then run the grid size migration algorithm
      * to migrate the later to the former, and load the workspace from the default
-     * {@link LauncherSettings.Favorites.TABLE_NAME}.
+     * {@link LauncherSettings.Favorites#TABLE_NAME}.
      *
      * @return false if the migration failed.
      */
@@ -146,10 +147,7 @@
         }
 
         SharedPreferences prefs = Utilities.getPrefs(context);
-        String gridSizeString = getPointString(idp.numColumns, idp.numRows);
         HashSet<String> validPackages = getValidPackages(context);
-        int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
-                idp.numDatabaseHotseatIcons);
 
         if (migrateForPreview) {
             if (!LauncherSettings.Settings.call(
@@ -174,16 +172,16 @@
             DbReader srcReader = new DbReader(t.getDb(),
                     migrateForPreview ? LauncherSettings.Favorites.TABLE_NAME
                             : LauncherSettings.Favorites.TMP_TABLE,
-                    context, validPackages, srcHotseatCount);
+                    context, validPackages);
             DbReader destReader = new DbReader(t.getDb(),
                     migrateForPreview ? LauncherSettings.Favorites.PREVIEW_TABLE_NAME
                             : LauncherSettings.Favorites.TABLE_NAME,
-                    context, validPackages, idp.numDatabaseHotseatIcons);
+                    context, validPackages);
 
             Point targetSize = new Point(idp.numColumns, idp.numRows);
             GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(context, t.getDb(),
                     srcReader, destReader, idp.numDatabaseHotseatIcons, targetSize);
-            task.migrate();
+            task.migrate(idp);
 
             if (!migrateForPreview) {
                 dropTable(t.getDb(), LauncherSettings.Favorites.TMP_TABLE);
@@ -201,16 +199,13 @@
 
             if (!migrateForPreview) {
                 // Save current configuration, so that the migration does not run again.
-                prefs.edit()
-                        .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
-                        .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numDatabaseHotseatIcons)
-                        .apply();
+                new DeviceGridState(idp).writeToPrefs(context);
             }
         }
     }
 
     @VisibleForTesting
-    protected boolean migrate() {
+    protected boolean migrate(InvariantDeviceProfile idp) {
         if (mHotseatDiff.isEmpty() && mWorkspaceDiff.isEmpty()) {
             return false;
         }
@@ -224,7 +219,14 @@
         Collections.sort(mWorkspaceDiff);
 
         // Migrate workspace.
+        // First we create a collection of the screens
+        List<Integer> screens = new ArrayList<>();
         for (int screenId = 0; screenId <= mDestReader.mLastScreenId; screenId++) {
+            screens.add(screenId);
+        }
+
+        // Then we place the items on the screens
+        for (int screenId : screens) {
             if (DEBUG) {
                 Log.d(TAG, "Migrating " + screenId);
             }
@@ -236,6 +238,8 @@
             }
         }
 
+        // In case the new grid is smaller, there might be some leftover items that don't fit on
+        // any of the screens, in this case we add them to new screens until all of them are placed.
         int screenId = mDestReader.mLastScreenId + 1;
         while (!mWorkspaceDiff.isEmpty()) {
             GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
@@ -491,7 +495,6 @@
         private final String mTableName;
         private final Context mContext;
         private final HashSet<String> mValidPackages;
-        private final int mHotseatSize;
         private int mLastScreenId = -1;
 
         private final ArrayList<DbEntry> mHotseatEntries = new ArrayList<>();
@@ -500,12 +503,11 @@
                 new ArrayMap<>();
 
         DbReader(SQLiteDatabase db, String tableName, Context context,
-                HashSet<String> validPackages, int hotseatSize) {
+                HashSet<String> validPackages) {
             mDb = db;
             mTableName = tableName;
             mContext = context;
             mValidPackages = validPackages;
-            mHotseatSize = hotseatSize;
         }
 
         protected ArrayList<DbEntry> loadHotseatEntries() {
@@ -530,11 +532,6 @@
                 entry.itemType = c.getInt(indexItemType);
                 entry.screenId = c.getInt(indexScreen);
 
-                if (entry.screenId >= mHotseatSize) {
-                    entriesToRemove.add(entry.id);
-                    continue;
-                }
-
                 try {
                     // calculate weight
                     switch (entry.itemType) {
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 7e3bcee..8a5a9bf 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -16,13 +16,10 @@
 
 package com.android.launcher3.model;
 
-import static android.graphics.BitmapFactory.decodeByteArray;
-
 import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
-import android.content.Intent.ShortcutIconResource;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
@@ -45,11 +42,10 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.IconRequestInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
@@ -184,32 +180,21 @@
      * Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
      */
     protected boolean loadIcon(WorkspaceItemInfo info) {
-        try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
-            if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
-                String packageName = getString(iconPackageIndex);
-                String resourceName = getString(iconResourceIndex);
-                if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) {
-                    info.iconResource = new ShortcutIconResource();
-                    info.iconResource.packageName = packageName;
-                    info.iconResource.resourceName = resourceName;
-                    BitmapInfo iconInfo = li.createIconBitmap(info.iconResource);
-                    if (iconInfo != null) {
-                        info.bitmap = iconInfo;
-                        return true;
-                    }
-                }
-            }
+        return createIconRequestInfo(info, false).loadWorkspaceIcon(mContext);
+    }
 
-            // Failed to load from resource, try loading from DB.
-            byte[] data = getBlob(iconIndex);
-            try {
-                info.bitmap = li.createIconBitmap(decodeByteArray(data, 0, data.length));
-                return true;
-            } catch (Exception e) {
-                Log.e(TAG, "Failed to decode byte array for info " + info, e);
-                return false;
-            }
-        }
+    public IconRequestInfo<WorkspaceItemInfo> createIconRequestInfo(
+            WorkspaceItemInfo wai, boolean useLowResIcon) {
+        String packageName = itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
+                ? getString(iconPackageIndex) : null;
+        String resourceName = itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
+                ? getString(iconResourceIndex) : null;
+        byte[] iconBlob = itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
+                || restoreFlag != 0
+                ? getBlob(iconIndex) : null;
+
+        return new IconRequestInfo<>(
+                wai, mActivityInfo, packageName, resourceName, iconBlob, useLowResIcon);
     }
 
     /**
@@ -262,6 +247,11 @@
      */
     public WorkspaceItemInfo getAppShortcutInfo(
             Intent intent, boolean allowMissingTarget, boolean useLowResIcon) {
+        return getAppShortcutInfo(intent, allowMissingTarget, useLowResIcon, true);
+    }
+
+    public WorkspaceItemInfo getAppShortcutInfo(
+            Intent intent, boolean allowMissingTarget, boolean useLowResIcon, boolean loadIcon) {
         if (user == null) {
             Log.d(TAG, "Null user found in getShortcutInfo");
             return null;
@@ -288,9 +278,11 @@
         info.user = user;
         info.intent = newIntent;
 
-        mIconCache.getTitleAndIcon(info, mActivityInfo, useLowResIcon);
-        if (mIconCache.isDefaultIcon(info.bitmap, user)) {
-            loadIcon(info);
+        if (loadIcon) {
+            mIconCache.getTitleAndIcon(info, mActivityInfo, useLowResIcon);
+            if (mIconCache.isDefaultIcon(info.bitmap, user)) {
+                loadIcon(info);
+            }
         }
 
         if (mActivityInfo != null) {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index f6b0b4d..a4f6f7a 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3.model;
 
-import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
@@ -44,6 +43,7 @@
 import android.graphics.Point;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.text.TextUtils;
@@ -72,6 +72,7 @@
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.IconRequestInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -79,13 +80,14 @@
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.provider.ImportDataTask;
 import com.android.launcher3.qsb.QsbContainerView;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.shortcuts.ShortcutRequest;
 import com.android.launcher3.shortcuts.ShortcutRequest.QueryResult;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.IOUtils;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.LooperIdleLock;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
@@ -174,10 +176,13 @@
     private void sendFirstScreenActiveInstallsBroadcast() {
         ArrayList<ItemInfo> firstScreenItems = new ArrayList<>();
         ArrayList<ItemInfo> allItems = mBgDataModel.getAllWorkspaceItems();
-        // Screen set is never empty
-        final int firstScreen = mBgDataModel.collectWorkspaceScreens().get(0);
 
-        filterCurrentWorkspaceItems(firstScreen, allItems, firstScreenItems,
+        // Screen set is never empty
+        IntArray allScreens = mBgDataModel.collectWorkspaceScreens();
+        final int firstScreen = allScreens.get(0);
+        IntSet firstScreens = IntSet.wrap(firstScreen);
+
+        filterCurrentWorkspaceItems(firstScreens, allItems, firstScreenItems,
                 new ArrayList<>() /* otherScreenItems are ignored */);
         mFirstScreenBroadcast.sendBroadcasts(mApp.getContext(), firstScreenItems);
     }
@@ -194,7 +199,12 @@
         TimingLogger logger = new TimingLogger(TAG, "run");
         try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
             List<ShortcutInfo> allShortcuts = new ArrayList<>();
-            loadWorkspace(allShortcuts);
+            Trace.beginSection("LoadWorkspace");
+            try {
+                loadWorkspace(allShortcuts);
+            } finally {
+                Trace.endSection();
+            }
             logASplit(logger, "loadWorkspace");
 
             // Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db.
@@ -208,7 +218,7 @@
             }
 
             verifyNotStopped();
-            mResults.bindWorkspace();
+            mResults.bindWorkspace(true /* incrementBindId */);
             logASplit(logger, "bindWorkspace");
 
             mModelDelegate.workspaceLoadComplete();
@@ -222,7 +232,13 @@
             verifyNotStopped();
 
             // second step
-            List<LauncherActivityInfo> allActivityList = loadAllApps();
+            Trace.beginSection("LoadAllApps");
+            List<LauncherActivityInfo> allActivityList;
+            try {
+               allActivityList = loadAllApps();
+            } finally {
+                Trace.endSection();
+            }
             logASplit(logger, "loadAllApps");
 
             verifyNotStopped();
@@ -324,16 +340,7 @@
         final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context);
 
         boolean clearDb = false;
-        try {
-            ImportDataTask.performImportIfPossible(context);
-        } catch (Exception e) {
-            // Migration failed. Clear workspace.
-            clearDb = true;
-        }
-
-        if (!clearDb && (MULTI_DB_GRID_MIRATION_ALGO.get()
-                ? !GridSizeMigrationTaskV2.migrateGridIfNeeded(context)
-                : !GridSizeMigrationTask.migrateGridIfNeeded(context))) {
+        if (!GridSizeMigrationTaskV2.migrateGridIfNeeded(context)) {
             // Migration failed. Clear workspace.
             clearDb = true;
         }
@@ -414,6 +421,7 @@
                 LauncherAppWidgetProviderInfo widgetProviderInfo;
                 Intent intent;
                 String targetPkg;
+                List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos = new ArrayList<>();
 
                 while (!mStopped && c.moveToNext()) {
                     try {
@@ -536,7 +544,10 @@
                             } else if (c.itemType ==
                                     LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
                                 info = c.getAppShortcutInfo(
-                                        intent, allowMissingTarget, useLowResIcon);
+                                        intent,
+                                        allowMissingTarget,
+                                        useLowResIcon,
+                                        !FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get());
                             } else if (c.itemType ==
                                     LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
 
@@ -573,6 +584,7 @@
                                         && pmHelper.isAppSuspended(targetPkg, c.user)) {
                                     disabledState |= FLAG_DISABLED_SUSPENDED;
                                 }
+                                info.options = c.getInt(optionsIndex);
 
                                 // App shortcuts that used to be automatically added to Launcher
                                 // didn't always have the correct intent flags set, so do that
@@ -588,6 +600,8 @@
                             }
 
                             if (info != null) {
+                                iconRequestInfos.add(c.createIconRequestInfo(info, useLowResIcon));
+
                                 c.applyCommonProperties(info);
 
                                 info.intent = intent;
@@ -791,8 +805,9 @@
                                 if (appWidgetInfo.restoreStatus !=
                                         LauncherAppWidgetInfo.RESTORE_COMPLETED) {
                                     appWidgetInfo.pendingItemInfo = WidgetsModel.newPendingItemInfo(
-                                            appWidgetInfo.providerName);
-                                    appWidgetInfo.pendingItemInfo.user = appWidgetInfo.user;
+                                            mApp.getContext(),
+                                            appWidgetInfo.providerName,
+                                            appWidgetInfo.user);
                                     mIconCache.getTitleAndIconForApp(
                                             appWidgetInfo.pendingItemInfo, false);
                                 }
@@ -805,6 +820,21 @@
                         Log.e(TAG, "Desktop items loading interrupted", e);
                     }
                 }
+                if (FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get()) {
+                    Trace.beginSection("LoadWorkspaceIconsInBulk");
+                    try {
+                        mIconCache.getTitlesAndIconsInBulk(iconRequestInfos);
+                        for (IconRequestInfo<WorkspaceItemInfo> iconRequestInfo :
+                                iconRequestInfos) {
+                            WorkspaceItemInfo wai = iconRequestInfo.itemInfo;
+                            if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) {
+                                iconRequestInfo.loadWorkspaceIcon(mApp.getContext());
+                            }
+                        }
+                    } finally {
+                        Trace.endSection();
+                    }
+                }
             } finally {
                 IOUtils.closeSilently(c);
             }
@@ -908,6 +938,8 @@
         List<LauncherActivityInfo> allActivityList = new ArrayList<>();
         // Clear the list of apps
         mBgAllAppsList.clear();
+
+        List<IconRequestInfo<AppInfo>> iconRequestInfos = new ArrayList<>();
         for (UserHandle user : profiles) {
             // Query for the set of apps
             final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
@@ -920,18 +952,43 @@
             // Create the ApplicationInfos
             for (int i = 0; i < apps.size(); i++) {
                 LauncherActivityInfo app = apps.get(i);
-                // This builds the icon bitmaps.
-                mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);
+                AppInfo appInfo = new AppInfo(app, user, quietMode);
+
+                iconRequestInfos.add(new IconRequestInfo<>(
+                        appInfo, app, /* useLowResIcon= */ false));
+                mBgAllAppsList.add(
+                        appInfo, app, !FeatureFlags.ENABLE_BULK_ALL_APPS_ICON_LOADING.get());
             }
             allActivityList.addAll(apps);
         }
 
+
         if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
             // get all active sessions and add them to the all apps list
             for (PackageInstaller.SessionInfo info :
                     mSessionHelper.getAllVerifiedSessions()) {
-                mBgAllAppsList.addPromiseApp(mApp.getContext(),
-                        PackageInstallInfo.fromInstallingState(info));
+                AppInfo promiseAppInfo = mBgAllAppsList.addPromiseApp(
+                        mApp.getContext(),
+                        PackageInstallInfo.fromInstallingState(info),
+                        !FeatureFlags.ENABLE_BULK_ALL_APPS_ICON_LOADING.get());
+
+                if (promiseAppInfo != null) {
+                    iconRequestInfos.add(new IconRequestInfo<>(
+                            promiseAppInfo,
+                            /* launcherActivityInfo= */ null,
+                            promiseAppInfo.usingLowResIcon()));
+                }
+            }
+        }
+
+        if (FeatureFlags.ENABLE_BULK_ALL_APPS_ICON_LOADING.get()) {
+            Trace.beginSection("LoadAllAppsIconsInBulk");
+            try {
+                mIconCache.getTitlesAndIconsInBulk(iconRequestInfos);
+                iconRequestInfos.forEach(iconRequestInfo ->
+                        mBgAllAppsList.updateSectionName(iconRequestInfo.itemInfo));
+            } finally {
+                Trace.endSection();
             }
         }
 
@@ -994,7 +1051,10 @@
             deviceProfile.getCellSize(cellSize);
             FileLog.d(TAG, "DeviceProfile available width: " + deviceProfile.availableWidthPx
                     + ", available height: " + deviceProfile.availableHeightPx
-                    + ", cellLayoutBorderSpacingPx: " + deviceProfile.cellLayoutBorderSpacingPx
+                    + ", cellLayoutBorderSpacePx Horizontal: "
+                    + deviceProfile.cellLayoutBorderSpacePx.x
+                    + ", cellLayoutBorderSpacePx Vertical: "
+                    + deviceProfile.cellLayoutBorderSpacePx.y
                     + ", cellSize: " + cellSize);
         }
 
diff --git a/src/com/android/launcher3/model/ModelDelegate.java b/src/com/android/launcher3/model/ModelDelegate.java
index 13ec1ec..765141a 100644
--- a/src/com/android/launcher3/model/ModelDelegate.java
+++ b/src/com/android/launcher3/model/ModelDelegate.java
@@ -40,19 +40,21 @@
      * Creates and initializes a new instance of the delegate
      */
     public static ModelDelegate newInstance(
-            Context context, LauncherAppState app, AllAppsList appsList, BgDataModel dataModel) {
+            Context context, LauncherAppState app, AllAppsList appsList, BgDataModel dataModel,
+            boolean isPrimaryInstance) {
         ModelDelegate delegate = Overrides.getObject(
                 ModelDelegate.class, context, R.string.model_delegate_class);
-
         delegate.mApp = app;
         delegate.mAppsList = appsList;
         delegate.mDataModel = dataModel;
+        delegate.mIsPrimaryInstance = isPrimaryInstance;
         return delegate;
     }
 
     protected LauncherAppState mApp;
     protected AllAppsList mAppsList;
     protected BgDataModel mDataModel;
+    protected boolean mIsPrimaryInstance;
 
     public ModelDelegate() { }
 
diff --git a/src/com/android/launcher3/model/ModelUtils.java b/src/com/android/launcher3/model/ModelUtils.java
index 9b5fac8..ef5eef1 100644
--- a/src/com/android/launcher3/model/ModelUtils.java
+++ b/src/com/android/launcher3/model/ModelUtils.java
@@ -31,6 +31,7 @@
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
 
@@ -51,7 +52,8 @@
      * Filters the set of items who are directly or indirectly (via another container) on the
      * specified screen.
      */
-    public static <T extends ItemInfo> void filterCurrentWorkspaceItems(int currentScreenId,
+    public static <T extends ItemInfo> void filterCurrentWorkspaceItems(
+            IntSet currentScreenIds,
             ArrayList<T> allWorkspaceItems,
             ArrayList<T> currentScreenItems,
             ArrayList<T> otherScreenItems) {
@@ -65,7 +67,11 @@
                 (lhs, rhs) -> Integer.compare(lhs.container, rhs.container));
         for (T info : allWorkspaceItems) {
             if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                if (info.screenId == currentScreenId) {
+                if (TestProtocol.sDebugTracing) {
+                    Log.d(TestProtocol.NULL_INT_SET, "filterCurrentWorkspaceItems: "
+                            + currentScreenIds);
+                }
+                if (currentScreenIds.contains(info.screenId)) {
                     currentScreenItems.add(info);
                     itemsOnScreen.add(info.id);
                 } else {
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 080ce20..0439e75 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -23,12 +23,13 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherModel.CallbackTask;
 import com.android.launcher3.LauncherProvider;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
@@ -36,19 +37,22 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.LooperExecutor;
 import com.android.launcher3.widget.LauncherAppWidgetHost;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.Executor;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import java.util.stream.StreamSupport;
@@ -63,7 +67,10 @@
     private final Context mContext;
     private final LauncherModel mModel;
     private final BgDataModel mBgDataModel;
-    private final Handler mUiHandler;
+    private final LooperExecutor mUiExecutor;
+
+    @Nullable
+    private final Callbacks mOwner;
 
     private final boolean mHasVerticalHotseat;
     private final boolean mVerifyChanges;
@@ -73,13 +80,15 @@
     private boolean mPreparingToUndo;
 
     public ModelWriter(Context context, LauncherModel model, BgDataModel dataModel,
-            boolean hasVerticalHotseat, boolean verifyChanges) {
+            boolean hasVerticalHotseat, boolean verifyChanges,
+            @Nullable Callbacks owner) {
         mContext = context;
         mModel = model;
         mBgDataModel = dataModel;
         mHasVerticalHotseat = hasVerticalHotseat;
         mVerifyChanges = verifyChanges;
-        mUiHandler = new Handler(Looper.getMainLooper());
+        mOwner = owner;
+        mUiExecutor = Executors.MAIN_EXECUTOR;
     }
 
     private void updateItemInfoProps(
@@ -155,6 +164,8 @@
     public void moveItemInDatabase(final ItemInfo item,
             int container, int screenId, int cellX, int cellY) {
         updateItemInfoProps(item, container, screenId, cellX, cellY);
+        notifyItemModified(item);
+
         enqueueDeleteRunnable(new UpdateItemRunnable(item, () ->
                 new ContentWriter(mContext)
                         .put(Favorites.CONTAINER, item.container)
@@ -171,6 +182,7 @@
     public void moveItemsInDatabase(final ArrayList<ItemInfo> items, int container, int screen) {
         ArrayList<ContentValues> contentValues = new ArrayList<>();
         int count = items.size();
+        notifyOtherCallbacks(c -> c.bindItemsModified(items));
 
         for (int i = 0; i < count; i++) {
             ItemInfo item = items.get(i);
@@ -196,8 +208,9 @@
         updateItemInfoProps(item, container, screenId, cellX, cellY);
         item.spanX = spanX;
         item.spanY = spanY;
+        notifyItemModified(item);
 
-        ((Executor) MODEL_EXECUTOR).execute(new UpdateItemRunnable(item, () ->
+        MODEL_EXECUTOR.execute(new UpdateItemRunnable(item, () ->
                 new ContentWriter(mContext)
                         .put(Favorites.CONTAINER, item.container)
                         .put(Favorites.CELLX, item.cellX)
@@ -212,13 +225,18 @@
      * Update an item to the database in a specified container.
      */
     public void updateItemInDatabase(ItemInfo item) {
-        ((Executor) MODEL_EXECUTOR).execute(new UpdateItemRunnable(item, () -> {
+        notifyItemModified(item);
+        MODEL_EXECUTOR.execute(new UpdateItemRunnable(item, () -> {
             ContentWriter writer = new ContentWriter(mContext);
             item.onAddToDatabase(writer);
             return writer;
         }));
     }
 
+    private void notifyItemModified(ItemInfo item) {
+        notifyOtherCallbacks(c -> c.bindItemsModified(Collections.singletonList(item)));
+    }
+
     /**
      * Add an item to the database in a specified container. Sets the container, screen, cellX and
      * cellY fields of the item. Also assigns an ID to the item.
@@ -229,10 +247,11 @@
 
         final ContentResolver cr = mContext.getContentResolver();
         item.id = Settings.call(cr, Settings.METHOD_NEW_ITEM_ID).getInt(Settings.EXTRA_VALUE);
+        notifyOtherCallbacks(c -> c.bindItems(Collections.singletonList(item), false));
 
         ModelVerifier verifier = new ModelVerifier();
         final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
-        ((Executor) MODEL_EXECUTOR).execute(() -> {
+        MODEL_EXECUTOR.execute(() -> {
             // Write the item on background thread, as some properties might have been updated in
             // the background.
             final ContentWriter writer = new ContentWriter(mContext);
@@ -274,6 +293,7 @@
                 (item) -> item.getTargetComponent() == null ? ""
                         : item.getTargetComponent().getPackageName()).collect(
                 Collectors.joining(",")), new Exception());
+        notifyDelete(items);
         enqueueDeleteRunnable(() -> {
             for (ItemInfo item : items) {
                 final Uri uri = Favorites.getContentUri(item.id);
@@ -290,6 +310,7 @@
      */
     public void deleteFolderAndContentsFromDatabase(final FolderInfo info) {
         ModelVerifier verifier = new ModelVerifier();
+        notifyDelete(Collections.singleton(info));
 
         enqueueDeleteRunnable(() -> {
             ContentResolver cr = mContext.getContentResolver();
@@ -308,6 +329,7 @@
      * Deletes the widget info and the widget id.
      */
     public void deleteWidgetInfo(final LauncherAppWidgetInfo info, LauncherAppWidgetHost host) {
+        notifyDelete(Collections.singleton(info));
         if (host != null && !info.isCustomWidget() && info.isWidgetIdAllocated()) {
             // Deleting an app widget ID is a void call but writes to disk before returning
             // to the caller...
@@ -316,6 +338,10 @@
         deleteItemFromDatabase(info);
     }
 
+    private void notifyDelete(Collection<? extends ItemInfo> items) {
+        notifyOtherCallbacks(c -> c.bindWorkspaceComponentsRemoved(ItemInfoMatcher.ofItems(items)));
+    }
+
     /**
      * Delete operations tracked using {@link #enqueueDeleteRunnable} will only be called
      * if {@link #commitDelete} is called. Note that one of {@link #commitDelete()} or
@@ -341,14 +367,14 @@
         if (mPreparingToUndo) {
             mDeleteRunnables.add(r);
         } else {
-            ((Executor) MODEL_EXECUTOR).execute(r);
+            MODEL_EXECUTOR.execute(r);
         }
     }
 
     public void commitDelete() {
         mPreparingToUndo = false;
         for (Runnable runnable : mDeleteRunnables) {
-            ((Executor) MODEL_EXECUTOR).execute(runnable);
+            MODEL_EXECUTOR.execute(runnable);
         }
         mDeleteRunnables.clear();
     }
@@ -364,6 +390,20 @@
         mModel.forceReload();
     }
 
+    private void notifyOtherCallbacks(CallbackTask task) {
+        if (mOwner == null) {
+            // If the call is happening from a model, it will take care of updating the callbacks
+            return;
+        }
+        mUiExecutor.execute(() -> {
+            for (Callbacks c : mModel.getCallbacks()) {
+                if (c != mOwner) {
+                    task.execute(c);
+                }
+            }
+        });
+    }
+
     private class UpdateItemRunnable extends UpdateItemBaseRunnable {
         private final ItemInfo mItem;
         private final Supplier<ContentWriter> mWriter;
@@ -484,7 +524,7 @@
 
             int executeId = mBgDataModel.lastBindId;
 
-            mUiHandler.post(() -> {
+            mUiExecutor.post(() -> {
                 int currentId = mBgDataModel.lastBindId;
                 if (currentId > executeId) {
                     // Model was already bound after job was executed.
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 82b0f7c..83fb3d1 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -123,7 +123,6 @@
                         iconCache.updateIconsForPkg(packages[i], mUser);
                         activitiesLists.put(
                                 packages[i], appsList.updatePackage(context, packages[i], mUser));
-                        app.getWidgetCache().removePackage(packages[i], mUser);
 
                         // The update may have changed which shortcuts/widgets are available.
                         // Refresh the widgets for the package if we have an activity running.
@@ -148,7 +147,6 @@
                 for (int i = 0; i < N; i++) {
                     if (DEBUG) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
                     appsList.removePackage(packages[i], mUser);
-                    app.getWidgetCache().removePackage(packages[i], mUser);
                 }
                 flagOp = FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
                 break;
diff --git a/src/com/android/launcher3/model/UserManagerState.java b/src/com/android/launcher3/model/UserManagerState.java
index 3a4206c..97a5905 100644
--- a/src/com/android/launcher3/model/UserManagerState.java
+++ b/src/com/android/launcher3/model/UserManagerState.java
@@ -36,7 +36,7 @@
      * Initialises the state values for all users
      */
     public void init(UserCache userCache, UserManager userManager) {
-        for (UserHandle user : userCache.getUserProfiles()) {
+        for (UserHandle user : userManager.getUserProfiles()) {
             long serialNo = userCache.getSerialNumberForUser(user);
             boolean isUserQuiet = userManager.isQuietModeEnabled(user);
             allUsers.put(serialNo, user);
diff --git a/src/com/android/launcher3/model/data/IconRequestInfo.java b/src/com/android/launcher3/model/data/IconRequestInfo.java
new file mode 100644
index 0000000..5dc6a3b
--- /dev/null
+++ b/src/com/android/launcher3/model/data/IconRequestInfo.java
@@ -0,0 +1,114 @@
+/*
+ * 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.model.data;
+
+import static android.graphics.BitmapFactory.decodeByteArray;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.LauncherIcons;
+
+/**
+ * Class representing one request for an icon to be queried in a sql database.
+ *
+ * @param <T> ItemInfoWithIcon subclass whose title and icon can be loaded and filled by an sql
+ *           query.
+ */
+public class IconRequestInfo<T extends ItemInfoWithIcon> {
+
+    private static final String TAG = "IconRequestInfo";
+
+    @NonNull public final T itemInfo;
+    @Nullable public final LauncherActivityInfo launcherActivityInfo;
+    @Nullable public final String packageName;
+    @Nullable public final String resourceName;
+    @Nullable public final byte[] iconBlob;
+    public final boolean useLowResIcon;
+
+    public IconRequestInfo(
+            @NonNull T itemInfo,
+            @Nullable LauncherActivityInfo launcherActivityInfo,
+            boolean useLowResIcon) {
+        this(
+                itemInfo,
+                launcherActivityInfo,
+                /* packageName= */ null,
+                /* resourceName= */ null,
+                /* iconBlob= */ null,
+                useLowResIcon);
+    }
+
+    public IconRequestInfo(
+            @NonNull T itemInfo,
+            @Nullable LauncherActivityInfo launcherActivityInfo,
+            @Nullable String packageName,
+            @Nullable String resourceName,
+            @Nullable byte[] iconBlob,
+            boolean useLowResIcon) {
+        this.itemInfo = itemInfo;
+        this.launcherActivityInfo = launcherActivityInfo;
+        this.packageName = packageName;
+        this.resourceName = resourceName;
+        this.iconBlob = iconBlob;
+        this.useLowResIcon = useLowResIcon;
+    }
+
+    /** Loads  */
+    public boolean loadWorkspaceIcon(Context context) {
+        if (!(itemInfo instanceof WorkspaceItemInfo)) {
+            throw new IllegalStateException(
+                    "loadWorkspaceIcon should only be use for a WorkspaceItemInfos: " + itemInfo);
+        }
+
+        try (LauncherIcons li = LauncherIcons.obtain(context)) {
+            WorkspaceItemInfo info = (WorkspaceItemInfo) itemInfo;
+            if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
+                if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) {
+                    info.iconResource = new Intent.ShortcutIconResource();
+                    info.iconResource.packageName = packageName;
+                    info.iconResource.resourceName = resourceName;
+                    BitmapInfo iconInfo = li.createIconBitmap(info.iconResource);
+                    if (iconInfo != null) {
+                        info.bitmap = iconInfo;
+                        return true;
+                    }
+                }
+            }
+
+            // Failed to load from resource, try loading from DB.
+            try {
+                if (iconBlob == null) {
+                    return false;
+                }
+                info.bitmap = li.createIconBitmap(decodeByteArray(
+                        iconBlob, 0, iconBlob.length));
+                return true;
+            } catch (Exception e) {
+                Log.e(TAG, "Failed to decode byte array for info " + info, e);
+                return false;
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 7091d2b..97398de 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -164,6 +164,7 @@
 
     public void copyFrom(ItemInfo info) {
         id = info.id;
+        title = info.title;
         cellX = info.cellX;
         cellY = info.cellY;
         spanX = info.spanX;
@@ -232,9 +233,9 @@
      * Write the fields of this item to the DB
      */
     public void onAddToDatabase(ContentWriter writer) {
-        if (screenId == Workspace.EXTRA_EMPTY_SCREEN_ID) {
+        if (Workspace.EXTRA_EMPTY_SCREEN_IDS.contains(screenId)) {
             // We should never persist an item on the extra empty screen.
-            throw new RuntimeException("Screen id should not be EXTRA_EMPTY_SCREEN_ID");
+            throw new RuntimeException("Screen id should not be extra empty screen: " + screenId);
         }
 
         writeToValues(writer);
diff --git a/src/com/android/launcher3/model/data/PackageItemInfo.java b/src/com/android/launcher3/model/data/PackageItemInfo.java
index a81fe6a..0055763 100644
--- a/src/com/android/launcher3/model/data/PackageItemInfo.java
+++ b/src/com/android/launcher3/model/data/PackageItemInfo.java
@@ -16,47 +16,41 @@
 
 package com.android.launcher3.model.data;
 
-import androidx.annotation.IntDef;
+import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
+
+import android.os.UserHandle;
 
 import com.android.launcher3.LauncherSettings;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
 /**
  * Represents a {@link Package} in the widget tray section.
  */
 public class PackageItemInfo extends ItemInfoWithIcon {
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({NO_CATEGORY, CONVERSATIONS})
-    public @interface Category{}
-    /** The package is not categorized in the widget tray. */
-    public static final int NO_CATEGORY = 0;
-    /** The package is categorized to conversations widget in the widget tray. */
-    public static final int CONVERSATIONS = 1;
-
     /**
      * Package name of the {@link PackageItemInfo}.
      */
     public final String packageName;
 
     /** Represents a widget category shown in the widget tray section. */
-    @Category public final int category;
+    public final int widgetCategory;
 
-    public PackageItemInfo(String packageName) {
-        this(packageName, NO_CATEGORY);
+    public PackageItemInfo(String packageName, UserHandle user) {
+        this(packageName, NO_CATEGORY, user);
     }
 
-    public PackageItemInfo(String packageName, @Category int category) {
+    public PackageItemInfo(String packageName, int widgetCategory, UserHandle user) {
         this.packageName = packageName;
-        this.category = category;
+        this.widgetCategory = widgetCategory;
+        this.user = user;
         this.itemType = LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE;
     }
 
     public PackageItemInfo(PackageItemInfo copy) {
         this.packageName = copy.packageName;
-        this.category = copy.category;
+        this.widgetCategory = copy.widgetCategory;
+        this.user = copy.user;
         this.itemType = LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE;
     }
 
@@ -75,11 +69,13 @@
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         PackageItemInfo that = (PackageItemInfo) o;
-        return Objects.equals(packageName, that.packageName);
+        return Objects.equals(packageName, that.packageName)
+                && Objects.equals(user, that.user)
+                && widgetCategory == that.widgetCategory;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(packageName, user);
+        return Objects.hash(packageName, user, widgetCategory);
     }
 }
diff --git a/src/com/android/launcher3/model/data/SearchActionItemInfo.java b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
index b3057d5..293c095 100644
--- a/src/com/android/launcher3/model/data/SearchActionItemInfo.java
+++ b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
@@ -25,8 +25,15 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.logger.LauncherAtom.ItemInfo;
 import com.android.launcher3.logger.LauncherAtom.SearchActionItem;
+import com.android.launcher3.model.AllAppsList;
+import com.android.launcher3.model.BaseModelUpdateTask;
+import com.android.launcher3.model.BgDataModel;
 
 /**
  * Represents a SearchAction with in launcher
@@ -38,13 +45,14 @@
     public static final int FLAG_BADGE_WITH_PACKAGE = 1 << 3;
     public static final int FLAG_PRIMARY_ICON_FROM_TITLE = 1 << 4;
     public static final int FLAG_BADGE_WITH_COMPONENT_NAME = 1 << 5;
+    public static final int FLAG_ALLOW_PINNING = 1 << 6;
 
-    private final String mFallbackPackageName;
+    private String mFallbackPackageName;
     private int mFlags = 0;
-    private final Icon mIcon;
+    private Icon mIcon;
 
     // If true title does not contain any personal info and eligible for logging.
-    private final boolean mIsPersonalTitle;
+    private boolean mIsPersonalTitle;
     private Intent mIntent;
 
     private PendingIntent mPendingIntent;
@@ -52,6 +60,7 @@
     public SearchActionItemInfo(Icon icon, String packageName, UserHandle user,
             CharSequence title, boolean isPersonalTitle) {
         mIsPersonalTitle = isPersonalTitle;
+        this.itemType = LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION;
         this.user = user == null ? Process.myUserHandle() : user;
         this.title = title;
         this.container = EXTENDED_CONTAINERS;
@@ -59,14 +68,18 @@
         mIcon = icon;
     }
 
-    public SearchActionItemInfo(SearchActionItemInfo info) {
+    private SearchActionItemInfo(SearchActionItemInfo info) {
         super(info);
-        mIcon = info.mIcon;
-        mFallbackPackageName = info.mFallbackPackageName;
-        mFlags = info.mFlags;
-        title = info.title;
-        this.container = EXTENDED_CONTAINERS;
-        this.mIsPersonalTitle = info.mIsPersonalTitle;
+    }
+
+    @Override
+    public void copyFrom(com.android.launcher3.model.data.ItemInfo info) {
+        super.copyFrom(info);
+        SearchActionItemInfo itemInfo = (SearchActionItemInfo) info;
+        this.mFallbackPackageName = itemInfo.mFallbackPackageName;
+        this.mIcon = itemInfo.mIcon;
+        this.mFlags = itemInfo.mFlags;
+        this.mIsPersonalTitle = itemInfo.mIsPersonalTitle;
     }
 
     /**
@@ -77,7 +90,7 @@
     }
 
     public void setFlags(int flags) {
-        mFlags |= flags ;
+        mFlags |= flags;
     }
 
     @Override
@@ -134,4 +147,50 @@
                 .setContainerInfo(getContainerInfo())
                 .build();
     }
+
+    /**
+     * Returns true if result supports drag/drop to home screen
+     */
+    public boolean supportsPinning() {
+        return hasFlags(FLAG_ALLOW_PINNING) && getIntentPackageName() != null;
+    }
+
+    /**
+     * Creates a {@link WorkspaceItemInfo} coorsponding to search action to be stored in launcher db
+     */
+    public WorkspaceItemInfo createWorkspaceItem(LauncherModel model) {
+        WorkspaceItemInfo info = new WorkspaceItemInfo();
+        info.title = title;
+        info.bitmap = bitmap;
+        info.intent = mIntent;
+
+        if (hasFlags(FLAG_SHOULD_START_FOR_RESULT)) {
+            info.options |= WorkspaceItemInfo.FLAG_START_FOR_RESULT;
+        }
+
+        model.enqueueModelUpdateTask(new BaseModelUpdateTask() {
+            @Override
+            public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+
+                model.updateAndBindWorkspaceItem(() -> {
+                    PackageItemInfo pkgInfo = new PackageItemInfo(getIntentPackageName(), user);
+                    app.getIconCache().getTitleAndIconForApp(pkgInfo, false);
+                    try (LauncherIcons li = LauncherIcons.obtain(app.getContext())) {
+                        info.bitmap = li.badgeBitmap(info.bitmap.icon, pkgInfo.bitmap);
+                    }
+                    return info;
+                });
+            }
+        });
+        return info;
+    }
+
+    @Nullable
+    private String getIntentPackageName() {
+        if (mIntent != null) {
+            if (mIntent.getPackage() != null) return mIntent.getPackage();
+            return mFallbackPackageName;
+        }
+        return null;
+    }
 }
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index 690e904..a195979 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -68,6 +68,11 @@
     public static final int FLAG_SUPPORTS_WEB_UI = 1 << 3;
 
     /**
+     *
+     */
+    public static final int FLAG_START_FOR_RESULT = 1 << 4;
+
+    /**
      * The intent used to start the application.
      */
     public Intent intent;
@@ -92,6 +97,8 @@
      */
     @NonNull private String[] personKeys = Utilities.EMPTY_STRING_ARRAY;
 
+    public int options;
+
 
     public WorkspaceItemInfo() {
         itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
@@ -127,6 +134,7 @@
         super.onAddToDatabase(writer);
         writer.put(Favorites.TITLE, title)
                 .put(Favorites.INTENT, getIntent())
+                .put(Favorites.OPTIONS, options)
                 .put(Favorites.RESTORED, status);
 
         if (!usingLowResIcon()) {
@@ -204,7 +212,7 @@
     }
 
     @Override
-    public ItemInfoWithIcon clone() {
+    public WorkspaceItemInfo clone() {
         return new WorkspaceItemInfo(this);
     }
 }
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index f7c730a..29eefe2 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -53,6 +53,9 @@
     private static final int ENTER_ANIMATION_STAGGERED_DELAY = 150;
     private static final int ENTER_ANIMATION_DURATION = 400;
 
+    private static final int DOT_ACTIVE_ALPHA = 255;
+    private static final int DOT_INACTIVE_ALPHA = 128;
+
     // This value approximately overshoots to 1.5 times the original size.
     private static final float ENTER_ANIMATION_OVERSHOOT_TENSION = 4.9f;
 
@@ -75,8 +78,6 @@
 
     private final Paint mCirclePaint;
     private final float mDotRadius;
-    private final int mActiveColor;
-    private final int mInActiveColor;
     private final boolean mIsRtl;
 
     private int mNumPages;
@@ -110,12 +111,10 @@
 
         mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
         mCirclePaint.setStyle(Style.FILL);
+        mCirclePaint.setColor(Themes.getAttrColor(context, R.attr.folderPaginationColor));
         mDotRadius = getResources().getDimension(R.dimen.page_indicator_dot_size) / 2;
         setOutlineProvider(new MyOutlineProver());
 
-        mActiveColor = Themes.getColorAccent(context);
-        mInActiveColor = Themes.getAttrColor(context, android.R.attr.colorControlHighlight);
-
         mIsRtl = Utilities.isRtl(getResources());
     }
 
@@ -253,18 +252,18 @@
                 circleGap = -circleGap;
             }
             for (int i = 0; i < mEntryAnimationRadiusFactors.length; i++) {
-                mCirclePaint.setColor(i == mActivePage ? mActiveColor : mInActiveColor);
+                mCirclePaint.setAlpha(i == mActivePage ? DOT_ACTIVE_ALPHA : DOT_INACTIVE_ALPHA);
                 canvas.drawCircle(x, y, mDotRadius * mEntryAnimationRadiusFactors[i], mCirclePaint);
                 x += circleGap;
             }
         } else {
-            mCirclePaint.setColor(mInActiveColor);
+            mCirclePaint.setAlpha(DOT_INACTIVE_ALPHA);
             for (int i = 0; i < mNumPages; i++) {
                 canvas.drawCircle(x, y, mDotRadius, mCirclePaint);
                 x += circleGap;
             }
 
-            mCirclePaint.setColor(mActiveColor);
+            mCirclePaint.setAlpha(DOT_ACTIVE_ALPHA);
             canvas.drawRoundRect(getActiveRect(), mDotRadius, mDotRadius, mCirclePaint);
         }
     }
diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
index f73d782..c685891 100644
--- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
@@ -268,9 +268,7 @@
         } else {
             lp.leftMargin = lp.rightMargin = 0;
             lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
-            lp.bottomMargin = grid.isTaskbarPresent
-                    ? grid.workspacePadding.bottom + grid.taskbarSize
-                    : grid.hotseatBarSizePx + insets.bottom;
+            lp.bottomMargin = grid.hotseatBarSizePx + insets.bottom;
         }
         setLayoutParams(lp);
     }
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index ab35bd6..4b86f65 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -17,7 +17,6 @@
 package com.android.launcher3.pm;
 
 import static com.android.launcher3.Utilities.getPrefs;
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -25,7 +24,6 @@
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageManager;
-import android.os.Build;
 import android.os.Process;
 import android.os.UserHandle;
 import android.text.TextUtils;
@@ -238,24 +236,12 @@
     }
 
     public InstallSessionTracker registerInstallTracker(InstallSessionTracker.Callback callback) {
-        InstallSessionTracker tracker = new InstallSessionTracker(this, callback);
-
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
-            mInstaller.registerSessionCallback(tracker, MODEL_EXECUTOR.getHandler());
-        } else {
-            mLauncherApps.registerPackageInstallerSessionCallback(MODEL_EXECUTOR, tracker);
-        }
+        InstallSessionTracker tracker = new InstallSessionTracker(
+                this, callback, mInstaller, mLauncherApps);
+        tracker.register();
         return tracker;
     }
 
-    void unregister(InstallSessionTracker tracker) {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
-            mInstaller.unregisterSessionCallback(tracker);
-        } else {
-            mLauncherApps.unregisterPackageInstallerSessionCallback(tracker);
-        }
-    }
-
     public static UserHandle getUserHandle(SessionInfo info) {
         return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle();
     }
diff --git a/src/com/android/launcher3/pm/InstallSessionTracker.java b/src/com/android/launcher3/pm/InstallSessionTracker.java
index b0b907a..e1b3c1a 100644
--- a/src/com/android/launcher3/pm/InstallSessionTracker.java
+++ b/src/com/android/launcher3/pm/InstallSessionTracker.java
@@ -18,9 +18,12 @@
 import static com.android.launcher3.pm.InstallSessionHelper.getUserHandle;
 import static com.android.launcher3.pm.PackageInstallInfo.STATUS_FAILED;
 import static com.android.launcher3.pm.PackageInstallInfo.STATUS_INSTALLED;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
+import android.content.pm.LauncherApps;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionInfo;
+import android.os.Build;
 import android.os.UserHandle;
 import android.util.SparseArray;
 
@@ -28,35 +31,53 @@
 
 import com.android.launcher3.util.PackageUserKey;
 
+import java.lang.ref.WeakReference;
+
 @WorkerThread
 public class InstallSessionTracker extends PackageInstaller.SessionCallback {
 
     // Lazily initialized
     private SparseArray<PackageUserKey> mActiveSessions = null;
 
-    private final InstallSessionHelper mInstallerCompat;
-    private final Callback mCallback;
+    private final WeakReference<InstallSessionHelper> mWeakHelper;
+    private final WeakReference<Callback> mWeakCallback;
+    private final PackageInstaller mInstaller;
+    private final LauncherApps mLauncherApps;
 
-    InstallSessionTracker(InstallSessionHelper installerCompat, Callback callback) {
-        mInstallerCompat = installerCompat;
-        mCallback = callback;
+
+    InstallSessionTracker(InstallSessionHelper installerCompat, Callback callback,
+            PackageInstaller installer, LauncherApps launcherApps) {
+        mWeakHelper = new WeakReference<>(installerCompat);
+        mWeakCallback = new WeakReference<>(callback);
+        mInstaller = installer;
+        mLauncherApps = launcherApps;
     }
 
     @Override
     public void onCreated(int sessionId) {
-        SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId);
+        InstallSessionHelper helper = mWeakHelper.get();
+        Callback callback = mWeakCallback.get();
+        if (callback == null || helper == null) {
+            return;
+        }
+        SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId, helper, callback);
         if (sessionInfo != null) {
-            mCallback.onInstallSessionCreated(PackageInstallInfo.fromInstallingState(sessionInfo));
+            callback.onInstallSessionCreated(PackageInstallInfo.fromInstallingState(sessionInfo));
         }
 
-        mInstallerCompat.tryQueuePromiseAppIcon(sessionInfo);
+        helper.tryQueuePromiseAppIcon(sessionInfo);
     }
 
     @Override
     public void onFinished(int sessionId, boolean success) {
+        InstallSessionHelper helper = mWeakHelper.get();
+        Callback callback = mWeakCallback.get();
+        if (callback == null || helper == null) {
+            return;
+        }
         // For a finished session, we can't get the session info. So use the
         // packageName from our local cache.
-        SparseArray<PackageUserKey> activeSessions = getActiveSessionMap();
+        SparseArray<PackageUserKey> activeSessions = getActiveSessionMap(helper);
         PackageUserKey key = activeSessions.get(sessionId);
         activeSessions.remove(sessionId);
 
@@ -65,21 +86,26 @@
             PackageInstallInfo info = PackageInstallInfo.fromState(
                     success ? STATUS_INSTALLED : STATUS_FAILED,
                     packageName, key.mUser);
-            mCallback.onPackageStateChanged(info);
+            callback.onPackageStateChanged(info);
 
-            if (!success && mInstallerCompat.promiseIconAddedForId(sessionId)) {
-                mCallback.onSessionFailure(packageName, key.mUser);
+            if (!success && helper.promiseIconAddedForId(sessionId)) {
+                callback.onSessionFailure(packageName, key.mUser);
                 // If it is successful, the id is removed in the the package added flow.
-                mInstallerCompat.removePromiseIconId(sessionId);
+                helper.removePromiseIconId(sessionId);
             }
         }
     }
 
     @Override
     public void onProgressChanged(int sessionId, float progress) {
-        SessionInfo session = mInstallerCompat.getVerifiedSessionInfo(sessionId);
+        InstallSessionHelper helper = mWeakHelper.get();
+        Callback callback = mWeakCallback.get();
+        if (callback == null || helper == null) {
+            return;
+        }
+        SessionInfo session = helper.getVerifiedSessionInfo(sessionId);
         if (session != null && session.getAppPackageName() != null) {
-            mCallback.onPackageStateChanged(PackageInstallInfo.fromInstallingState(session));
+            callback.onPackageStateChanged(PackageInstallInfo.fromInstallingState(session));
         }
     }
 
@@ -88,35 +114,53 @@
 
     @Override
     public void onBadgingChanged(int sessionId) {
-        SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId);
+        InstallSessionHelper helper = mWeakHelper.get();
+        Callback callback = mWeakCallback.get();
+        if (callback == null || helper == null) {
+            return;
+        }
+        SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId, helper, callback);
         if (sessionInfo != null) {
-            mInstallerCompat.tryQueuePromiseAppIcon(sessionInfo);
+            helper.tryQueuePromiseAppIcon(sessionInfo);
         }
     }
 
-    private SessionInfo pushSessionDisplayToLauncher(int sessionId) {
-        SessionInfo session = mInstallerCompat.getVerifiedSessionInfo(sessionId);
+    private SessionInfo pushSessionDisplayToLauncher(
+            int sessionId, InstallSessionHelper helper, Callback callback) {
+        SessionInfo session = helper.getVerifiedSessionInfo(sessionId);
         if (session != null && session.getAppPackageName() != null) {
             PackageUserKey key =
                     new PackageUserKey(session.getAppPackageName(), getUserHandle(session));
-            getActiveSessionMap().put(session.getSessionId(), key);
-            mCallback.onUpdateSessionDisplay(key, session);
+            getActiveSessionMap(helper).put(session.getSessionId(), key);
+            callback.onUpdateSessionDisplay(key, session);
             return session;
         }
         return null;
     }
 
-    private SparseArray<PackageUserKey> getActiveSessionMap() {
+    private SparseArray<PackageUserKey> getActiveSessionMap(InstallSessionHelper helper) {
         if (mActiveSessions == null) {
             mActiveSessions = new SparseArray<>();
-            mInstallerCompat.getActiveSessions().forEach(
+            helper.getActiveSessions().forEach(
                     (key, si) -> mActiveSessions.put(si.getSessionId(), key));
         }
         return mActiveSessions;
     }
 
+    void register() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+            mInstaller.registerSessionCallback(this, MODEL_EXECUTOR.getHandler());
+        } else {
+            mLauncherApps.registerPackageInstallerSessionCallback(MODEL_EXECUTOR, this);
+        }
+    }
+
     public void unregister() {
-        mInstallerCompat.unregister(this);
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+            mInstaller.unregisterSessionCallback(this);
+        } else {
+            mLauncherApps.unregisterPackageInstallerSessionCallback(this);
+        }
     }
 
     public interface Callback {
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index 5ade22b..5aab41a 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -21,10 +21,8 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.ArrayMap;
-import android.util.Log;
 import android.util.LongSparseArray;
 
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.util.SimpleBroadcastReceiver;
@@ -60,9 +58,6 @@
     private void onUsersChanged(Intent intent) {
         enableAndResetCache();
         mUserChangeListeners.forEach(Runnable::run);
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.WORK_PROFILE_REMOVED, "profile changed", new Exception());
-        }
     }
 
     /**
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 5d3ba75..2230914 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -33,7 +33,6 @@
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
@@ -50,26 +49,23 @@
 import android.widget.FrameLayout;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.shortcuts.DeepShortcutView;
-import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.launcher3.widget.LocalColorExtractor;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -77,7 +73,7 @@
  *
  * @param <T> The activity on with the popup shows
  */
-public abstract class ArrowPopup<T extends StatefulActivity<LauncherState>>
+public abstract class ArrowPopup<T extends Context & ActivityContext>
         extends AbstractFloatingView {
 
     // Duration values (ms) for popup open and close animations.
@@ -101,7 +97,7 @@
 
     protected final LayoutInflater mInflater;
     private final float mOutlineRadius;
-    protected final T mLauncher;
+    protected final T mActivityContext;
     protected final boolean mIsRtl;
 
     private final int mArrowOffsetVertical;
@@ -126,24 +122,21 @@
     private Runnable mOnCloseCallback = () -> { };
 
     // The rect string of the view that the arrow is attached to, in screen reference frame.
-    protected String mArrowColorRectString;
     private int mArrowColor;
-    protected final HashMap<String, View> mViewForRect = new HashMap<>();
-
-    @Nullable protected LocalColorExtractor mColorExtractor;
+    protected final List<LocalColorExtractor> mColorExtractors;
 
     private final float mElevation;
     private final int mBackgroundColor;
 
     private final String mIterateChildrenTag;
 
-    private final int[] mColors;
+    private final int[] mColorIds;
 
     public ArrowPopup(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         mInflater = LayoutInflater.from(context);
         mOutlineRadius = Themes.getDialogCornerRadius(context);
-        mLauncher = BaseDraggingActivity.fromContext(context);
+        mActivityContext = ActivityContext.lookupContext(context);
         mIsRtl = Utilities.isRtl(getResources());
 
         mBackgroundColor = Themes.getAttrColor(context, R.attr.popupColorPrimary);
@@ -175,20 +168,18 @@
 
         mIterateChildrenTag = getContext().getString(R.string.popup_container_iterate_children);
 
-        boolean isAboveAnotherSurface = getTopOpenViewWithType(mLauncher, TYPE_FOLDER) != null
-                || mLauncher.getStateManager().getState() == LauncherState.ALL_APPS;
-        if (!isAboveAnotherSurface && Utilities.ATLEAST_S && ENABLE_LOCAL_COLOR_POPUPS.get()) {
-            setupColorExtraction();
+        boolean shouldUseColorExtraction = mActivityContext.shouldUseColorExtractionForPopup();
+        if (shouldUseColorExtraction && Utilities.ATLEAST_S && ENABLE_LOCAL_COLOR_POPUPS.get()) {
+            mColorExtractors = new ArrayList<>();
+        } else {
+            mColorExtractors = null;
         }
 
-        if (isAboveAnotherSurface) {
-            mColors = new int[] {
-                    getColorStateList(context, R.color.popup_shade_first).getDefaultColor()};
+        if (shouldUseColorExtraction) {
+            mColorIds = new int[]{R.color.popup_shade_first, R.color.popup_shade_second,
+                    R.color.popup_shade_third};
         } else {
-            mColors = new int[] {
-                    getColorStateList(context, R.color.popup_shade_first).getDefaultColor(),
-                    getColorStateList(context, R.color.popup_shade_second).getDefaultColor(),
-                    getColorStateList(context, R.color.popup_shade_third).getDefaultColor()};
+            mColorIds = new int[]{R.color.popup_shade_first};
         }
     }
 
@@ -240,17 +231,22 @@
     }
 
     /**
-     * @param backgroundColor When Color.TRANSPARENT, we get color from {@link #mColors}.
+     * @param backgroundColor When Color.TRANSPARENT, we get color from {@link #mColorIds}.
      *                        Otherwise, we will use this color for all child views.
      */
     private void assignMarginsAndBackgrounds(ViewGroup viewGroup, int backgroundColor) {
-        final boolean getColorFromColorArray = backgroundColor == Color.TRANSPARENT;
+        int[] colors = null;
+        if (backgroundColor == Color.TRANSPARENT) {
+            // Lazily get the colors so they match the current wallpaper colors.
+            colors = Arrays.stream(mColorIds).map(
+                    r -> getColorStateList(getContext(), r).getDefaultColor()).toArray();
+        }
 
         int count = viewGroup.getChildCount();
         int totalVisibleShortcuts = 0;
         for (int i = 0; i < count; i++) {
             View view = viewGroup.getChildAt(i);
-            if (view.getVisibility() == VISIBLE && view instanceof DeepShortcutView) {
+            if (view.getVisibility() == VISIBLE && isShortcutOrWrapper(view)) {
                 totalVisibleShortcuts++;
             }
         }
@@ -270,9 +266,17 @@
                 MarginLayoutParams mlp = (MarginLayoutParams) lastView.getLayoutParams();
                 mlp.bottomMargin = 0;
 
+                if (colors != null) {
+                    backgroundColor = colors[numVisibleChild % colors.length];
+                }
 
-                if (getColorFromColorArray) {
-                    backgroundColor = mColors[numVisibleChild % mColors.length];
+                if (!ENABLE_LOCAL_COLOR_POPUPS.get()) {
+                    // Arrow color matches the first child or the last child.
+                    if (!mIsAboveIcon && numVisibleChild == 0 && viewGroup == this) {
+                        mArrowColor = backgroundColor;
+                    } else if (mIsAboveIcon) {
+                        mArrowColor = backgroundColor;
+                    }
                 }
 
                 if (view instanceof ViewGroup && mIterateChildrenTag.equals(view.getTag())) {
@@ -281,7 +285,7 @@
                     continue;
                 }
 
-                if (view instanceof DeepShortcutView) {
+                if (isShortcutOrWrapper(view)) {
                     if (totalVisibleShortcuts == 1) {
                         view.setBackgroundResource(R.drawable.single_item_primary);
                     } else if (totalVisibleShortcuts > 1) {
@@ -298,12 +302,6 @@
 
                 if (!ENABLE_LOCAL_COLOR_POPUPS.get()) {
                     setChildColor(view, backgroundColor, colorAnimator);
-                    // Arrow color matches the first child or the last child.
-                    if (!mIsAboveIcon && numVisibleChild == 0) {
-                        mArrowColor = backgroundColor;
-                    } else if (mIsAboveIcon) {
-                        mArrowColor = backgroundColor;
-                    }
                 }
 
                 numVisibleChild++;
@@ -314,6 +312,12 @@
         measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
     }
 
+    /**
+     * Returns {@code true} if the child is a shortcut or wraps a shortcut.
+     */
+    protected boolean isShortcutOrWrapper(View view) {
+        return view instanceof DeepShortcutView;
+    }
 
     @TargetApi(Build.VERSION_CODES.S)
     private int getExtractedColor(SparseIntArray colors) {
@@ -323,37 +327,6 @@
         return colors.get(index, mBackgroundColor);
     }
 
-    @TargetApi(Build.VERSION_CODES.S)
-    private void setupColorExtraction() {
-        Workspace workspace = mLauncher.findViewById(R.id.workspace);
-        if (workspace == null) {
-            return;
-        }
-
-        mColorExtractor = LocalColorExtractor.newInstance(mLauncher);
-        mColorExtractor.setListener((rect, extractedColors) -> {
-            String rectString = rect.toShortString();
-            View v = mViewForRect.get(rectString);
-            AnimatorSet colors = new AnimatorSet();
-            if (v != null) {
-                int newColor = getExtractedColor(extractedColors);
-                setChildColor(v, newColor, colors);
-                int numChildren = v instanceof ViewGroup ? ((ViewGroup) v).getChildCount() : 0;
-                for (int i = 0; i < numChildren; ++i) {
-                    View childView = ((ViewGroup) v).getChildAt(i);
-                    setChildColor(childView, newColor, colors);
-
-                }
-                if (rectString.equals(mArrowColorRectString)) {
-                    mArrowColor = newColor;
-                    updateArrowColor();
-                }
-            }
-            colors.setDuration(150);
-            v.post(colors::start);
-        });
-    }
-
     protected void addPreDrawForColorExtraction(Launcher launcher) {
         getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
             @Override
@@ -374,40 +347,55 @@
     }
 
     private void initColorExtractionLocations(Launcher launcher) {
-        if (mColorExtractor == null) {
+        if (mColorExtractors == null) {
             return;
         }
-        ArrayList<RectF> locations = new ArrayList<>();
+        Workspace workspace = launcher.getWorkspace();
+        if (workspace == null) {
+            return;
+        }
 
         boolean firstVisibleChild = true;
+        int screenId = workspace.getScreenIdForPageIndex(workspace.getCurrentPage());
+        DragLayer dragLayer = launcher.getDragLayer();
+
+        final View[] viewAlignedWithArrow = new View[1];
+
         // Order matters here, since we need the arrow to match the color of its adjacent view.
-        for (View view : getChildrenForColorExtraction()) {
+        for (final View view : getChildrenForColorExtraction()) {
             if (view != null && view.getVisibility() == VISIBLE) {
-                RectF rf = new RectF();
-                mColorExtractor.getExtractedRectForView(launcher,
-                        launcher.getWorkspace().getCurrentPage(), view, rf);
-                if (!rf.isEmpty()) {
-                    locations.add(rf);
-                    String rectString = rf.toShortString();
-                    mViewForRect.put(rectString, view);
-                    if (mIsAboveIcon) {
-                        mArrowColorRectString = rectString;
-                    } else {
-                        if (firstVisibleChild) {
-                            mArrowColorRectString = rectString;
+                Rect pos = new Rect();
+                dragLayer.getDescendantRectRelativeToSelf(view, pos);
+                if (!pos.isEmpty()) {
+                    LocalColorExtractor extractor = LocalColorExtractor.newInstance(launcher);
+                    extractor.setWorkspaceLocation(pos, dragLayer, screenId);
+                    extractor.setListener(extractedColors -> {
+                        AnimatorSet colors = new AnimatorSet();
+                        int newColor = getExtractedColor(extractedColors);
+                        setChildColor(view, newColor, colors);
+                        int numChildren = view instanceof ViewGroup
+                                ? ((ViewGroup) view).getChildCount() : 0;
+                        for (int i = 0; i < numChildren; ++i) {
+                            View childView = ((ViewGroup) view).getChildAt(i);
+                            setChildColor(childView, newColor, colors);
                         }
-                    }
+                        if (viewAlignedWithArrow[0] == view) {
+                            mArrowColor = newColor;
+                            updateArrowColor();
+                        }
+                        colors.setDuration(150);
+                        view.post(colors::start);
+                    });
+                    mColorExtractors.add(extractor);
 
-                    if (firstVisibleChild) {
-                        firstVisibleChild = false;
+                    if (mIsAboveIcon || firstVisibleChild) {
+                        viewAlignedWithArrow[0] = view;
                     }
-
+                    firstVisibleChild = false;
                 }
             }
         }
-        if (!locations.isEmpty()) {
-            mColorExtractor.addLocation(locations);
-        }
+
     }
 
     /**
@@ -447,7 +435,7 @@
     /**
      * Shows the popup at the desired location.
      */
-    protected void show() {
+    public void show() {
         setupForDisplay();
         onInflationComplete(false);
         assignMarginsAndBackgrounds(this);
@@ -510,7 +498,7 @@
         mArrow.setPivotY(mIsAboveIcon ? mArrowHeight : 0);
     }
 
-    private void updateArrowColor() {
+    protected void updateArrowColor() {
         if (!Gravity.isVertical(mGravity)) {
             mArrow.setBackground(new RoundedArrowDrawable(
                     mArrowWidth, mArrowHeight, mArrowPointRadius,
@@ -814,11 +802,8 @@
         getPopupContainer().removeView(this);
         getPopupContainer().removeView(mArrow);
         mOnCloseCallback.run();
-        mArrowColorRectString = null;
-        mViewForRect.clear();
-        if (mColorExtractor != null) {
-            mColorExtractor.removeLocations();
-            mColorExtractor.setListener(null);
+        if (mColorExtractors != null) {
+            mColorExtractors.forEach(e -> e.setListener(null));
         }
     }
 
@@ -830,6 +815,6 @@
     }
 
     protected BaseDragLayer getPopupContainer() {
-        return mLauncher.getDragLayer();
+        return mActivityContext.getDragLayer();
     }
 }
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index bc3419a..b963950 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -46,7 +46,6 @@
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
@@ -64,10 +63,10 @@
 import com.android.launcher3.popup.PopupDataProvider.PopupDataChangeListener;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
-import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.ShortcutUtil;
+import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.BaseDragLayer;
 
 import java.util.ArrayList;
@@ -83,7 +82,7 @@
  *
  * @param <T> The activity on with the popup shows
  */
-public class PopupContainerWithArrow<T extends StatefulActivity<LauncherState>>
+public class PopupContainerWithArrow<T extends Context & ActivityContext>
         extends ArrowPopup<T> implements DragSource, DragController.DragListener {
 
     private final List<DeepShortcutView> mShortcuts = new ArrayList<>();
@@ -151,7 +150,7 @@
 
     public OnClickListener getItemClickListener() {
         return (view) -> {
-            mLauncher.getItemOnClickListener().onClick(view);
+            mActivityContext.getItemOnClickListener().onClick(view);
             close(true);
         };
     }
@@ -192,10 +191,10 @@
     }
 
     /**
-     * Shows the notifications and deep shortcuts associated with {@param icon}.
+     * Shows the notifications and deep shortcuts associated with a Launcher {@param icon}.
      * @return the container if shown or null.
      */
-    public static PopupContainerWithArrow showForIcon(BubbleTextView icon) {
+    public static PopupContainerWithArrow<Launcher> showForIcon(BubbleTextView icon) {
         Launcher launcher = Launcher.getLauncher(icon.getContext());
         if (getOpen(launcher) != null) {
             // There is already an items container open, so don't open this one.
@@ -207,7 +206,7 @@
             return null;
         }
 
-        final PopupContainerWithArrow container =
+        final PopupContainerWithArrow<Launcher> container =
                 (PopupContainerWithArrow) launcher.getLayoutInflater().inflate(
                         R.layout.popup_container, launcher.getDragLayer(), false);
         container.configureForLauncher(launcher);
@@ -326,7 +325,7 @@
 
         // Load the shortcuts on a background thread and update the container as it animates.
         MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(PopupPopulator.createUpdateRunnable(
-                mLauncher, originalItemInfo, new Handler(Looper.getMainLooper()),
+                mActivityContext, originalItemInfo, new Handler(Looper.getMainLooper()),
                 this, mShortcuts, notificationKeys));
     }
 
@@ -439,7 +438,7 @@
 
     private void updateNotificationHeader() {
         ItemInfoWithIcon itemInfo = (ItemInfoWithIcon) mOriginalIcon.getTag();
-        DotInfo dotInfo = mLauncher.getDotInfoForItem(itemInfo);
+        DotInfo dotInfo = mActivityContext.getDotInfoForItem(itemInfo);
         if (mNotificationContainer != null && dotInfo != null) {
             mNotificationContainer.updateHeader(dotInfo.getNotificationCount());
         }
@@ -480,19 +479,19 @@
 
     @Override
     protected void closeComplete() {
-        PopupContainerWithArrow openPopup = getOpen(mLauncher);
+        super.closeComplete();
+        PopupContainerWithArrow openPopup = getOpen(mActivityContext);
         if (openPopup == null || openPopup.mOriginalIcon != mOriginalIcon) {
             mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
             mOriginalIcon.setForceHideDot(false);
         }
-        super.closeComplete();
     }
 
     /**
      * Returns a PopupContainerWithArrow which is already open or null
      */
-    public static PopupContainerWithArrow getOpen(BaseDraggingActivity launcher) {
-        return getOpenView(launcher, TYPE_ACTION_POPUP);
+    public static <T extends Context & ActivityContext> PopupContainerWithArrow getOpen(T context) {
+        return getOpenView(context, TYPE_ACTION_POPUP);
     }
 
     /**
@@ -593,6 +592,7 @@
                 mNotificationContainer.setVisibility(GONE);
                 updateHiddenShortcuts();
                 assignMarginsAndBackgrounds(PopupContainerWithArrow.this);
+                updateArrowColor();
             } else {
                 mNotificationContainer.trimNotifications(
                         NotificationKeyData.extractKeysOnly(dotInfo.getNotificationKeys()));
diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java
index 5ed6f2e..1dce1f2 100644
--- a/src/com/android/launcher3/popup/PopupPopulator.java
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -19,6 +19,7 @@
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS;
 
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.pm.ShortcutInfo;
 import android.os.Handler;
 import android.os.UserHandle;
@@ -26,7 +27,6 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.data.ItemInfo;
@@ -36,6 +36,7 @@
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.shortcuts.ShortcutRequest;
+import com.android.launcher3.views.ActivityContext;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -128,7 +129,8 @@
     /**
      * Returns a runnable to update the provided shortcuts and notifications
      */
-    public static Runnable createUpdateRunnable(final BaseDraggingActivity launcher,
+    public static <T extends Context & ActivityContext> Runnable createUpdateRunnable(
+            final T context,
             final ItemInfo originalInfo,
             final Handler uiHandler, final PopupContainerWithArrow container,
             final List<DeepShortcutView> shortcutViews,
@@ -144,22 +146,22 @@
                     infos = Collections.emptyList();
                 } else {
                     infos = notificationListener.getNotificationsForKeys(notificationKeys).stream()
-                            .map(sbn -> new NotificationInfo(launcher, sbn, originalInfo))
+                            .map(sbn -> new NotificationInfo(context, sbn, originalInfo))
                             .collect(Collectors.toList());
                 }
                 uiHandler.post(() -> container.applyNotificationInfos(infos));
             }
 
-            List<ShortcutInfo> shortcuts = new ShortcutRequest(launcher, user)
+            List<ShortcutInfo> shortcuts = new ShortcutRequest(context, user)
                     .withContainer(activity)
                     .query(ShortcutRequest.PUBLISHED);
             String shortcutIdToDeDupe = notificationKeys.isEmpty() ? null
                     : notificationKeys.get(0).shortcutId;
             shortcuts = PopupPopulator.sortAndFilterShortcuts(shortcuts, shortcutIdToDeDupe);
-            IconCache cache = LauncherAppState.getInstance(launcher).getIconCache();
+            IconCache cache = LauncherAppState.getInstance(context).getIconCache();
             for (int i = 0; i < shortcuts.size() && i < shortcutViews.size(); i++) {
                 final ShortcutInfo shortcut = shortcuts.get(i);
-                final WorkspaceItemInfo si = new WorkspaceItemInfo(shortcut, launcher);
+                final WorkspaceItemInfo si = new WorkspaceItemInfo(shortcut, context);
                 cache.getUnbadgedShortcutIcon(si, shortcut);
                 si.rank = i;
                 si.container = CONTAINER_SHORTCUTS;
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index d3f4909..826c79b 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -18,12 +18,14 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.InstantAppResolver;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.widget.WidgetsBottomSheet;
 
 import java.util.List;
@@ -35,7 +37,7 @@
  * Example system shortcuts, defined as inner classes, include Widgets and AppInfo.
  * @param <T>
  */
-public abstract class SystemShortcut<T extends BaseDraggingActivity> extends ItemInfo
+public abstract class SystemShortcut<T extends Context & ActivityContext> extends ItemInfo
         implements View.OnClickListener {
 
     private final int mIconResId;
@@ -50,8 +52,6 @@
      */
     private boolean isEnabled = true;
 
-    private boolean mHasFinishRecentsInAction = false;
-
     public SystemShortcut(int iconResId, int labelResId, T target, ItemInfo itemInfo) {
         mIconResId = iconResId;
         mLabelResId = labelResId;
@@ -102,15 +102,7 @@
         return mAccessibilityActionId == action;
     }
 
-    public void setHasFinishRecentsInAction(boolean hasFinishRecentsInAction) {
-        mHasFinishRecentsInAction = hasFinishRecentsInAction;
-    }
-
-    public boolean hasFinishRecentsInAction() {
-        return mHasFinishRecentsInAction;
-    }
-
-    public interface Factory<T extends BaseDraggingActivity> {
+    public interface Factory<T extends Context & ActivityContext> {
 
         @Nullable SystemShortcut<T> getShortcut(T activity, ItemInfo itemInfo);
     }
@@ -145,9 +137,9 @@
 
     public static final Factory<BaseDraggingActivity> APP_INFO = AppInfo::new;
 
-    public static class AppInfo extends SystemShortcut {
+    public static class AppInfo<T extends Context & ActivityContext> extends SystemShortcut<T> {
 
-        public AppInfo(BaseDraggingActivity target, ItemInfo itemInfo) {
+        public AppInfo(T target, ItemInfo itemInfo) {
             super(R.drawable.ic_info_no_shadow, R.string.app_info_drop_target_label, target,
                     itemInfo);
         }
@@ -155,7 +147,7 @@
         @Override
         public void onClick(View view) {
             dismissTaskMenuView(mTarget);
-            Rect sourceBounds = mTarget.getViewBounds(view);
+            Rect sourceBounds = Utilities.getViewBounds(view);
             new PackageManagerHelper(mTarget).startDetailsActivityForInfo(
                     mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
             mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
@@ -180,7 +172,7 @@
         return new Install(activity, itemInfo);
     };
 
-    public static class Install extends SystemShortcut {
+    public static class Install extends SystemShortcut<BaseDraggingActivity> {
 
         public Install(BaseDraggingActivity target, ItemInfo itemInfo) {
             super(R.drawable.ic_install_no_shadow, R.string.install_drop_target_label,
@@ -196,7 +188,7 @@
         }
     }
 
-    public static void dismissTaskMenuView(BaseDraggingActivity activity) {
+    public static <T extends Context & ActivityContext> void dismissTaskMenuView(T activity) {
         AbstractFloatingView.closeOpenViews(activity, true,
             AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
     }
diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java
deleted file mode 100644
index c9af2fe..0000000
--- a/src/com/android/launcher3/provider/ImportDataTask.java
+++ /dev/null
@@ -1,438 +0,0 @@
-/*
- * Copyright (C) 2016 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.provider;
-
-import static com.android.launcher3.Utilities.getDevicePrefs;
-
-import android.content.ContentProviderOperation;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.net.Uri;
-import android.os.Process;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.SparseBooleanArray;
-
-import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
-import com.android.launcher3.DefaultLayoutParser;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherProvider;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.LauncherSettings.Settings;
-import com.android.launcher3.Workspace;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.logging.FileLog;
-import com.android.launcher3.model.GridSizeMigrationTask;
-import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.IntSparseArrayMap;
-import com.android.launcher3.util.PackageManagerHelper;
-
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.HashSet;
-
-/**
- * Utility class to import data from another Launcher which is based on Launcher3 schema.
- */
-public class ImportDataTask {
-
-    public static final String KEY_DATA_IMPORT_SRC_PKG = "data_import_src_pkg";
-    public static final String KEY_DATA_IMPORT_SRC_AUTHORITY = "data_import_src_authority";
-
-    private static final String TAG = "ImportDataTask";
-    private static final int MIN_ITEM_COUNT_FOR_SUCCESSFUL_MIGRATION = 6;
-    // Insert items progressively to avoid OOM exception when loading icons.
-    private static final int BATCH_INSERT_SIZE = 15;
-
-    private final Context mContext;
-
-    private final Uri mOtherFavoritesUri;
-
-    private int mHotseatSize;
-    private int mMaxGridSizeX;
-    private int mMaxGridSizeY;
-
-    private ImportDataTask(Context context, String sourceAuthority) {
-        mContext = context;
-        mOtherFavoritesUri = Uri.parse("content://" + sourceAuthority + "/" + Favorites.TABLE_NAME);
-    }
-
-    public boolean importWorkspace() throws Exception {
-        FileLog.d(TAG, "Importing DB from " + mOtherFavoritesUri);
-
-        mHotseatSize = mMaxGridSizeX = mMaxGridSizeY = 0;
-        importWorkspaceItems();
-        GridSizeMigrationTask.markForMigration(mContext, mMaxGridSizeX, mMaxGridSizeY, mHotseatSize);
-
-        // Create empty DB flag.
-        LauncherSettings.Settings.call(mContext.getContentResolver(),
-                LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
-        return true;
-    }
-
-    /**
-     * 1) Imports all the workspace entries from the source provider.
-     * 2) For home screen entries, maps the screen id based on {@param screenIdMap}
-     * 3) In the end fills any holes in hotseat with items from default hotseat layout.
-     */
-    private void importWorkspaceItems() throws Exception {
-        String profileId = Long.toString(UserCache.INSTANCE.get(mContext)
-                .getSerialNumberForUser(Process.myUserHandle()));
-
-        boolean createEmptyRowOnFirstScreen;
-        if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
-            try (Cursor c = mContext.getContentResolver().query(mOtherFavoritesUri, null,
-                    // get items on the first row of the first screen (min screen id)
-                    "profileId = ? AND container = -100 AND cellY = 0 AND screen = " +
-                    "(SELECT MIN(screen) FROM favorites WHERE container = -100)",
-                    new String[]{profileId},
-                    null)) {
-                // First row of first screen is not empty
-                createEmptyRowOnFirstScreen = c.moveToNext();
-            }
-        } else {
-            createEmptyRowOnFirstScreen = false;
-        }
-
-        ArrayList<ContentProviderOperation> insertOperations = new ArrayList<>(BATCH_INSERT_SIZE);
-
-        // Set of package names present in hotseat
-        final HashSet<String> hotseatTargetApps = new HashSet<>();
-        int maxId = 0;
-
-        // Number of imported items on workspace and hotseat
-        int totalItemsOnWorkspace = 0;
-
-        try (Cursor c = mContext.getContentResolver()
-                .query(mOtherFavoritesUri, null,
-                        // Only migrate the primary user
-                        Favorites.PROFILE_ID + " = ?", new String[]{profileId},
-                        // Get the items sorted by container, so that the folders are loaded
-                        // before the corresponding items.
-                        Favorites.CONTAINER + " , " + Favorites.SCREEN)) {
-
-            // various columns we expect to exist.
-            final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
-            final int intentIndex = c.getColumnIndexOrThrow(Favorites.INTENT);
-            final int titleIndex = c.getColumnIndexOrThrow(Favorites.TITLE);
-            final int containerIndex = c.getColumnIndexOrThrow(Favorites.CONTAINER);
-            final int itemTypeIndex = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE);
-            final int widgetProviderIndex = c.getColumnIndexOrThrow(Favorites.APPWIDGET_PROVIDER);
-            final int screenIndex = c.getColumnIndexOrThrow(Favorites.SCREEN);
-            final int cellXIndex = c.getColumnIndexOrThrow(Favorites.CELLX);
-            final int cellYIndex = c.getColumnIndexOrThrow(Favorites.CELLY);
-            final int spanXIndex = c.getColumnIndexOrThrow(Favorites.SPANX);
-            final int spanYIndex = c.getColumnIndexOrThrow(Favorites.SPANY);
-            final int rankIndex = c.getColumnIndexOrThrow(Favorites.RANK);
-            final int iconIndex = c.getColumnIndexOrThrow(Favorites.ICON);
-            final int iconPackageIndex = c.getColumnIndexOrThrow(Favorites.ICON_PACKAGE);
-            final int iconResourceIndex = c.getColumnIndexOrThrow(Favorites.ICON_RESOURCE);
-
-            SparseBooleanArray mValidFolders = new SparseBooleanArray();
-            ContentValues values = new ContentValues();
-
-            Integer firstScreenId = null;
-            while (c.moveToNext()) {
-                values.clear();
-                int id = c.getInt(idIndex);
-                maxId = Math.max(maxId, id);
-                int type = c.getInt(itemTypeIndex);
-                int container = c.getInt(containerIndex);
-
-                int screen = c.getInt(screenIndex);
-
-                int cellX = c.getInt(cellXIndex);
-                int cellY = c.getInt(cellYIndex);
-                int spanX = c.getInt(spanXIndex);
-                int spanY = c.getInt(spanYIndex);
-
-                switch (container) {
-                    case Favorites.CONTAINER_DESKTOP: {
-                        if (screen < Workspace.FIRST_SCREEN_ID) {
-                            FileLog.d(TAG, String.format(
-                                    "Skipping item %d, type %d not on a valid screen %d",
-                                    id, type, screen));
-                            continue;
-                        }
-                        if (firstScreenId == null) {
-                            firstScreenId = screen;
-                        }
-                        // Reset the screen to 0-index value
-                        if (createEmptyRowOnFirstScreen && firstScreenId.equals(screen)) {
-                            // Shift items by 1.
-                            cellY++;
-                            // Change the screen id to first screen
-                            screen = Workspace.FIRST_SCREEN_ID;
-                        }
-
-                        mMaxGridSizeX = Math.max(mMaxGridSizeX, cellX + spanX);
-                        mMaxGridSizeY = Math.max(mMaxGridSizeY, cellY + spanY);
-                        break;
-                    }
-                    case Favorites.CONTAINER_HOTSEAT: {
-                        mHotseatSize = Math.max(mHotseatSize, screen + 1);
-                        break;
-                    }
-                    default:
-                        if (!mValidFolders.get(container)) {
-                            FileLog.d(TAG, String.format("Skipping item %d, type %d not in a valid folder %d", id, type, container));
-                            continue;
-                        }
-                }
-
-                Intent intent = null;
-                switch (type) {
-                    case Favorites.ITEM_TYPE_FOLDER: {
-                        mValidFolders.put(id, true);
-                        // Use a empty intent to indicate a folder.
-                        intent = new Intent();
-                        break;
-                    }
-                    case Favorites.ITEM_TYPE_APPWIDGET: {
-                        values.put(Favorites.RESTORED,
-                                LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
-                                        LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY |
-                                        LauncherAppWidgetInfo.FLAG_UI_NOT_READY);
-                        values.put(Favorites.APPWIDGET_PROVIDER, c.getString(widgetProviderIndex));
-                        break;
-                    }
-                    case Favorites.ITEM_TYPE_SHORTCUT:
-                    case Favorites.ITEM_TYPE_APPLICATION: {
-                        intent = Intent.parseUri(c.getString(intentIndex), 0);
-                        if (PackageManagerHelper.isLauncherAppTarget(intent)) {
-                            type = Favorites.ITEM_TYPE_APPLICATION;
-                        } else {
-                            values.put(Favorites.ICON_PACKAGE, c.getString(iconPackageIndex));
-                            values.put(Favorites.ICON_RESOURCE, c.getString(iconResourceIndex));
-                        }
-                        values.put(Favorites.ICON,  c.getBlob(iconIndex));
-                        values.put(Favorites.INTENT, intent.toUri(0));
-                        values.put(Favorites.RANK, c.getInt(rankIndex));
-
-                        values.put(Favorites.RESTORED, 1);
-                        break;
-                    }
-                    default:
-                        FileLog.d(TAG, String.format("Skipping item %d, not a valid type %d", id, type));
-                        continue;
-                }
-
-                if (container == Favorites.CONTAINER_HOTSEAT) {
-                    if (intent == null) {
-                        FileLog.d(TAG, String.format("Skipping item %d, null intent on hotseat", id));
-                        continue;
-                    }
-                    if (intent.getComponent() != null) {
-                        intent.setPackage(intent.getComponent().getPackageName());
-                    }
-                    hotseatTargetApps.add(getPackage(intent));
-                }
-
-                values.put(Favorites._ID, id);
-                values.put(Favorites.ITEM_TYPE, type);
-                values.put(Favorites.CONTAINER, container);
-                values.put(Favorites.SCREEN, screen);
-                values.put(Favorites.CELLX, cellX);
-                values.put(Favorites.CELLY, cellY);
-                values.put(Favorites.SPANX, spanX);
-                values.put(Favorites.SPANY, spanY);
-                values.put(Favorites.TITLE, c.getString(titleIndex));
-                insertOperations.add(ContentProviderOperation
-                        .newInsert(Favorites.CONTENT_URI).withValues(values).build());
-                if (container < 0) {
-                    totalItemsOnWorkspace++;
-                }
-
-                if (insertOperations.size() >= BATCH_INSERT_SIZE) {
-                    mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY,
-                            insertOperations);
-                    insertOperations.clear();
-                }
-            }
-        }
-        FileLog.d(TAG, totalItemsOnWorkspace + " items imported from external source");
-        if (totalItemsOnWorkspace < MIN_ITEM_COUNT_FOR_SUCCESSFUL_MIGRATION) {
-            throw new Exception("Insufficient data");
-        }
-        if (!insertOperations.isEmpty()) {
-            mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY,
-                    insertOperations);
-            insertOperations.clear();
-        }
-
-        IntSparseArrayMap<Object> hotseatItems = GridSizeMigrationTask
-                .removeBrokenHotseatItems(mContext);
-        int myHotseatCount = LauncherAppState.getIDP(mContext).numDatabaseHotseatIcons;
-        if (hotseatItems.size() < myHotseatCount) {
-            // Insufficient hotseat items. Add a few more.
-            HotseatParserCallback parserCallback = new HotseatParserCallback(
-                    hotseatTargetApps, hotseatItems, insertOperations, maxId + 1, myHotseatCount);
-            new HotseatLayoutParser(mContext,
-                    parserCallback).loadLayout(null, new IntArray());
-            mHotseatSize = hotseatItems.keyAt(hotseatItems.size() - 1) + 1;
-
-            if (!insertOperations.isEmpty()) {
-                mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY,
-                        insertOperations);
-            }
-        }
-    }
-
-    private static String getPackage(Intent intent) {
-        return intent.getComponent() != null ? intent.getComponent().getPackageName()
-            : intent.getPackage();
-    }
-
-    /**
-     * Performs data import if possible.
-     * @return true on successful data import, false if it was not available
-     * @throws Exception if the import failed
-     */
-    public static boolean performImportIfPossible(Context context) throws Exception {
-        SharedPreferences devicePrefs = getDevicePrefs(context);
-        String sourcePackage = devicePrefs.getString(KEY_DATA_IMPORT_SRC_PKG, "");
-        String sourceAuthority = devicePrefs.getString(KEY_DATA_IMPORT_SRC_AUTHORITY, "");
-
-        if (TextUtils.isEmpty(sourcePackage) || TextUtils.isEmpty(sourceAuthority)) {
-            return false;
-        }
-
-        // Synchronously clear the migration flags. This ensures that we do not try migration
-        // again and thus prevents potential crash loops due to migration failure.
-        devicePrefs.edit().remove(KEY_DATA_IMPORT_SRC_PKG).remove(KEY_DATA_IMPORT_SRC_AUTHORITY).commit();
-
-        if (!Settings.call(context.getContentResolver(), Settings.METHOD_WAS_EMPTY_DB_CREATED)
-                .getBoolean(Settings.EXTRA_VALUE, false)) {
-            // Only migration if a new DB was created.
-            return false;
-        }
-
-        for (ProviderInfo info : context.getPackageManager().queryContentProviders(
-                null, context.getApplicationInfo().uid, 0)) {
-
-            if (sourcePackage.equals(info.packageName)) {
-                if ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
-                    // Only migrate if the source launcher is also on system image.
-                    return false;
-                }
-
-                // Wait until we found a provider with matching authority.
-                if (sourceAuthority.equals(info.authority)) {
-                    if (TextUtils.isEmpty(info.readPermission) ||
-                            context.checkPermission(info.readPermission, Process.myPid(),
-                                    Process.myUid()) == PackageManager.PERMISSION_GRANTED) {
-                        // All checks passed, run the import task.
-                        return new ImportDataTask(context, sourceAuthority).importWorkspace();
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Extension of {@link DefaultLayoutParser} which only allows icons and shortcuts.
-     */
-    private static class HotseatLayoutParser extends DefaultLayoutParser {
-        public HotseatLayoutParser(Context context, LayoutParserCallback callback) {
-            super(context, null, callback, context.getResources(),
-                    LauncherAppState.getIDP(context).defaultLayoutId);
-        }
-
-        @Override
-        protected ArrayMap<String, TagParser> getLayoutElementsMap() {
-            // Only allow shortcut parsers
-            ArrayMap<String, TagParser> parsers = new ArrayMap<>();
-            parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
-            parsers.put(TAG_SHORTCUT, new UriShortcutParser(mSourceRes));
-            parsers.put(TAG_RESOLVE, new ResolveParser());
-            return parsers;
-        }
-    }
-
-    /**
-     * {@link LayoutParserCallback} which adds items in empty hotseat spots.
-     */
-    private static class HotseatParserCallback implements LayoutParserCallback {
-        private final HashSet<String> mExistingApps;
-        private final IntSparseArrayMap<Object> mExistingItems;
-        private final ArrayList<ContentProviderOperation> mOutOps;
-        private final int mRequiredSize;
-        private int mStartItemId;
-
-        HotseatParserCallback(
-                HashSet<String> existingApps, IntSparseArrayMap<Object> existingItems,
-                ArrayList<ContentProviderOperation> outOps, int startItemId, int requiredSize) {
-            mExistingApps = existingApps;
-            mExistingItems = existingItems;
-            mOutOps = outOps;
-            mRequiredSize = requiredSize;
-            mStartItemId = startItemId;
-        }
-
-        @Override
-        public int generateNewItemId() {
-            return mStartItemId++;
-        }
-
-        @Override
-        public int insertAndCheck(SQLiteDatabase db, ContentValues values) {
-            if (mExistingItems.size() >= mRequiredSize) {
-                // No need to add more items.
-                return 0;
-            }
-            if (!Integer.valueOf(Favorites.CONTAINER_HOTSEAT)
-                    .equals(values.getAsInteger(Favorites.CONTAINER))) {
-                // Ignore items which are not for hotseat.
-                return 0;
-            }
-
-            Intent intent;
-            try {
-                intent = Intent.parseUri(values.getAsString(Favorites.INTENT), 0);
-            } catch (URISyntaxException e) {
-                return 0;
-            }
-            String pkg = getPackage(intent);
-            if (pkg == null || mExistingApps.contains(pkg)) {
-                // The item does not target an app or is already in hotseat.
-                return 0;
-            }
-            mExistingApps.add(pkg);
-
-            // find next vacant spot.
-            int screen = 0;
-            while (mExistingItems.get(screen) != null) {
-                screen++;
-            }
-            mExistingItems.put(screen, intent);
-            values.put(Favorites.SCREEN, screen);
-            mOutOps.add(ContentProviderOperation.newInsert(Favorites.CONTENT_URI).withValues(values).build());
-            return 0;
-        }
-    }
-}
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
index 7e05a5a..6855bb1 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -16,84 +16,21 @@
 
 package com.android.launcher3.provider;
 
-import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
-import android.database.DatabaseUtils;
 import android.database.sqlite.SQLiteDatabase;
 import android.os.Binder;
 import android.os.Process;
-import android.util.Log;
 
-import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.util.IntArray;
 
-import java.util.Locale;
-
 /**
  * A set of utility methods for Launcher DB used for DB updates and migration.
  */
 public class LauncherDbUtils {
 
-    private static final String TAG = "LauncherDbUtils";
-
-    /**
-     * Makes the first screen as screen 0 (if screen 0 already exists,
-     * renames it to some other number).
-     * If the first row of screen 0 is non empty, runs a 'lossy' GridMigrationTask to clear
-     * the first row. The items in the first screen are moved and resized but the carry-forward
-     * items are simply deleted.
-     */
-    public static boolean prepareScreenZeroToHostQsb(Context context, SQLiteDatabase db) {
-        try (SQLiteTransaction t = new SQLiteTransaction(db)) {
-            // Get the first screen
-            final int firstScreenId;
-            try (Cursor c = db.rawQuery(String.format(Locale.ENGLISH,
-                    "SELECT MIN(%1$s) from %2$s where %3$s = %4$d",
-                    Favorites.SCREEN, Favorites.TABLE_NAME, Favorites.CONTAINER,
-                    Favorites.CONTAINER_DESKTOP), null)) {
-
-                if (!c.moveToNext()) {
-                    // No update needed
-                    t.commit();
-                    return true;
-                }
-
-                firstScreenId = c.getInt(0);
-            }
-
-            if (firstScreenId != 0) {
-                // Rename the first screen to 0.
-                renameScreen(db, firstScreenId, 0);
-            }
-
-            // Check if the first row is empty
-            if (DatabaseUtils.queryNumEntries(db, Favorites.TABLE_NAME,
-                    "container = -100 and screen = 0 and cellY = 0") == 0) {
-                // First row is empty, no need to migrate.
-                t.commit();
-                return true;
-            }
-
-            new LossyScreenMigrationTask(context, LauncherAppState.getIDP(context), db)
-                    .migrateScreen0();
-            t.commit();
-            return true;
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to update workspace size", e);
-            return false;
-        }
-    }
-
-    private static void renameScreen(SQLiteDatabase db, int oldScreen, int newScreen) {
-        String[] whereParams = new String[] { Integer.toString(oldScreen) };
-        ContentValues values = new ContentValues();
-        values.put(Favorites.SCREEN, newScreen);
-        db.update(Favorites.TABLE_NAME, values, "container = -100 and screen = ?", whereParams);
-    }
-
     public static IntArray queryIntArray(SQLiteDatabase db, String tableName, String columnName,
             String selection, String groupBy, String orderBy) {
         IntArray out = new IntArray();
diff --git a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
deleted file mode 100644
index c0111b9..0000000
--- a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2016 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.provider;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Point;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace;
-import com.android.launcher3.model.GridSizeMigrationTask;
-import com.android.launcher3.util.IntSparseArrayMap;
-
-import java.util.ArrayList;
-
-/**
- * An extension of {@link GridSizeMigrationTask} which migrates only one screen and
- * deletes all carry-forward items.
- */
-public class LossyScreenMigrationTask extends GridSizeMigrationTask {
-
-    private final IntSparseArrayMap<DbEntry> mOriginalItems;
-    private final IntSparseArrayMap<DbEntry> mUpdates;
-
-    protected LossyScreenMigrationTask(
-            Context context, InvariantDeviceProfile idp, SQLiteDatabase db) {
-        // Decrease the rows count by 1
-        super(context, db, getValidPackages(context), false /* usePreviewTable */,
-                new Point(idp.numColumns, idp.numRows + 1),
-                new Point(idp.numColumns, idp.numRows));
-
-        mOriginalItems = new IntSparseArrayMap<>();
-        mUpdates = new IntSparseArrayMap<>();
-    }
-
-    @Override
-    protected void update(DbEntry item) {
-        mUpdates.put(item.id, item.copy());
-    }
-
-    @Override
-    protected ArrayList<DbEntry> loadWorkspaceEntries(int screen) {
-        ArrayList<DbEntry> result = super.loadWorkspaceEntries(screen);
-        for (DbEntry entry : result) {
-            mOriginalItems.put(entry.id, entry.copy());
-
-            // Shift all items by 1 in y direction and mark them for update.
-            entry.cellY++;
-            mUpdates.put(entry.id, entry.copy());
-        }
-
-        return result;
-    }
-
-    public void migrateScreen0() {
-        migrateScreen(Workspace.FIRST_SCREEN_ID);
-
-        ContentValues tempValues = new ContentValues();
-        for (DbEntry update : mUpdates) {
-            DbEntry org = mOriginalItems.get(update.id);
-
-            if (org.cellX != update.cellX || org.cellY != update.cellY
-                    || org.spanX != update.spanX || org.spanY != update.spanY) {
-                tempValues.clear();
-                update.addToContentValues(tempValues);
-                mDb.update(Favorites.TABLE_NAME, tempValues, "_id = ?",
-                        new String[] {Integer.toString(update.id)});
-            }
-        }
-
-        // Delete any carry over items as we are only migration a single screen.
-        for (DbEntry entry : mCarryOver) {
-            mEntryToRemove.add(entry.id);
-        }
-
-        if (!mEntryToRemove.isEmpty()) {
-            mDb.delete(Favorites.TABLE_NAME,
-                    Utilities.createDbSelectionQuery(Favorites._ID, mEntryToRemove), null);
-        }
-    }
-}
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 223f4f1..257d732 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.provider;
 
+import static com.android.launcher3.model.DeviceGridState.TYPE_PHONE;
 import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
 
 import android.app.backup.BackupManager;
@@ -38,6 +39,7 @@
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.model.DeviceGridState;
 import com.android.launcher3.model.GridBackupTable;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -57,7 +59,7 @@
 public class RestoreDbTask {
 
     private static final String TAG = "RestoreDbTask";
-    private static final String RESTORE_TASK_PENDING = "restore_task_pending";
+    private static final String RESTORED_DEVICE_TYPE = "restored_task_pending";
 
     private static final String INFO_COLUMN_NAME = "name";
     private static final String INFO_COLUMN_DEFAULT_VALUE = "dflt_value";
@@ -65,13 +67,36 @@
     private static final String APPWIDGET_OLD_IDS = "appwidget_old_ids";
     private static final String APPWIDGET_IDS = "appwidget_ids";
 
-    public static boolean performRestore(Context context, DatabaseHelper helper,
-            BackupManager backupManager) {
+    /**
+     * Tries to restore the backup DB if needed
+     */
+    public static void restoreIfNeeded(Context context, DatabaseHelper helper) {
+        if (!isPending(context)) {
+            return;
+        }
+        if (!performRestore(context, helper)) {
+            helper.createEmptyDB(helper.getWritableDatabase());
+        }
+
+        // Set is pending to false irrespective of the result, so that it doesn't get
+        // executed again.
+        Utilities.getPrefs(context).edit().remove(RESTORED_DEVICE_TYPE).commit();
+
+        InvariantDeviceProfile.INSTANCE.get(context).reinitializeAfterRestore(context);
+    }
+
+    private static boolean performRestore(Context context, DatabaseHelper helper) {
+        if (!DeviceGridState.deviceTypeCompatible(
+                new DeviceGridState(LauncherAppState.getIDP(context)).getDeviceType(),
+                Utilities.getPrefs(context).getInt(RESTORED_DEVICE_TYPE, TYPE_PHONE))) {
+            // DO NOT restore if the device types are incompatible.
+            return false;
+        }
         SQLiteDatabase db = helper.getWritableDatabase();
         try (SQLiteTransaction t = new SQLiteTransaction(db)) {
             RestoreDbTask task = new RestoreDbTask();
             task.backupWorkspace(context, db);
-            task.sanitizeDB(helper, db, backupManager);
+            task.sanitizeDB(helper, db, new BackupManager(context));
             task.restoreAppWidgetIdsIfExists(context);
             t.commit();
             return true;
@@ -279,12 +304,17 @@
     }
 
     public static boolean isPending(Context context) {
-        return Utilities.getPrefs(context).getBoolean(RESTORE_TASK_PENDING, false);
+        return Utilities.getPrefs(context).contains(RESTORED_DEVICE_TYPE);
     }
 
-    public static void setPending(Context context, boolean isPending) {
-        FileLog.d(TAG, "Restore data received through full backup " + isPending);
-        Utilities.getPrefs(context).edit().putBoolean(RESTORE_TASK_PENDING, isPending).commit();
+    /**
+     * Marks the DB state as pending restoration
+     */
+    public static void setPending(Context context) {
+        FileLog.d(TAG, "Restore data received through full backup ");
+        Utilities.getPrefs(context).edit()
+                .putInt(RESTORED_DEVICE_TYPE, new DeviceGridState(context).getDeviceType())
+                .commit();
     }
 
     private void restoreAppWidgetIdsIfExists(Context context) {
diff --git a/src/com/android/launcher3/recyclerview/ViewHolderBinder.java b/src/com/android/launcher3/recyclerview/ViewHolderBinder.java
index 5b8d5bc..31436c4 100644
--- a/src/com/android/launcher3/recyclerview/ViewHolderBinder.java
+++ b/src/com/android/launcher3/recyclerview/ViewHolderBinder.java
@@ -17,8 +17,13 @@
 
 import android.view.ViewGroup;
 
+import androidx.annotation.IntDef;
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
 /**
  * Creates and populates views with data
  *
@@ -26,6 +31,15 @@
  * @param <V> A subclass of {@link ViewHolder} which holds references to views.
  */
 public interface ViewHolderBinder<T, V extends ViewHolder> {
+
+    int POSITION_DEFAULT = 0;
+    int POSITION_FIRST = 1 << 0;
+    int POSITION_LAST = 1 << 1;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {POSITION_DEFAULT, POSITION_FIRST, POSITION_LAST}, flag = true)
+    @interface ListPosition {}
+
     /**
      * Creates a new view, and attach it to the parent {@link ViewGroup}. Then, populates UI
      * references in a {@link ViewHolder}.
@@ -33,7 +47,7 @@
     V newViewHolder(ViewGroup parent);
 
     /** Populate UI references in {@link ViewHolder} with data. */
-    void bindViewHolder(V viewHolder, T data, int position);
+    void bindViewHolder(V viewHolder, T data, @ListPosition int position, List<Object> payloads);
 
     /**
      * Called when the view is recycled. Views are recycled in batches once they are sufficiently
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index 5999091..1a96c23 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -35,22 +35,13 @@
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
-import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.Themes;
-import com.android.launcher3.util.ViewOnDrawExecutor;
 import com.android.launcher3.views.BaseDragLayer;
-import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 
-import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
 
 /**
  * Launcher activity for secondary displays
@@ -175,67 +166,10 @@
     }
 
     @Override
-    public int getPageToBindSynchronously() {
-        return 0;
-    }
-
-    @Override
-    public void clearPendingBinds() { }
-
-    @Override
-    public void startBinding() { }
-
-    @Override
-    public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons) { }
-
-    @Override
-    public void bindScreens(IntArray orderedScreenIds) { }
-
-    @Override
-    public void finishFirstPageBind(ViewOnDrawExecutor executor) {
-        if (executor != null) {
-            executor.onLoadAnimationCompleted();
-        }
-    }
-
-    @Override
-    public void finishBindingItems(int pageBoundFirst) { }
-
-    @Override
-    public void preAddApps() { }
-
-    @Override
-    public void bindAppsAdded(IntArray newScreens, ArrayList<ItemInfo> addNotAnimated,
-            ArrayList<ItemInfo> addAnimated) { }
-
-    @Override
     public void bindIncrementalDownloadProgressUpdated(AppInfo app) {
         mAppsView.getAppsStore().updateProgressBar(app);
     }
 
-    @Override
-    public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) { }
-
-    @Override
-    public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets) { }
-
-    @Override
-    public void bindRestoreItemsChange(HashSet<ItemInfo> updates) { }
-
-    @Override
-    public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher) { }
-
-    @Override
-    public void bindAllWidgets(List<WidgetsListBaseEntry> widgets) { }
-
-    @Override
-    public void onPageBoundSynchronously(int page) { }
-
-    @Override
-    public void executeOnNextDraw(ViewOnDrawExecutor executor) {
-        executor.attachTo(getDragLayer(), false, null);
-    }
-
     /**
      * Called when apps-button is clicked
      */
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
index f78f6dd..1820933 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
@@ -112,7 +112,7 @@
         for (int i = 0; i < count; i++) {
             final View child = getChildAt(i);
             if (child == mAppsView) {
-                int padding = 2 * (grid.desiredWorkspaceLeftRightMarginPx
+                int padding = 2 * (grid.desiredWorkspaceHorizontalMarginPx
                         + grid.cellLayoutPaddingLeftRightPx);
 
                 int maxWidth = grid.allAppsCellWidthPx * grid.numShownAllAppsColumns + padding;
diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
index 4d63218..b06b8a1 100644
--- a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
+++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
@@ -20,6 +20,7 @@
 import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
 
+import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
 import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.PLUGIN_CHANGED;
 import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.pluginEnabledKey;
 
@@ -29,6 +30,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
@@ -44,6 +46,7 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.widget.EditText;
+import android.widget.Toast;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -57,12 +60,15 @@
 import androidx.preference.SwitchPreference;
 
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.config.FlagTogglerPrefUi;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
+import com.android.launcher3.util.OnboardingPrefs;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -104,6 +110,7 @@
         initFlags();
         loadPluginPrefs();
         maybeAddSandboxCategory();
+        addOnboardingPrefsCatergory();
 
         if (getActivity() != null) {
             getActivity().setTitle("Developer Options");
@@ -153,6 +160,15 @@
             }
         });
 
+        if (getArguments() != null) {
+            String filter = getArguments().getString(EXTRA_FRAGMENT_ARG_KEY);
+            // Normally EXTRA_FRAGMENT_ARG_KEY is used to highlight the preference with the given
+            // key. This is a slight variation where we instead filter by the human-readable titles.
+            if (filter != null) {
+                filterBox.setText(filter);
+            }
+        }
+
         View listView = getListView();
         final int bottomPadding = listView.getPaddingBottom();
         listView.setOnApplyWindowInsetsListener((v, insets) -> {
@@ -355,6 +371,28 @@
         sandboxCategory.addPreference(launchSandboxModeTutorialPreference);
     }
 
+    private void addOnboardingPrefsCatergory() {
+        PreferenceCategory onboardingCategory = newCategory("Onboarding Flows");
+        onboardingCategory.setSummary("Reset these if you want to see the education again.");
+        for (Map.Entry<String, String[]> titleAndKeys : OnboardingPrefs.ALL_PREF_KEYS.entrySet()) {
+            String title = titleAndKeys.getKey();
+            String[] keys = titleAndKeys.getValue();
+            Preference onboardingPref = new Preference(getContext());
+            onboardingPref.setTitle(title);
+            onboardingPref.setSummary("Tap to reset");
+            onboardingPref.setOnPreferenceClickListener(preference -> {
+                SharedPreferences.Editor sharedPrefsEdit = Utilities.getPrefs(getContext()).edit();
+                for (String key : keys) {
+                    sharedPrefsEdit.remove(key);
+                }
+                sharedPrefsEdit.apply();
+                Toast.makeText(getContext(), "Reset " + title, Toast.LENGTH_SHORT).show();
+                return true;
+            });
+            onboardingCategory.addPreference(onboardingPref);
+        }
+    }
+
     private String toName(String action) {
         String str = action.replace("com.android.systemui.action.PLUGIN_", "")
                 .replace("com.android.launcher3.action.PLUGIN_", "");
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index 8a35cb3..7a23caa 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -17,11 +17,15 @@
 
 import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
 
+import android.graphics.Insets;
+import android.os.Build;
 import android.os.Handler;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.WindowInsets;
 
 import androidx.annotation.CallSuper;
+import androidx.annotation.RequiresApi;
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.LauncherRootView;
@@ -173,4 +177,12 @@
         mHandler.removeCallbacks(mHandleDeferredResume);
         Utilities.postAsyncCallback(mHandler, mHandleDeferredResume);
     }
+
+    /**
+     * Gives subclasses a chance to override some window insets (via
+     * {@link android.view.WindowInsets.Builder#setInsets(int, Insets)}).
+     */
+    @RequiresApi(api = Build.VERSION_CODES.R)
+    public void updateWindowInsets(WindowInsets.Builder updatedInsetsBuilder,
+            WindowInsets oldInsets) { }
 }
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 8db1dbe..5fe5450 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -59,11 +59,10 @@
 
         float scale = grid.workspaceSpringLoadShrinkFactor;
         Rect insets = launcher.getDragLayer().getInsets();
-        int insetsBottom = grid.isTaskbarPresent ? grid.taskbarSize : insets.bottom;
 
         float scaledHeight = scale * ws.getNormalChildHeight();
         float shrunkTop = insets.top + grid.dropTargetBarSizePx;
-        float shrunkBottom = ws.getMeasuredHeight() - insetsBottom
+        float shrunkBottom = ws.getMeasuredHeight() - insets.bottom
                 - grid.workspacePadding.bottom
                 - grid.workspaceSpringLoadedBottomSpace;
         float totalShrunkSpace = shrunkBottom - shrunkTop;
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 4261d08..5a9c074 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -62,6 +62,10 @@
     }
 
     public Bundle call(String method) {
+        return call(method, /*arg=*/ null);
+    }
+
+    public Bundle call(String method, String arg) {
         final Bundle response = new Bundle();
         switch (method) {
             case TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT: {
@@ -98,12 +102,21 @@
                         l -> WidgetsFullSheet.getWidgetsView(l).getCurrentScrollY());
             }
 
-            case TestProtocol.REQUEST_WINDOW_INSETS: {
-                return getUIProperty(Bundle::putParcelable, a -> {
-                    WindowInsets insets = a.getWindow()
+            case TestProtocol.REQUEST_TARGET_INSETS: {
+                return getUIProperty(Bundle::putParcelable, activity -> {
+                    WindowInsets insets = activity.getWindow()
                             .getDecorView().getRootWindowInsets();
                     return Insets.max(
-                            insets.getSystemGestureInsets(), insets.getSystemWindowInsets());
+                            insets.getSystemGestureInsets(),
+                            insets.getSystemWindowInsets());
+                }, this::getCurrentActivity);
+            }
+
+            case TestProtocol.REQUEST_WINDOW_INSETS: {
+                return getUIProperty(Bundle::putParcelable, activity -> {
+                    WindowInsets insets = activity.getWindow()
+                            .getDecorView().getRootWindowInsets();
+                    return insets.getSystemWindowInsets();
                 }, this::getCurrentActivity);
             }
 
@@ -117,6 +130,24 @@
                 TestProtocol.sDisableSensorRotation = true;
                 return response;
 
+            case TestProtocol.REQUEST_IS_TABLET:
+                response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, mDeviceProfile.isTablet);
+                return response;
+
+            case TestProtocol.REQUEST_IS_TWO_PANELS:
+                response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+                        mDeviceProfile.isTwoPanels);
+                return response;
+
+            case TestProtocol.REQUEST_SET_FORCE_PAUSE_TIMEOUT:
+                TestProtocol.sForcePauseTimeout = Long.parseLong(arg);
+                return response;
+
+            case TestProtocol.REQUEST_GET_HAD_NONTEST_EVENTS:
+                response.putBoolean(
+                        TestProtocol.TEST_INFO_RESPONSE_FIELD, TestLogging.sHadEventsNotFromTest);
+                return response;
+
             default:
                 return null;
         }
diff --git a/src/com/android/launcher3/testing/TestInformationProvider.java b/src/com/android/launcher3/testing/TestInformationProvider.java
index bd177c0..4f2619c 100644
--- a/src/com/android/launcher3/testing/TestInformationProvider.java
+++ b/src/com/android/launcher3/testing/TestInformationProvider.java
@@ -60,7 +60,7 @@
         if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
             TestInformationHandler handler = TestInformationHandler.newInstance(getContext());
             handler.init(getContext());
-            return handler.call(method);
+            return handler.call(method, arg);
         }
         return null;
     }
diff --git a/src/com/android/launcher3/testing/TestLogging.java b/src/com/android/launcher3/testing/TestLogging.java
index 51e0819..103b565 100644
--- a/src/com/android/launcher3/testing/TestLogging.java
+++ b/src/com/android/launcher3/testing/TestLogging.java
@@ -17,6 +17,8 @@
 package com.android.launcher3.testing;
 
 import android.util.Log;
+import android.view.InputEvent;
+import android.view.KeyEvent;
 import android.view.MotionEvent;
 
 import com.android.launcher3.Utilities;
@@ -25,6 +27,7 @@
 
 public final class TestLogging {
     private static BiConsumer<String, String> sEventConsumer;
+    public static boolean sHadEventsNotFromTest;
 
     private static void recordEventSlow(String sequence, String event) {
         Log.d(TestProtocol.TAPL_EVENTS_TAG, sequence + " / " + event);
@@ -46,9 +49,24 @@
         }
     }
 
+    private static void registerEventNotFromTest(InputEvent event) {
+        if (!sHadEventsNotFromTest && event.getDeviceId() != -1) {
+            sHadEventsNotFromTest = true;
+            Log.d(TestProtocol.PERMANENT_DIAG_TAG, "First event not from test: " + event);
+        }
+    }
+
+    public static void recordKeyEvent(String sequence, String message, KeyEvent event) {
+        if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+            recordEventSlow(sequence, message + ": " + event);
+            registerEventNotFromTest(event);
+        }
+    }
+
     public static void recordMotionEvent(String sequence, String message, MotionEvent event) {
         if (Utilities.IS_RUNNING_IN_TEST_HARNESS && event.getAction() != MotionEvent.ACTION_MOVE) {
             recordEventSlow(sequence, message + ": " + event);
+            registerEventNotFromTest(event);
         }
     }
 
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index b6da7fc..5bf0342 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -24,6 +24,7 @@
     public static final String SWITCHED_TO_STATE_MESSAGE = "TAPL_SWITCHED_TO_STATE";
     public static final String SCROLL_FINISHED_MESSAGE = "TAPL_SCROLL_FINISHED";
     public static final String PAUSE_DETECTED_MESSAGE = "TAPL_PAUSE_DETECTED";
+    public static final String DISMISS_ANIMATION_ENDS_MESSAGE = "TAPL_DISMISS_ANIMATION_ENDS";
     public static final int NORMAL_STATE_ORDINAL = 0;
     public static final int SPRING_LOADED_STATE_ORDINAL = 1;
     public static final int OVERVIEW_STATE_ORDINAL = 2;
@@ -85,6 +86,7 @@
     public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags";
     public static final String REQUEST_APPS_LIST_SCROLL_Y = "apps-list-scroll-y";
     public static final String REQUEST_WIDGETS_SCROLL_Y = "widgets-scroll-y";
+    public static final String REQUEST_TARGET_INSETS = "target-insets";
     public static final String REQUEST_WINDOW_INSETS = "window-insets";
     public static final String REQUEST_PID = "pid";
     public static final String REQUEST_FORCE_GC = "gc";
@@ -92,21 +94,32 @@
     public static final String REQUEST_RECENT_TASKS_LIST = "recent-tasks-list";
     public static final String REQUEST_START_EVENT_LOGGING = "start-event-logging";
     public static final String REQUEST_GET_TEST_EVENTS = "get-test-events";
+    public static final String REQUEST_GET_HAD_NONTEST_EVENTS = "get-had-nontest-events";
     public static final String REQUEST_STOP_EVENT_LOGGING = "stop-event-logging";
     public static final String REQUEST_CLEAR_DATA = "clear-data";
+    public static final String REQUEST_IS_TABLET = "is-tablet";
+    public static final String REQUEST_IS_TWO_PANELS = "is-two-panel";
+    public static final String REQUEST_GET_ACTIVITIES_CREATED_COUNT =
+            "get-activities-created-count";
+    public static final String REQUEST_GET_ACTIVITIES = "get-activities";
+    public static final String REQUEST_GET_FOCUSED_TASK_HEIGHT_FOR_TABLET =
+            "get-focused-task-height-for-tablet";
+    public static final String REQUEST_GET_GRID_TASK_SIZE_RECT_FOR_TABLET =
+            "get-grid-task-size-rect-for-tablet";
+
+    public static Long sForcePauseTimeout;
+    public static final String REQUEST_SET_FORCE_PAUSE_TIMEOUT = "set-force-pause-timeout";
 
     public static boolean sDebugTracing = false;
     public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing";
     public static final String REQUEST_DISABLE_DEBUG_TRACING = "disable-debug-tracing";
 
-    public static final String REQUEST_OVERVIEW_SHARE_ENABLED = "overview-share-enabled";
-    public static final String REQUEST_OVERVIEW_CONTENT_PUSH_ENABLED =
-            "overview-content-push-enabled";
 
     public static boolean sDisableSensorRotation;
     public static final String REQUEST_MOCK_SENSOR_ROTATION = "mock-sensor-rotation";
 
     public static final String PERMANENT_DIAG_TAG = "TaplTarget";
-    public static final String WORK_PROFILE_REMOVED = "b/159671700";
-    public static final String FALLBACK_ACTIVITY_NO_SET = "b/181019015";
+    public static final String TASK_VIEW_ID_CRASH = "b/195430732";
+    public static final String NO_DROP_TARGET = "b/195031154";
+    public static final String NULL_INT_SET = "b/200572078";
 }
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 5f8a4d4..61d488c 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -21,7 +21,6 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
-import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
@@ -217,7 +216,7 @@
                     mFlingBlockCheck.blockFling();
                 }
             }
-            if (mToState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
+            if (mToState == LauncherState.ALL_APPS) {
                 mAllAppsOvershootStarted = true;
                 // 1f, value when all apps container hit the top
                 mLauncher.getAppsView().onPull(progress - 1f, progress - 1f);
@@ -333,7 +332,7 @@
         anim.setFloatValues(startProgress, endProgress);
         updateSwipeCompleteAnimation(anim, duration, targetState, velocity, fling);
         mCurrentAnimation.dispatchOnStart();
-        if (targetState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
+        if (targetState == LauncherState.ALL_APPS) {
             if (mAllAppsOvershootStarted) {
                 mLauncher.getAppsView().onRelease();
                 mAllAppsOvershootStarted = false;
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index ab2652a..989a9e4 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -1,5 +1,5 @@
-/**
- * Copyright (C) 2019 The Android Open Source Project
+/*
+ * Copyright (C) 2015 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.
@@ -17,18 +17,31 @@
 
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
 
 import android.view.MotionEvent;
+import android.view.animation.Interpolator;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.states.StateAnimationConfig;
 
 /**
  * TouchController to switch between NORMAL and ALL_APPS state.
  */
 public class AllAppsSwipeController extends AbstractStateChangeTouchController {
 
+    private static final float ALLAPPS_STAGGERED_FADE_THRESHOLD = 0.5f;
+
+    public static final Interpolator ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER =
+            Interpolators.clampToProgress(LINEAR, 0, ALLAPPS_STAGGERED_FADE_THRESHOLD);
+    public static final Interpolator ALLAPPS_STAGGERED_FADE_LATE_RESPONDER =
+            Interpolators.clampToProgress(LINEAR, ALLAPPS_STAGGERED_FADE_THRESHOLD, 1f);
+
     public AllAppsSwipeController(Launcher l) {
         super(l, SingleAxisSwipeDetector.VERTICAL);
     }
@@ -65,12 +78,44 @@
     @Override
     protected float initCurrentAnimation() {
         float range = getShiftRange();
-        long maxAccuracy = (long) (2 * range);
+        StateAnimationConfig config = getConfigForStates(mFromState, mToState);
+        config.duration = (long) (2 * range);
+
         mCurrentAnimation = mLauncher.getStateManager()
-                .createAnimationToNewWorkspace(mToState, maxAccuracy);
+                .createAnimationToNewWorkspace(mToState, config);
         float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range;
         float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range;
         float totalShift = endVerticalShift - startVerticalShift;
         return 1 / totalShift;
     }
+
+    @Override
+    protected StateAnimationConfig getConfigForStates(LauncherState fromState,
+            LauncherState toState) {
+        StateAnimationConfig config = super.getConfigForStates(fromState, toState);
+        if (fromState == NORMAL && toState == ALL_APPS) {
+            applyNormalToAllAppsAnimConfig(config);
+        } else if (fromState == ALL_APPS && toState == NORMAL) {
+            applyAllAppsToNormalConfig(config);
+        }
+        return config;
+    }
+
+    /**
+     * Applies Animation config values for transition from all apps to home
+     */
+    public static void applyAllAppsToNormalConfig(StateAnimationConfig config) {
+        config.setInterpolator(ANIM_SCRIM_FADE, ALLAPPS_STAGGERED_FADE_LATE_RESPONDER);
+        config.setInterpolator(ANIM_ALL_APPS_FADE, ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER);
+    }
+
+    /**
+     * Applies Animation config values for transition from home to all apps
+     */
+    public static void applyNormalToAllAppsAnimConfig(StateAnimationConfig config) {
+        config.setInterpolator(ANIM_SCRIM_FADE, ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER);
+        config.setInterpolator(ANIM_ALL_APPS_FADE, ALLAPPS_STAGGERED_FADE_LATE_RESPONDER);
+    }
+
+
 }
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index b53f96e..5e907a4 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -46,6 +46,8 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.InstanceIdSequence;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.FolderInfo;
@@ -314,6 +316,12 @@
                 intent = new Intent(intent);
                 intent.setPackage(null);
             }
+            if ((si.options & WorkspaceItemInfo.FLAG_START_FOR_RESULT) != 0) {
+                launcher.startActivityForResult(item.getIntent(), 0);
+                InstanceId instanceId = new InstanceIdSequence().newInstanceId();
+                launcher.logAppLaunch(launcher.getStatsLogManager(), item, instanceId);
+                return;
+            }
         }
         if (v != null && launcher.supportsAdaptiveIconAnimation(v)) {
             // Preload the icon to reduce latency b/w swapping the floating view with the original.
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index d047eca..a190f52 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -16,7 +16,12 @@
 
 package com.android.launcher3.touch;
 
-import static android.widget.ListPopupWindow.WRAP_CONTENT;
+import static android.view.Gravity.CENTER_VERTICAL;
+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.LauncherAnimUtils.VIEW_TRANSLATE_X;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
@@ -36,12 +41,16 @@
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
+import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
+import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
 import com.android.launcher3.views.BaseDragLayer;
 
 import java.util.Collections;
@@ -162,16 +171,15 @@
     }
 
     @Override
-    public int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition,
+    public int getSplitTaskViewDismissDirection(@StagePosition int stagePosition,
             DeviceProfile dp) {
         // Don't use device profile here because we know we're in fake landscape, only split option
         // available is top/left
-        if (splitPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+        if (stagePosition == STAGE_POSITION_TOP_OR_LEFT) {
             // Top (visually left) side
             return SPLIT_TRANSLATE_PRIMARY_NEGATIVE;
         }
-        throw new IllegalStateException("Invalid split stage position: " +
-                splitPosition.mStagePosition);
+        throw new IllegalStateException("Invalid split stage position: " + stagePosition);
     }
 
     @Override
@@ -205,13 +213,18 @@
     }
 
     @Override
-    public int getChildStart(View view) {
-        return view.getTop();
+    public void setPrimaryScale(View view, float scale) {
+        view.setScaleY(scale);
     }
 
     @Override
-    public float getChildStartWithTranslation(View view) {
-        return view.getTop() + view.getTranslationY();
+    public void setSecondaryScale(View view, float scale) {
+        view.setScaleX(scale);
+    }
+
+    @Override
+    public int getChildStart(View view) {
+        return view.getTop();
     }
 
     @Override
@@ -230,11 +243,6 @@
         return view.getHeight() - view.getPaddingBottom() - insets.bottom;
     }
 
-    @Override
-    public int getPrimaryTranslationDirectionFactor() {
-        return -1;
-    }
-
     public int getSecondaryTranslationDirectionFactor() {
         return 1;
     }
@@ -249,31 +257,28 @@
     }
 
     @Override
-    public int getSplitAnimationTranslation(int translationOffset, DeviceProfile dp) {
-        return translationOffset;
-    }
-
-    @Override
-    public float getTaskMenuX(float x, View thumbnailView, int overScroll) {
+    public float getTaskMenuX(float x, View thumbnailView, int overScroll,
+            DeviceProfile deviceProfile) {
         return thumbnailView.getMeasuredWidth() + x;
     }
 
     @Override
     public float getTaskMenuY(float y, View thumbnailView, int overScroll) {
-        return y + overScroll;
+        return y + overScroll +
+                (thumbnailView.getMeasuredHeight() - thumbnailView.getMeasuredWidth()) / 2f;
     }
 
     @Override
-    public int getTaskMenuWidth(View view) {
-        return view.getMeasuredHeight();
+    public int getTaskMenuWidth(View view, DeviceProfile deviceProfile) {
+        return view.getMeasuredWidth();
     }
 
     @Override
     public void setTaskOptionsMenuLayoutOrientation(DeviceProfile deviceProfile,
             LinearLayout taskMenuLayout, int dividerSpacing,
             ShapeDrawable dividerDrawable) {
-        taskMenuLayout.setOrientation(LinearLayout.HORIZONTAL);
-        dividerDrawable.setIntrinsicWidth(dividerSpacing);
+        taskMenuLayout.setOrientation(LinearLayout.VERTICAL);
+        dividerDrawable.setIntrinsicHeight(dividerSpacing);
         taskMenuLayout.setDividerDrawable(dividerDrawable);
     }
 
@@ -281,12 +286,9 @@
     public void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp,
             LinearLayout viewGroup, DeviceProfile deviceProfile) {
         // Phone fake landscape
-        viewGroup.setOrientation(LinearLayout.VERTICAL);
-        lp.width = 0;
+        viewGroup.setOrientation(LinearLayout.HORIZONTAL);
+        lp.width = MATCH_PARENT;
         lp.height = WRAP_CONTENT;
-        lp.weight = 1;
-        Utilities.setStartMarginForView(viewGroup.findViewById(R.id.text), 0);
-        Utilities.setStartMarginForView(viewGroup.findViewById(R.id.icon), 0);
     }
 
     @Override
@@ -353,6 +355,105 @@
     }
 
     @Override
+    public void getInitialSplitPlaceholderBounds(int placeholderHeight, DeviceProfile dp,
+            @StagePosition int stagePosition, Rect out) {
+        // In fake land/seascape, the placeholder always needs to go to the "top" of the device,
+        // which is the same bounds as 0 rotation.
+        int width = dp.widthPx;
+        out.set(0, 0, width, placeholderHeight);
+    }
+
+    @Override
+    public void getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp,
+            @StagePosition int stagePosition, Rect out1, Rect out2) {
+        // In fake land/seascape, the window bounds are always top and bottom half
+        int screenHeight = dp.heightPx;
+        int screenWidth = dp.widthPx;
+        out1.set(0, 0, screenWidth, screenHeight / 2  - splitDividerSize);
+        out2.set(0, screenHeight / 2  + splitDividerSize, screenWidth, screenHeight);
+    }
+
+    @Override
+    public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect,
+            StagedSplitBounds splitInfo, int desiredStagePosition) {
+        float diff;
+        float horizontalDividerDiff = splitInfo.visualDividerBounds.width() / 2f;
+        if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
+            diff = outRect.height() * (1f - splitInfo.leftTaskPercent) + horizontalDividerDiff;
+            outRect.bottom -= diff;
+        } else {
+            diff = outRect.height() * splitInfo.leftTaskPercent + horizontalDividerDiff;
+            outRect.top += diff;
+        }
+    }
+
+    @Override
+    public void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot,
+            int parentWidth, int parentHeight,
+            SplitConfigurationOptions.StagedSplitBounds splitBoundsConfig, DeviceProfile dp) {
+        int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
+        int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
+        int dividerBar = splitBoundsConfig.visualDividerBounds.width();
+        int primarySnapshotHeight;
+        int primarySnapshotWidth;
+        int secondarySnapshotHeight;
+        int secondarySnapshotWidth;
+
+        float taskPercent = splitBoundsConfig.appsStackedVertically ?
+                splitBoundsConfig.topTaskPercent : splitBoundsConfig.leftTaskPercent;
+        primarySnapshotWidth = parentWidth;
+        primarySnapshotHeight = (int) (totalThumbnailHeight * taskPercent);
+
+        secondarySnapshotWidth = parentWidth;
+        secondarySnapshotHeight = totalThumbnailHeight - primarySnapshotHeight - dividerBar;
+        secondarySnapshot.setTranslationY(primarySnapshotHeight + spaceAboveSnapshot + dividerBar);
+        primarySnapshot.measure(
+                View.MeasureSpec.makeMeasureSpec(primarySnapshotWidth, View.MeasureSpec.EXACTLY),
+                View.MeasureSpec.makeMeasureSpec(primarySnapshotHeight, View.MeasureSpec.EXACTLY));
+        secondarySnapshot.measure(
+                View.MeasureSpec.makeMeasureSpec(secondarySnapshotWidth, View.MeasureSpec.EXACTLY),
+                View.MeasureSpec.makeMeasureSpec(secondarySnapshotHeight,
+                        View.MeasureSpec.EXACTLY));
+    }
+
+    @Override
+    public void setIconAndSnapshotParams(View iconView, int taskIconMargin, int taskIconHeight,
+            FrameLayout.LayoutParams snapshotParams, boolean isRtl) {
+        FrameLayout.LayoutParams iconParams =
+                (FrameLayout.LayoutParams) iconView.getLayoutParams();
+        iconParams.gravity = (isRtl ? START : END) | CENTER_VERTICAL;
+        iconParams.rightMargin = -taskIconHeight - taskIconMargin / 2;
+        iconParams.leftMargin = 0;
+        iconParams.topMargin = snapshotParams.topMargin / 2;
+    }
+
+    @Override
+    public void setSplitIconParams(View primaryIconView, View secondaryIconView,
+            int taskIconHeight, Rect primarySnapshotBounds, Rect secondarySnapshotBounds,
+            boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig) {
+        FrameLayout.LayoutParams primaryIconParams =
+                (FrameLayout.LayoutParams) primaryIconView.getLayoutParams();
+        FrameLayout.LayoutParams secondaryIconParams =
+                new FrameLayout.LayoutParams(primaryIconParams);
+
+        int primaryHeight = primarySnapshotBounds.height();
+        int secondaryHeight = secondarySnapshotBounds.height();
+        primaryIconParams.gravity = (isRtl ? START : END) | TOP;
+        primaryIconView.setTranslationY((primaryHeight + taskIconHeight) / 2f );
+
+        secondaryIconParams.gravity = (isRtl ? START : END) | TOP;
+        secondaryIconView.setTranslationY(primaryHeight
+                + ((secondaryHeight + taskIconHeight) / 2f));
+        primaryIconView.setLayoutParams(primaryIconParams);
+        secondaryIconView.setLayoutParams(secondaryIconParams);
+    }
+
+    @Override
+    public int getDefaultSplitPosition(DeviceProfile deviceProfile) {
+        throw new IllegalStateException("Default position not available in fake landscape");
+    }
+
+    @Override
     public FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary,
             DeviceProfile deviceProfile) {
         return primary;
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 266e05f..8112afd 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -28,11 +28,14 @@
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
+import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
+import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
 
 import java.util.List;
 
@@ -78,27 +81,26 @@
     FloatProperty<View> getSecondaryViewTranslate();
 
     /**
-     * @param splitPosition The position where the view to be split will go
+     * @param stagePosition The position where the view to be split will go
      * @return {@link #SPLIT_TRANSLATE_*} constants to indicate which direction the
      * dismissal should happen
      */
-    int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition, DeviceProfile dp);
+    int getSplitTaskViewDismissDirection(@StagePosition int stagePosition, DeviceProfile dp);
     int getPrimaryScroll(View view);
     float getPrimaryScale(View view);
     int getChildStart(View view);
-    float getChildStartWithTranslation(View view);
     int getCenterForPage(View view, Rect insets);
     int getScrollOffsetStart(View view, Rect insets);
     int getScrollOffsetEnd(View view, Rect insets);
-    int getPrimaryTranslationDirectionFactor();
     int getSecondaryTranslationDirectionFactor();
     int getSplitTranslationDirectionFactor(@StagePosition int stagePosition);
-    int getSplitAnimationTranslation(int translationOffset, DeviceProfile dp);
     ChildBounds getChildBounds(View child, int childStart, int pageCenter, boolean layoutChild);
     void setMaxScroll(AccessibilityEvent event, int maxScroll);
     boolean getRecentsRtlSetting(Resources resources);
     float getDegreesRotated();
     int getRotation();
+    void setPrimaryScale(View view, float scale);
+    void setSecondaryScale(View view, float scale);
 
     <T> T getPrimaryValue(T x, T y);
     <T> T getSecondaryValue(T x, T y);
@@ -114,11 +116,55 @@
             DeviceProfile deviceProfile);
     int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect);
     List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp);
+    /**
+     * @param splitholderSize height of placeholder view in portrait, width in landscape
+     */
+    void getInitialSplitPlaceholderBounds(int splitholderSize, DeviceProfile dp,
+            @StagePosition int stagePosition, Rect out);
+
+    /**
+     * @param splitDividerSize height of split screen drag handle in portrait, width in landscape
+     * @param stagePosition the split position option (top/left, bottom/right) of the first
+     *                           task selected for entering split
+     * @param out1 the bounds for where the first selected app will be
+     * @param out2 the bounds for where the second selected app will be, complimentary to
+     *             {@param out1} based on {@param initialSplitOption}
+     */
+    void getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp,
+            @StagePosition int stagePosition, Rect out1, Rect out2);
+
+    int getDefaultSplitPosition(DeviceProfile deviceProfile);
+
+    /**
+     * @param outRect This is expected to be the rect that has the dimensions for a non-split,
+     *                fullscreen task in overview. This will directly be modified.
+     * @param desiredStagePosition Which stage position (topLeft/rightBottom) we want to resize
+     *                           outRect for
+     */
+    void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect,
+            StagedSplitBounds splitInfo,
+            @SplitConfigurationOptions.StagePosition int desiredStagePosition);
+
+    void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot,
+            int parentWidth, int parentHeight,
+            SplitConfigurationOptions.StagedSplitBounds splitBoundsConfig, DeviceProfile dp);
 
     // Overview TaskMenuView methods
-    float getTaskMenuX(float x, View thumbnailView, int overScroll);
+    void setIconAndSnapshotParams(View iconView, int taskIconMargin, int taskIconHeight,
+            FrameLayout.LayoutParams snapshotParams, boolean isRtl);
+    void setSplitIconParams(View primaryIconView, View secondaryIconView,
+            int taskIconHeight, Rect primarySnapshotBounds, Rect secondarySnapshotBounds,
+            boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig);
+
+    /*
+     * The following two methods try to center the TaskMenuView in landscape by finding the center
+     * of the thumbnail view and then subtracting half of the taskMenu width. In this case, the
+     * taskMenu width is the same size as the thumbnail width (what got set below in
+     * getTaskMenuWidth()), so we directly use that in the calculations.
+     */
+    float getTaskMenuX(float x, View thumbnailView, int overScroll, DeviceProfile deviceProfile);
     float getTaskMenuY(float y, View thumbnailView, int overScroll);
-    int getTaskMenuWidth(View view);
+    int getTaskMenuWidth(View view, DeviceProfile deviceProfile);
     /**
      * Sets linear layout orientation for {@link com.android.launcher3.popup.SystemShortcut} items
      * inside task menu view.
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index dd97af5..576c6f5 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -16,6 +16,12 @@
 
 package com.android.launcher3.touch;
 
+import static android.view.Gravity.BOTTOM;
+import static android.view.Gravity.CENTER_HORIZONTAL;
+import static android.view.Gravity.START;
+import static android.view.Gravity.TOP;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
 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;
@@ -24,6 +30,7 @@
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
 
 import android.content.res.Resources;
+import android.graphics.Matrix;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
@@ -34,12 +41,16 @@
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
+import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
+import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
 import com.android.launcher3.views.BaseDragLayer;
 
 import java.util.ArrayList;
@@ -47,6 +58,9 @@
 
 public class PortraitPagedViewHandler implements PagedOrientationHandler {
 
+    private final Matrix mTmpMatrix = new Matrix();
+    private final RectF mTmpRectF = new RectF();
+
     @Override
     public <T> T getPrimaryValue(T x, T y) {
         return x;
@@ -158,9 +172,9 @@
     }
 
     @Override
-    public int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition,
+    public int getSplitTaskViewDismissDirection(@StagePosition int stagePosition,
             DeviceProfile dp) {
-        if (splitPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+        if (stagePosition == STAGE_POSITION_TOP_OR_LEFT) {
             if (dp.isLandscape) {
                 // Left side
                 return SPLIT_TRANSLATE_PRIMARY_NEGATIVE;
@@ -168,12 +182,11 @@
                 // Top side
                 return SPLIT_TRANSLATE_SECONDARY_NEGATIVE;
             }
-        } else if (splitPosition.mStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
+        } else if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
             // We don't have a bottom option, so should be right
             return SPLIT_TRANSLATE_PRIMARY_POSITIVE;
         }
-        throw new IllegalStateException("Invalid split stage position: " +
-                splitPosition.mStagePosition);
+        throw new IllegalStateException("Invalid split stage position: " + stagePosition);
     }
 
     @Override
@@ -207,13 +220,18 @@
     }
 
     @Override
-    public int getChildStart(View view) {
-        return view.getLeft();
+    public void setPrimaryScale(View view, float scale) {
+        view.setScaleX(scale);
     }
 
     @Override
-    public float getChildStartWithTranslation(View view) {
-        return view.getLeft() + view.getTranslationX();
+    public void setSecondaryScale(View view, float scale) {
+        view.setScaleY(scale);
+    }
+
+    @Override
+    public int getChildStart(View view) {
+        return view.getLeft();
     }
 
     @Override
@@ -232,11 +250,6 @@
         return view.getWidth() - view.getPaddingRight() - insets.right;
     }
 
-    @Override
-    public int getPrimaryTranslationDirectionFactor() {
-        return 1;
-    }
-
     public int getSecondaryTranslationDirectionFactor() {
         return -1;
     }
@@ -251,16 +264,14 @@
     }
 
     @Override
-    public int getSplitAnimationTranslation(int translationOffset, DeviceProfile dp) {
-        if (dp.isLandscape) {
-            return translationOffset;
+    public float getTaskMenuX(float x, View thumbnailView, int overScroll,
+            DeviceProfile deviceProfile) {
+        if (deviceProfile.isLandscape) {
+            return x + overScroll
+                    + (thumbnailView.getMeasuredWidth() - thumbnailView.getMeasuredHeight()) / 2f;
+        } else {
+            return x + overScroll;
         }
-        return 0;
-    }
-
-    @Override
-    public float getTaskMenuX(float x, View thumbnailView, int overScroll) {
-        return x + overScroll;
     }
 
     @Override
@@ -269,43 +280,27 @@
     }
 
     @Override
-    public int getTaskMenuWidth(View view) {
-        return view.getMeasuredWidth();
+    public int getTaskMenuWidth(View view, DeviceProfile deviceProfile) {
+        return deviceProfile.isLandscape && !deviceProfile.overviewShowAsGrid ?
+                view.getMeasuredHeight() :
+                view.getMeasuredWidth();
     }
 
     @Override
     public void setTaskOptionsMenuLayoutOrientation(DeviceProfile deviceProfile,
             LinearLayout taskMenuLayout, int dividerSpacing,
             ShapeDrawable dividerDrawable) {
-        if (deviceProfile.isLandscape && !deviceProfile.isTablet) {
-            // Phone landscape
-            taskMenuLayout.setOrientation(LinearLayout.HORIZONTAL);
-            dividerDrawable.setIntrinsicWidth(dividerSpacing);
-        } else {
-            // Phone Portrait, LargeScreen Landscape/Portrait
-            taskMenuLayout.setOrientation(LinearLayout.VERTICAL);
-            dividerDrawable.setIntrinsicHeight(dividerSpacing);
-        }
+        taskMenuLayout.setOrientation(LinearLayout.VERTICAL);
+        dividerDrawable.setIntrinsicHeight(dividerSpacing);
         taskMenuLayout.setDividerDrawable(dividerDrawable);
     }
 
     @Override
     public void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp,
             LinearLayout viewGroup, DeviceProfile deviceProfile) {
-        if (deviceProfile.isLandscape && !deviceProfile.isTablet) {
-            // Phone landscape
-            viewGroup.setOrientation(LinearLayout.VERTICAL);
-            lp.width = 0;
-            lp.weight = 1;
-            Utilities.setStartMarginForView(viewGroup.findViewById(R.id.text), 0);
-            Utilities.setStartMarginForView(viewGroup.findViewById(R.id.icon), 0);
-        } else {
-            // Phone Portrait, LargeScreen Landscape/Portrait
-            viewGroup.setOrientation(LinearLayout.HORIZONTAL);
-            lp.width = LinearLayout.LayoutParams.MATCH_PARENT;
-        }
-
-        lp.height = LinearLayout.LayoutParams.WRAP_CONTENT;
+        viewGroup.setOrientation(LinearLayout.HORIZONTAL);
+        lp.width = LinearLayout.LayoutParams.MATCH_PARENT;
+        lp.height = WRAP_CONTENT;
     }
 
     @Override
@@ -398,6 +393,183 @@
     }
 
     @Override
+    public void getInitialSplitPlaceholderBounds(int placeholderHeight, DeviceProfile dp,
+            @StagePosition int stagePosition, Rect out) {
+        int width = dp.widthPx;
+        out.set(0, 0, width, placeholderHeight);
+        if (!dp.isLandscape) {
+            // portrait, phone or tablet - spans width of screen, nothing else to do
+            return;
+        }
+
+        // Now we rotate the portrait rect depending on what side we want pinned
+        boolean pinToRight = stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
+
+        int screenHeight = dp.heightPx;
+        float postRotateScale = (float) screenHeight / width;
+        mTmpMatrix.reset();
+        mTmpMatrix.postRotate(pinToRight ? 90 : 270);
+        mTmpMatrix.postTranslate(pinToRight ? width : 0, pinToRight ? 0 : width);
+        // 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.roundOut(out);
+    }
+
+    @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.isLandscape) {
+            // 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,
+            StagedSplitBounds splitInfo, int desiredStagePosition) {
+        boolean isLandscape = dp.isLandscape;
+        float verticalDividerDiff = splitInfo.visualDividerBounds.height() / 2f;
+        float horizontalDividerDiff = splitInfo.visualDividerBounds.width() / 2f;
+        float diff;
+        if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
+            if (isLandscape) {
+                diff = outRect.width() * (1f - splitInfo.leftTaskPercent) + horizontalDividerDiff;
+                outRect.right -= diff;
+            } else {
+                diff = outRect.height() * (1f - splitInfo.topTaskPercent) + verticalDividerDiff;
+                outRect.bottom -= diff;
+            }
+        } else {
+            if (isLandscape) {
+                diff = outRect.width() * splitInfo.leftTaskPercent + horizontalDividerDiff;
+                outRect.left += diff;
+            } else {
+                diff = outRect.height() * splitInfo.topTaskPercent + verticalDividerDiff;
+                outRect.top += diff;
+            }
+        }
+    }
+
+    @Override
+    public void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot,
+            int parentWidth, int parentHeight,
+            SplitConfigurationOptions.StagedSplitBounds splitBoundsConfig, DeviceProfile dp) {
+        int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
+        int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
+        int dividerBar = (splitBoundsConfig.appsStackedVertically ?
+                splitBoundsConfig.visualDividerBounds.height() :
+                splitBoundsConfig.visualDividerBounds.width());
+        int primarySnapshotHeight;
+        int primarySnapshotWidth;
+        int secondarySnapshotHeight;
+        int secondarySnapshotWidth;
+        float taskPercent = splitBoundsConfig.appsStackedVertically ?
+                splitBoundsConfig.topTaskPercent : splitBoundsConfig.leftTaskPercent;
+        if (dp.isLandscape) {
+            primarySnapshotHeight = totalThumbnailHeight;
+            primarySnapshotWidth = (int) (parentWidth * taskPercent);
+
+            secondarySnapshotHeight = totalThumbnailHeight;
+            secondarySnapshotWidth = parentWidth - primarySnapshotWidth - dividerBar;
+            int translationX = primarySnapshotWidth + dividerBar;
+            secondarySnapshot.setTranslationX(translationX);
+            secondarySnapshot.setTranslationY(spaceAboveSnapshot);
+        } else {
+            primarySnapshotWidth = parentWidth;
+            primarySnapshotHeight = (int) (totalThumbnailHeight * taskPercent);
+
+            secondarySnapshotWidth = parentWidth;
+            secondarySnapshotHeight = totalThumbnailHeight - primarySnapshotHeight - dividerBar;
+            int translationY = primarySnapshotHeight + spaceAboveSnapshot + dividerBar;
+            secondarySnapshot.setTranslationY(translationY);
+            secondarySnapshot.setTranslationX(0);
+        }
+        primarySnapshot.measure(
+                View.MeasureSpec.makeMeasureSpec(primarySnapshotWidth, View.MeasureSpec.EXACTLY),
+                View.MeasureSpec.makeMeasureSpec(primarySnapshotHeight, View.MeasureSpec.EXACTLY));
+        secondarySnapshot.measure(
+                View.MeasureSpec.makeMeasureSpec(secondarySnapshotWidth, View.MeasureSpec.EXACTLY),
+                View.MeasureSpec.makeMeasureSpec(secondarySnapshotHeight,
+                        View.MeasureSpec.EXACTLY));
+    }
+
+    @Override
+    public void setIconAndSnapshotParams(View iconView, int taskIconMargin, int taskIconHeight,
+            FrameLayout.LayoutParams snapshotParams, boolean isRtl) {
+        FrameLayout.LayoutParams iconParams =
+                (FrameLayout.LayoutParams) iconView.getLayoutParams();
+        iconParams.gravity = TOP | CENTER_HORIZONTAL;
+        iconParams.leftMargin = iconParams.rightMargin = 0;
+        iconParams.topMargin = taskIconMargin;
+    }
+
+    @Override
+    public void setSplitIconParams(View primaryIconView, View secondaryIconView,
+            int taskIconHeight, Rect primarySnapshotBounds, Rect secondarySnapshotBounds,
+            boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig) {
+        FrameLayout.LayoutParams primaryIconParams =
+                (FrameLayout.LayoutParams) primaryIconView.getLayoutParams();
+        FrameLayout.LayoutParams secondaryIconParams =
+                new FrameLayout.LayoutParams(primaryIconParams);
+
+        if (deviceProfile.isLandscape) {
+            int primaryWidth = primarySnapshotBounds.width();
+            int secondaryWidth = secondarySnapshotBounds.width();
+            primaryIconParams.gravity = TOP | START;
+            primaryIconView.setTranslationX((primaryWidth - taskIconHeight) / 2f );
+
+            secondaryIconParams.gravity = TOP | START;
+            secondaryIconView.setTranslationX(primaryWidth
+                    + ((secondaryWidth - taskIconHeight) / 2f));
+        } else {
+            primaryIconView.setTranslationX(0);
+            secondaryIconView.setTranslationX(0);
+            primaryIconView.setTranslationY(0);
+            secondaryIconView.setTranslationY(0);
+            secondaryIconParams.gravity = BOTTOM | CENTER_HORIZONTAL;
+            secondaryIconParams.bottomMargin = -(secondaryIconParams.topMargin + taskIconHeight);
+        }
+        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.isLandscape) {
+            return STAGE_POSITION_BOTTOM_OR_RIGHT;
+        } else {
+            return STAGE_POSITION_TOP_OR_LEFT;
+        }
+    }
+
+    @Override
     public FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary,
             DeviceProfile dp) {
         if (dp.isLandscape) { // or seascape
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 91d44bd..d5851c8 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -16,6 +16,10 @@
 
 package com.android.launcher3.touch;
 
+import static android.view.Gravity.CENTER_VERTICAL;
+import static android.view.Gravity.END;
+import static android.view.Gravity.START;
+
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
@@ -26,6 +30,7 @@
 import android.graphics.Rect;
 import android.view.Surface;
 import android.view.View;
+import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 
 import com.android.launcher3.DeviceProfile;
@@ -54,11 +59,6 @@
     }
 
     @Override
-    public int getSplitAnimationTranslation(int translationOffset, DeviceProfile dp) {
-        return translationOffset;
-    }
-
-    @Override
     public boolean getRecentsRtlSetting(Resources resources) {
         return Utilities.isRtl(resources);
     }
@@ -81,13 +81,15 @@
     }
 
     @Override
-    public float getTaskMenuX(float x, View thumbnailView, int overScroll) {
+    public float getTaskMenuX(float x, View thumbnailView, int overScroll,
+            DeviceProfile deviceProfile) {
         return x;
     }
 
     @Override
     public float getTaskMenuY(float y, View thumbnailView, int overScroll) {
-        return y + thumbnailView.getMeasuredHeight() + overScroll;
+        return y + overScroll +
+                (thumbnailView.getMeasuredHeight() + thumbnailView.getMeasuredWidth()) / 2f;
     }
 
     @Override
@@ -114,6 +116,17 @@
                 STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
     }
 
+    @Override
+    public void setIconAndSnapshotParams(View mIconView, int taskIconMargin, int taskIconHeight,
+            FrameLayout.LayoutParams snapshotParams, boolean isRtl) {
+        FrameLayout.LayoutParams iconParams =
+                (FrameLayout.LayoutParams) mIconView.getLayoutParams();
+        iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL;
+        iconParams.leftMargin = -taskIconHeight - taskIconMargin / 2;
+        iconParams.rightMargin = 0;
+        iconParams.topMargin = snapshotParams.topMargin / 2;
+    }
+
     /* ---------- The following are only used by TaskViewTouchHandler. ---------- */
 
     @Override
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index 4fa658e..20d2ad3 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -41,7 +41,6 @@
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.views.OptionsPopupView;
 
 /**
  * Helper class to handle touch on empty space in workspace and show options popup on long press
@@ -175,7 +174,7 @@
                 mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
                         HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
                 mLauncher.getStatsLogManager().logger().log(LAUNCHER_WORKSPACE_LONGPRESS);
-                OptionsPopupView.showDefaultOptions(mLauncher, mTouchDownPoint.x, mTouchDownPoint.y);
+                mLauncher.showDefaultOptions(mTouchDownPoint.x, mTouchDownPoint.y);
             } else {
                 cancelLongPress();
             }
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index e2c0a32..2068c29 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -23,6 +23,8 @@
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH;
 
+import static java.util.Collections.emptyMap;
+
 import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.content.ComponentCallbacks;
@@ -34,10 +36,11 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.os.Build;
+import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
 import android.view.Display;
-import android.view.WindowMetrics;
 
 import androidx.annotation.AnyThread;
 import androidx.annotation.UiThread;
@@ -47,7 +50,7 @@
 import com.android.launcher3.uioverrides.ApiWrapper;
 
 import java.util.ArrayList;
-import java.util.Collections;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
@@ -55,7 +58,7 @@
  * Utility class to cache properties of default display to avoid a system RPC on every call.
  */
 @SuppressLint("NewApi")
-public class DisplayController implements DisplayListener, ComponentCallbacks {
+public class DisplayController implements DisplayListener, ComponentCallbacks, SafeCloseable {
 
     private static final String TAG = "DisplayController";
 
@@ -76,9 +79,12 @@
 
     // Null for SDK < S
     private final Context mWindowContext;
-
+    // The callback in this listener updates DeviceProfile, which other listeners might depend on
+    private DisplayInfoChangeListener mPriorityListener;
     private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
+
     private Info mInfo;
+    private boolean mDestroyed = false;
 
     private DisplayController(Context context) {
         mContext = context;
@@ -95,19 +101,35 @@
             mContext.registerReceiver(configChangeReceiver,
                     new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
         }
+        mInfo = new Info(getDisplayInfoContext(display), display,
+                getInternalDisplays(mDM), emptyMap());
+        mDM.registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler());
+    }
 
-        // Create a single holder for all internal displays. External display holders created
-        // lazily.
-        Set<PortraitSize> extraInternalDisplays = new ArraySet<>();
-        for (Display d : mDM.getDisplays()) {
-            if (ApiWrapper.isInternalDisplay(display) && d.getDisplayId() != DEFAULT_DISPLAY) {
+    private static ArrayMap<String, PortraitSize> getInternalDisplays(
+            DisplayManager displayManager) {
+        Display[] displays = displayManager.getDisplays();
+        ArrayMap<String, PortraitSize> internalDisplays = new ArrayMap<>();
+        for (Display display : displays) {
+            if (ApiWrapper.isInternalDisplay(display)) {
                 Point size = new Point();
-                d.getRealSize(size);
-                extraInternalDisplays.add(new PortraitSize(size.x, size.y));
+                display.getRealSize(size);
+                internalDisplays.put(ApiWrapper.getUniqueId(display),
+                        new PortraitSize(size.x, size.y));
             }
         }
-        mInfo = new Info(getDisplayInfoContext(display), display, extraInternalDisplays);
-        mDM.registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler());
+        return internalDisplays;
+    }
+
+    @Override
+    public void close() {
+        mDestroyed = true;
+        if (mWindowContext != null) {
+            mWindowContext.unregisterComponentCallbacks(this);
+        } else {
+            // TODO: unregister broadcast receiver
+        }
+        mDM.unregisterDisplayListener(this);
     }
 
     @Override
@@ -157,6 +179,9 @@
      * Only used for pre-S
      */
     private void onConfigChanged(Intent intent) {
+        if (mDestroyed) {
+            return;
+        }
         Configuration config = mContext.getResources().getConfiguration();
         if (mInfo.fontScale != config.fontScale || mInfo.densityDpi != config.densityDpi) {
             Log.d(TAG, "Configuration changed, notifying listeners");
@@ -184,6 +209,10 @@
     @Override
     public final void onLowMemory() { }
 
+    public void setPriorityListener(DisplayInfoChangeListener listener) {
+        mPriorityListener = listener;
+    }
+
     public void addChangeListener(DisplayInfoChangeListener listener) {
         mListeners.add(listener);
     }
@@ -203,13 +232,18 @@
     @AnyThread
     private void handleInfoChange(Display display) {
         Info oldInfo = mInfo;
-        Set<PortraitSize> extraDisplaysSizes = oldInfo.mAllSizes.size() > 1
-                ? oldInfo.mAllSizes : Collections.emptySet();
 
         Context displayContext = getDisplayInfoContext(display);
-        Info newInfo = new Info(displayContext, display, extraDisplaysSizes);
+        Info newInfo = new Info(displayContext, display,
+                oldInfo.mInternalDisplays, oldInfo.mPerDisplayBounds);
+
+        if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale) {
+            // Cache may not be valid anymore, recreate without cache
+            newInfo = new Info(displayContext, display, getInternalDisplays(mDM), emptyMap());
+        }
+
         int change = 0;
-        if (!newInfo.mScreenSizeDp.equals(oldInfo.mScreenSizeDp)) {
+        if (!newInfo.displayId.equals(oldInfo.displayId)) {
             change |= CHANGE_ACTIVE_SCREEN;
         }
         if (newInfo.rotation != oldInfo.rotation) {
@@ -223,6 +257,14 @@
         }
         if (!newInfo.supportedBounds.equals(oldInfo.supportedBounds)) {
             change |= CHANGE_SUPPORTED_BOUNDS;
+
+            PortraitSize realSize = new PortraitSize(newInfo.currentSize.x, newInfo.currentSize.y);
+            PortraitSize expectedSize = oldInfo.mInternalDisplays.get(
+                    ApiWrapper.getUniqueId(display));
+            if (!realSize.equals(expectedSize) && display.getState() == Display.STATE_OFF) {
+                Log.e("b/198965093", "Display size changed while display is off, ignoring change");
+                return;
+            }
         }
 
         if (change != 0) {
@@ -233,6 +275,9 @@
     }
 
     private void notifyChange(Context context, int flags) {
+        if (mPriorityListener != null) {
+            mPriorityListener.onDisplayInfoChanged(context, mInfo, flags);
+        }
         for (int i = mListeners.size() - 1; i >= 0; i--) {
             mListeners.get(i).onDisplayInfoChanged(context, mInfo, flags);
         }
@@ -240,7 +285,6 @@
 
     public static class Info {
 
-        public final int id;
         public final int singleFrameMs;
 
         // Configuration properties
@@ -249,19 +293,22 @@
         public final int densityDpi;
 
         private final PortraitSize mScreenSizeDp;
-        private final Set<PortraitSize> mAllSizes;
 
         public final Point currentSize;
 
+        public String displayId;
         public final Set<WindowBounds> supportedBounds = new ArraySet<>();
+        private final Map<String, Set<WindowBounds>> mPerDisplayBounds = new ArrayMap<>();
+        private final ArrayMap<String, PortraitSize> mInternalDisplays;
 
         public Info(Context context, Display display) {
-            this(context, display, Collections.emptySet());
+            this(context, display, new ArrayMap<>(), emptyMap());
         }
 
-        private Info(Context context, Display display, Set<PortraitSize> extraDisplaysSizes) {
-            id = display.getDisplayId();
-
+        private Info(Context context, Display display,
+                ArrayMap<String, PortraitSize> internalDisplays,
+                Map<String, Set<WindowBounds>> perDisplayBoundsCache) {
+            mInternalDisplays = internalDisplays;
             rotation = display.getRotation();
 
             Configuration config = context.getResources().getConfiguration();
@@ -271,32 +318,51 @@
 
             singleFrameMs = getSingleFrameMs(display);
             currentSize = new Point();
-
             display.getRealSize(currentSize);
 
-            if (extraDisplaysSizes.isEmpty() || !Utilities.ATLEAST_S) {
-                Point smallestSize = new Point();
-                Point largestSize = new Point();
-                display.getCurrentSizeRange(smallestSize, largestSize);
+            displayId = ApiWrapper.getUniqueId(display);
+            Set<WindowBounds> currentSupportedBounds =
+                    getSupportedBoundsForDisplay(display, currentSize);
+            mPerDisplayBounds.put(displayId, currentSupportedBounds);
+            supportedBounds.addAll(currentSupportedBounds);
 
-                int portraitWidth = Math.min(currentSize.x, currentSize.y);
-                int portraitHeight = Math.max(currentSize.x, currentSize.y);
+            if (ApiWrapper.isInternalDisplay(display) && internalDisplays.size() > 1) {
+                int displayCount = internalDisplays.size();
+                for (int i = 0; i < displayCount; i++) {
+                    String displayKey = internalDisplays.keyAt(i);
+                    if (TextUtils.equals(displayId, displayKey)) {
+                        continue;
+                    }
 
-                supportedBounds.add(new WindowBounds(portraitWidth, portraitHeight,
-                        smallestSize.x, largestSize.y));
-                supportedBounds.add(new WindowBounds(portraitHeight, portraitWidth,
-                        largestSize.x, smallestSize.y));
-                mAllSizes = Collections.singleton(new PortraitSize(currentSize.x, currentSize.y));
-            } else {
-                mAllSizes = new ArraySet<>(extraDisplaysSizes);
-                mAllSizes.add(new PortraitSize(currentSize.x, currentSize.y));
-                Set<WindowMetrics> metrics = WindowManagerCompat.getDisplayProfiles(
-                        context, mAllSizes, densityDpi,
-                        ApiWrapper.TASKBAR_DRAWN_IN_PROCESS);
-                metrics.forEach(wm -> supportedBounds.add(WindowBounds.fromWindowMetrics(wm)));
+                    Set<WindowBounds> displayBounds = perDisplayBoundsCache.get(displayKey);
+                    if (displayBounds == null) {
+                        // We assume densityDpi is the same across all internal displays
+                        displayBounds = WindowManagerCompat.estimateDisplayProfiles(
+                                context, internalDisplays.valueAt(i), densityDpi,
+                                ApiWrapper.TASKBAR_DRAWN_IN_PROCESS);
+                    }
+
+                    supportedBounds.addAll(displayBounds);
+                    mPerDisplayBounds.put(displayKey, displayBounds);
+                }
             }
         }
 
+        private static Set<WindowBounds> getSupportedBoundsForDisplay(Display display, Point size) {
+            Point smallestSize = new Point();
+            Point largestSize = new Point();
+            display.getCurrentSizeRange(smallestSize, largestSize);
+
+            int portraitWidth = Math.min(size.x, size.y);
+            int portraitHeight = Math.max(size.x, size.y);
+            Set<WindowBounds> result = new ArraySet<>();
+            result.add(new WindowBounds(portraitWidth, portraitHeight,
+                    smallestSize.x, largestSize.y));
+            result.add(new WindowBounds(portraitHeight, portraitWidth,
+                    largestSize.x, smallestSize.y));
+            return result;
+        }
+
         /**
          * Returns true if the bounds represent a tablet
          */
diff --git a/src/com/android/launcher3/util/FlingAnimation.java b/src/com/android/launcher3/util/FlingAnimation.java
index c9aa51c..ac864e9 100644
--- a/src/com/android/launcher3/util/FlingAnimation.java
+++ b/src/com/android/launcher3/util/FlingAnimation.java
@@ -1,12 +1,14 @@
 package com.android.launcher3.util;
 
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
 
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.view.animation.AnimationUtils;
 import android.view.animation.DecelerateInterpolator;
 
@@ -35,7 +37,7 @@
     protected final float mUX, mUY;
 
     protected Rect mIconRect;
-    protected Rect mFrom;
+    protected RectF mFrom;
     protected int mDuration;
     protected float mAnimationTimeFraction;
 
@@ -55,17 +57,17 @@
     @Override
     public void run() {
         mIconRect = mDropTarget.getIconRect(mDragObject);
+        mDragObject.dragView.cancelAnimation();
+        mDragObject.dragView.requestLayout();
 
         // Initiate from
-        mFrom = new Rect();
-        mDragLayer.getViewRectRelativeToSelf(mDragObject.dragView, mFrom);
-        float scale = mDragObject.dragView.getScaleX();
-        float xOffset = ((scale - 1f) * mDragObject.dragView.getMeasuredWidth()) / 2f;
-        float yOffset = ((scale - 1f) * mDragObject.dragView.getMeasuredHeight()) / 2f;
-        mFrom.left += xOffset;
-        mFrom.right -= xOffset;
-        mFrom.top += yOffset;
-        mFrom.bottom -= yOffset;
+        Rect from = new Rect();
+        mDragLayer.getViewRectRelativeToSelf(mDragObject.dragView, from);
+
+        mFrom = new RectF(from);
+        mFrom.inset(
+                ((1 - mDragObject.dragView.getScaleX()) * from.width()) / 2f,
+                ((1 - mDragObject.dragView.getScaleY()) * from.height()) / 2f);
         mDuration = Math.abs(mUY) > Math.abs(mUX) ? initFlingUpDuration() : initFlingLeftDuration();
 
         mAnimationTimeFraction = ((float) mDuration) / (mDuration + DRAG_END_DELAY);
@@ -95,17 +97,15 @@
             }
         };
 
-        Runnable onAnimationEndRunnable = new Runnable() {
-            @Override
-            public void run() {
-                mLauncher.getStateManager().goToState(NORMAL);
-                mDropTarget.completeDrop(mDragObject);
-            }
-        };
-
         mDropTarget.onDrop(mDragObject, mDragOptions);
-        mDragLayer.animateView(mDragObject.dragView, this, duration, tInterpolator,
-                onAnimationEndRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null);
+        ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+        anim.setDuration(duration).setInterpolator(tInterpolator);
+        anim.addUpdateListener(this);
+        anim.addListener(forEndCallback(() -> {
+            mLauncher.getStateManager().goToState(NORMAL);
+            mDropTarget.completeDrop(mDragObject);
+        }));
+        mDragLayer.playDropAnimation(mDragObject.dragView, anim, DragLayer.ANIMATION_END_DISAPPEAR);
     }
 
     /**
@@ -129,7 +129,7 @@
         }
         double t = (-mUY - Math.sqrt(d)) / mAY;
 
-        float sX = -mFrom.exactCenterX() + mIconRect.exactCenterX();
+        float sX = -mFrom.centerX() + mIconRect.exactCenterX();
 
         // Find horizontal acceleration such that: u*t + a*t*t/2 = s
         mAX = (float) ((sX - t * mUX) * 2 / (t * t));
@@ -157,7 +157,7 @@
         }
         double t = (-mUX - Math.sqrt(d)) / mAX;
 
-        float sY = -mFrom.exactCenterY() + mIconRect.exactCenterY();
+        float sY = -mFrom.centerY() + mIconRect.exactCenterY();
 
         // Find vertical acceleration such that: u*t + a*t*t/2 = s
         mAY = (float) ((sY - t * mUY) * 2 / (t * t));
diff --git a/src/com/android/launcher3/util/HorizontalInsettableView.java b/src/com/android/launcher3/util/HorizontalInsettableView.java
new file mode 100644
index 0000000..7979bc0
--- /dev/null
+++ b/src/com/android/launcher3/util/HorizontalInsettableView.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+/**
+ * Allows the implementing view to add insets to the left and right.
+ */
+public interface HorizontalInsettableView {
+
+    /**
+     * Sets left and right insets for the view so it looks like the width of the view is
+     * reduced when inset is increased.
+     *
+     * The inset is calculated based on the width of the view: e.g. when the width of
+     * the view is 100px then if we apply 0.15f horizontal inset percentage the rendered width
+     * of the view will be 70px with 15px of padding on the left and right sides.
+     *
+     * @param insetPercentage width percentage to inset the content from the left and from the right
+     */
+    void setHorizontalInsets(float insetPercentage);
+
+}
diff --git a/src/com/android/launcher3/util/IntArray.java b/src/com/android/launcher3/util/IntArray.java
index 7252f7a..1c78795 100644
--- a/src/com/android/launcher3/util/IntArray.java
+++ b/src/com/android/launcher3/util/IntArray.java
@@ -17,13 +17,14 @@
 package com.android.launcher3.util;
 
 import java.util.Arrays;
+import java.util.Iterator;
 import java.util.StringTokenizer;
 
 /**
  * Copy of the platform hidden implementation of android.util.IntArray.
  * Implements a growing array of int primitives.
  */
-public class IntArray implements Cloneable {
+public class IntArray implements Cloneable, Iterable<Integer> {
     private static final int MIN_CAPACITY_INCREMENT = 12;
 
     private static final int[] EMPTY_INT = new int[0];
@@ -272,4 +273,30 @@
             throw new ArrayIndexOutOfBoundsException("length=" + len + "; index=" + index);
         }
     }
+
+    @Override
+    public Iterator<Integer> iterator() {
+        return new ValueIterator();
+    }
+
+    @Thunk
+    class ValueIterator implements Iterator<Integer> {
+
+        private int mNextIndex = 0;
+
+        @Override
+        public boolean hasNext() {
+            return mNextIndex < size();
+        }
+
+        @Override
+        public Integer next() {
+            return get(mNextIndex++);
+        }
+
+        @Override
+        public void remove() {
+            removeIndex(--mNextIndex);
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/util/IntSet.java b/src/com/android/launcher3/util/IntSet.java
index 851f129..4fd06fe 100644
--- a/src/com/android/launcher3/util/IntSet.java
+++ b/src/com/android/launcher3/util/IntSet.java
@@ -16,11 +16,13 @@
 package com.android.launcher3.util;
 
 import java.util.Arrays;
+import java.util.Iterator;
 
 /**
  * A wrapper over IntArray implementing a growing set of int primitives.
+ * The elements in the array are sorted in ascending order.
  */
-public class IntSet {
+public class IntSet implements Iterable<Integer> {
 
     final IntArray mArray = new IntArray();
 
@@ -34,6 +36,25 @@
         }
     }
 
+    /**
+     * Appends the specified IntSet's values to the set if they does not exist, then returns the
+     * original set that now also contains the new values.
+     */
+    public IntSet addAll(IntSet other) {
+        other.forEach(this::add);
+        return this;
+    }
+
+    /**
+     * Removes the specified value from the set if it exist.
+     */
+    public void remove(int value) {
+        int index = Arrays.binarySearch(mArray.mValues, 0, mArray.mSize, value);
+        if (index >= 0) {
+            mArray.removeIndex(index);
+        }
+    }
+
     public boolean contains(int value) {
         return Arrays.binarySearch(mArray.mValues, 0, mArray.mSize, value) >= 0;
     }
@@ -61,6 +82,9 @@
         return (obj instanceof IntSet) && ((IntSet) obj).mArray.equals(mArray);
     }
 
+    /**
+     * Returns the wrapped IntArray. The elements in the array are sorted in ascending order.
+     */
     public IntArray getArray() {
         return mArray;
     }
@@ -78,4 +102,30 @@
         Arrays.sort(set.mArray.mValues, 0, set.mArray.mSize);
         return set;
     }
+
+    /**
+     * Returns an IntSet with the given values.
+     */
+    public static IntSet wrap(int... array) {
+        return wrap(IntArray.wrap(array));
+    }
+
+    /**
+     * Returns an IntSet with the given values.
+     */
+    public static IntSet wrap(Iterable<Integer> iterable) {
+        IntSet set = new IntSet();
+        iterable.forEach(set::add);
+        return set;
+    }
+
+    @Override
+    public Iterator<Integer> iterator() {
+        return mArray.iterator();
+    }
+
+    @Override
+    public String toString() {
+        return "IntSet{" + mArray.toConcatString() + '}';
+    }
 }
diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java
index 354609d..ab3083d 100644
--- a/src/com/android/launcher3/util/ItemInfoMatcher.java
+++ b/src/com/android/launcher3/util/ItemInfoMatcher.java
@@ -23,6 +23,7 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
 
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -31,6 +32,11 @@
  */
 public interface ItemInfoMatcher {
 
+    /**
+     * Empty component used for match testing
+     */
+    ComponentName EMPTY_COMPONENT = new ComponentName("", "");
+
     boolean matches(ItemInfo info, ComponentName cn);
 
     /**
@@ -39,7 +45,7 @@
     default boolean matchesInfo(ItemInfo info) {
         if (info != null) {
             ComponentName cn = info.getTargetComponent();
-            return cn != null && matches(info, cn);
+            return matches(info, cn != null ? cn : EMPTY_COMPONENT);
         } else {
             return false;
         }
@@ -89,4 +95,13 @@
     static ItemInfoMatcher ofItemIds(IntSet ids) {
         return (info, cn) -> ids.contains(info.id);
     }
+
+    /**
+     * Returns a matcher for items with provided items
+     */
+    static ItemInfoMatcher ofItems(Collection<? extends ItemInfo> items) {
+        IntSet ids = new IntSet();
+        items.forEach(item -> ids.add(item.id));
+        return ofItemIds(ids);
+    }
 }
diff --git a/src/com/android/launcher3/util/LauncherBindableItemsContainer.java b/src/com/android/launcher3/util/LauncherBindableItemsContainer.java
new file mode 100644
index 0000000..a4cb30a
--- /dev/null
+++ b/src/com/android/launcher3/util/LauncherBindableItemsContainer.java
@@ -0,0 +1,113 @@
+/*
+ * 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.graphics.drawable.Drawable;
+import android.view.View;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.graphics.PreloadIconDrawable;
+import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
+
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * 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#bindWorkspaceItemsChanged(List)}
+     */
+    default void updateWorkspaceItems(List<WorkspaceItemInfo> shortcuts, ActivityContext context) {
+        final HashSet<WorkspaceItemInfo> updates = new HashSet<>(shortcuts);
+        ItemOperator op = (info, v) -> {
+            if (v instanceof BubbleTextView && updates.contains(info)) {
+                WorkspaceItemInfo si = (WorkspaceItemInfo) info;
+                BubbleTextView shortcut = (BubbleTextView) v;
+                Drawable oldIcon = shortcut.getIcon();
+                boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable)
+                        && ((PreloadIconDrawable) oldIcon).hasNotCompleted();
+                shortcut.applyFromWorkspaceItem(si, si.isPromise() != oldPromiseState);
+            } else if (info instanceof FolderInfo && v instanceof FolderIcon) {
+                ((FolderIcon) v).updatePreviewItems(updates::contains);
+            }
+
+            // Iterate all items
+            return false;
+        };
+
+        mapOverItems(op);
+        Folder openFolder = Folder.getOpen(context);
+        if (openFolder != null) {
+            openFolder.iterateOverItems(op);
+        }
+    }
+
+    /**
+     * Called to update restored items as a result of
+     * {@link com.android.launcher3.model.BgDataModel.Callbacks#bindRestoreItemsChange(HashSet)}}
+     */
+    default void updateRestoreItems(final HashSet<ItemInfo> updates, ActivityContext context) {
+        ItemOperator op = (info, v) -> {
+            if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView
+                    && updates.contains(info)) {
+                ((BubbleTextView) v).applyLoadingState(false /* promiseStateChanged */);
+            } else if (v instanceof PendingAppWidgetHostView
+                    && info instanceof LauncherAppWidgetInfo
+                    && updates.contains(info)) {
+                ((PendingAppWidgetHostView) v).applyState();
+            } else if (v instanceof FolderIcon && info instanceof FolderInfo) {
+                ((FolderIcon) v).updatePreviewItems(updates::contains);
+            }
+            // process all the shortcuts
+            return false;
+        };
+
+        mapOverItems(op);
+        Folder folder = Folder.getOpen(context);
+        if (folder != null) {
+            folder.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/LogConfig.java b/src/com/android/launcher3/util/LogConfig.java
index 528a6e9..6bc26e7 100644
--- a/src/com/android/launcher3/util/LogConfig.java
+++ b/src/com/android/launcher3/util/LogConfig.java
@@ -35,4 +35,9 @@
      * When turned on, we enable doodle related logging.
      */
     public static final String DOODLE_LOGGING = "DoodleLogging";
+
+    /**
+     * When turned on, we enable suggest related logging.
+     */
+    public static final String SEARCH_LOGGING = "SearchLogging";
 }
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index f6003dd..badcd35 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -18,13 +18,21 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
 import android.content.Context;
+import android.content.ContextWrapper;
 import android.os.Looper;
+import android.util.Log;
 
+import androidx.annotation.UiThread;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext;
 import com.android.launcher3.util.ResourceBasedOverride.Overrides;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ExecutionException;
 
 /**
@@ -40,8 +48,8 @@
     }
 
     public T get(Context context) {
-        if (context instanceof PreviewContext) {
-            return ((PreviewContext) context).getObject(this, mProvider);
+        if (context instanceof SandboxContext) {
+            return ((SandboxContext) context).getObject(this, mProvider);
         }
 
         if (mValue == null) {
@@ -80,4 +88,80 @@
 
         T get(Context context);
     }
+
+    /**
+     * Abstract Context which allows custom implementations for
+     * {@link MainThreadInitializedObject} providers
+     */
+    public static abstract class SandboxContext extends ContextWrapper {
+
+        private static final String TAG = "SandboxContext";
+
+        protected final Set<MainThreadInitializedObject> mAllowedObjects;
+        protected final Map<MainThreadInitializedObject, Object> mObjectMap = new HashMap<>();
+        protected final ArrayList<Object> mOrderedObjects = new ArrayList<>();
+
+        private final Object mDestroyLock = new Object();
+        private boolean mDestroyed = false;
+
+        public SandboxContext(Context base, MainThreadInitializedObject... allowedObjects) {
+            super(base);
+            mAllowedObjects = new HashSet<>(Arrays.asList(allowedObjects));
+        }
+
+        @Override
+        public Context getApplicationContext() {
+            return this;
+        }
+
+        public void onDestroy() {
+            synchronized (mDestroyLock) {
+                // Destroy in reverse order
+                for (int i = mOrderedObjects.size() - 1; i >= 0; i--) {
+                    Object o = mOrderedObjects.get(i);
+                    if (o instanceof SafeCloseable) {
+                        ((SafeCloseable) o).close();
+                    }
+                }
+                mDestroyed = true;
+            }
+        }
+
+        /**
+         * Find a cached object from mObjectMap if we have already created one. If not, generate
+         * an object using the provider.
+         */
+        private <T> T getObject(MainThreadInitializedObject<T> object, ObjectProvider<T> provider) {
+            synchronized (mDestroyLock) {
+                if (mDestroyed) {
+                    Log.e(TAG, "Static object access with a destroyed context");
+                }
+                if (!mAllowedObjects.contains(object)) {
+                    throw new IllegalStateException(
+                            "Leaking unknown objects " + object + "  " + provider);
+                }
+                T t = (T) mObjectMap.get(object);
+                if (t != null) {
+                    return t;
+                }
+                if (Looper.myLooper() == Looper.getMainLooper()) {
+                    t = createObject(provider);
+                    mObjectMap.put(object, t);
+                    mOrderedObjects.add(t);
+                    return t;
+                }
+            }
+
+            try {
+                return MAIN_EXECUTOR.submit(() -> getObject(object, provider)).get();
+            } catch (InterruptedException | ExecutionException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        @UiThread
+        protected <T> T createObject(ObjectProvider<T> provider) {
+            return provider.get(this);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/util/MultiValueAlpha.java b/src/com/android/launcher3/util/MultiValueAlpha.java
index 5be9529..bd39391 100644
--- a/src/com/android/launcher3/util/MultiValueAlpha.java
+++ b/src/com/android/launcher3/util/MultiValueAlpha.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.util;
 
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
 import android.util.FloatProperty;
 import android.view.View;
 
@@ -121,5 +123,15 @@
         public String toString() {
             return Float.toString(mValue);
         }
+
+        /**
+         * Creates and returns an Animator from the current value to the given value. Future
+         * animator on the same target automatically cancels the previous one.
+         */
+        public Animator animateToValue(float value) {
+            ObjectAnimator animator = ObjectAnimator.ofFloat(this, VALUE, value);
+            animator.setAutoCancel(true);
+            return animator;
+        }
     }
 }
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index 40bc9c3..5ba0d30 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -38,6 +38,15 @@
     public static final String HOTSEAT_LONGPRESS_TIP_SEEN = "launcher.hotseat_longpress_tip_seen";
     public static final String SEARCH_EDU_SEEN = "launcher.search_edu_seen";
     public static final String SEARCH_SNACKBAR_COUNT = "launcher.keyboard_snackbar_count";
+    public static final String TASKBAR_EDU_SEEN = "launcher.taskbar_edu_seen";
+    // When adding a new key, add it here as well, to be able to reset it from Developer Options.
+    public static final Map<String, String[]> ALL_PREF_KEYS = Map.of(
+            "All Apps Bounce", new String[] { HOME_BOUNCE_SEEN, HOME_BOUNCE_COUNT },
+            "Hybrid Hotseat Education", new String[] { HOTSEAT_DISCOVERY_TIP_COUNT,
+                    HOTSEAT_LONGPRESS_TIP_SEEN },
+            "Search Education", new String[] { SEARCH_EDU_SEEN, SEARCH_SNACKBAR_COUNT },
+            "Taskbar Education", new String[] { TASKBAR_EDU_SEEN }
+    );
 
     /**
      * Events that either have happened or have not (booleans).
@@ -45,7 +54,8 @@
     @StringDef(value = {
             HOME_BOUNCE_SEEN,
             HOTSEAT_LONGPRESS_TIP_SEEN,
-            SEARCH_EDU_SEEN
+            SEARCH_EDU_SEEN,
+            TASKBAR_EDU_SEEN
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventBoolKey {
diff --git a/src/com/android/launcher3/util/PackageUserKey.java b/src/com/android/launcher3/util/PackageUserKey.java
index 3a3b5a2..92d9737 100644
--- a/src/com/android/launcher3/util/PackageUserKey.java
+++ b/src/com/android/launcher3/util/PackageUserKey.java
@@ -1,19 +1,24 @@
 package com.android.launcher3.util;
 
+import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
+
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.PackageItemInfo;
 
-import java.util.Arrays;
+import java.util.Objects;
 
-/** Creates a hash key based on package name and user. */
+/** Creates a hash key based on package name, widget category, and user. */
 public class PackageUserKey {
 
     public String mPackageName;
+    public int mWidgetCategory;
     public UserHandle mUser;
     private int mHashCode;
 
@@ -27,14 +32,31 @@
         return new PackageUserKey(notification.getPackageName(), notification.getUser());
     }
 
+    /** Creates a {@link PackageUserKey} from {@link PackageItemInfo}. */
+    public static PackageUserKey fromPackageItemInfo(PackageItemInfo info) {
+        if (TextUtils.isEmpty(info.packageName) && info.widgetCategory != NO_CATEGORY) {
+            return new PackageUserKey(info.widgetCategory, info.user);
+        }
+        return new PackageUserKey(info.packageName, info.user);
+    }
+
     public PackageUserKey(String packageName, UserHandle user) {
         update(packageName, user);
     }
 
+    public PackageUserKey(int widgetCategory, UserHandle user) {
+        update(/* packageName= */ "", widgetCategory, user);
+    }
+
     public void update(String packageName, UserHandle user) {
+        update(packageName, NO_CATEGORY, user);
+    }
+
+    private void update(String packageName, int widgetCategory, UserHandle user) {
         mPackageName = packageName;
+        mWidgetCategory = widgetCategory;
         mUser = user;
-        mHashCode = Arrays.hashCode(new Object[] {packageName, user});
+        mHashCode = Objects.hash(packageName, widgetCategory, user);
     }
 
     /**
@@ -59,12 +81,14 @@
     public boolean equals(Object obj) {
         if (!(obj instanceof PackageUserKey)) return false;
         PackageUserKey otherKey = (PackageUserKey) obj;
-        return mPackageName.equals(otherKey.mPackageName) && mUser.equals(otherKey.mUser);
+        return Objects.equals(mPackageName, otherKey.mPackageName)
+                && mWidgetCategory == otherKey.mWidgetCategory
+                && Objects.equals(mUser, otherKey.mUser);
     }
 
     @NonNull
     @Override
     public String toString() {
-        return mPackageName + "#" + mUser;
+        return mPackageName + "#" + mUser + ",category=" + mWidgetCategory;
     }
 }
diff --git a/src/com/android/launcher3/util/PluralMessageFormat.java b/src/com/android/launcher3/util/PluralMessageFormat.java
new file mode 100644
index 0000000..5e4ce8d
--- /dev/null
+++ b/src/com/android/launcher3/util/PluralMessageFormat.java
@@ -0,0 +1,45 @@
+/*
+ * 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.content.Context;
+import android.icu.text.MessageFormat;
+
+import androidx.annotation.StringRes;
+
+import java.util.HashMap;
+import java.util.Locale;
+
+/** A helper class to format common ICU plural strings. */
+public class PluralMessageFormat {
+
+    /**
+     * Returns a plural string from a ICU format message template, which takes "count" as an
+     * argument.
+     *
+     * <p>An example of ICU format message template provided by {@code stringId}:
+     * {count, plural, =1{# widget} other{# widgets}}
+     */
+    public static final String getIcuPluralString(Context context, @StringRes int stringId,
+            int count) {
+        MessageFormat icuCountFormat = new MessageFormat(
+                context.getResources().getString(stringId),
+                Locale.getDefault());
+        HashMap<String, Object> args = new HashMap();
+        args.put("count", count);
+        return icuCountFormat.format(args);
+    }
+}
diff --git a/src/com/android/launcher3/util/SettingsCache.java b/src/com/android/launcher3/util/SettingsCache.java
index 10611c7..0c5b722 100644
--- a/src/com/android/launcher3/util/SettingsCache.java
+++ b/src/com/android/launcher3/util/SettingsCache.java
@@ -47,7 +47,7 @@
  *
  * Cache will also be updated if a key queried is missing (even if it has no listeners registered).
  */
-public class SettingsCache extends ContentObserver {
+public class SettingsCache extends ContentObserver implements SafeCloseable {
 
     /** Hidden field Settings.Secure.NOTIFICATION_BADGING */
     public static final Uri NOTIFICATION_BADGING_URI =
@@ -69,7 +69,6 @@
     private final Map<Uri, CopyOnWriteArrayList<OnChangeListener>> mListenerMap = new HashMap<>();
     protected final ContentResolver mResolver;
 
-
     /**
      * Singleton instance
      */
@@ -82,6 +81,11 @@
     }
 
     @Override
+    public void close() {
+        mResolver.unregisterContentObserver(this);
+    }
+
+    @Override
     public void onChange(boolean selfChange, Uri uri) {
         // We use default of 1, but if we're getting an onChange call, can assume a non-default
         // value will exist
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index 573c8bd..0b083e3 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -18,6 +18,8 @@
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import android.graphics.Rect;
+
 import androidx.annotation.IntDef;
 
 import java.lang.annotation.Retention;
@@ -67,19 +69,64 @@
     ///////////////////////////////////
 
     public static class SplitPositionOption {
-        public final int mIconResId;
-        public final int mTextResId;
+        public final int iconResId;
+        public final int textResId;
         @StagePosition
-        public final int mStagePosition;
+        public final int stagePosition;
 
         @StageType
         public final int mStageType;
 
         public SplitPositionOption(int iconResId, int textResId, int stagePosition, int stageType) {
-            mIconResId = iconResId;
-            mTextResId = textResId;
-            mStagePosition = stagePosition;
+            this.iconResId = iconResId;
+            this.textResId = textResId;
+            this.stagePosition = stagePosition;
             mStageType = stageType;
         }
     }
+
+    public static class StagedSplitBounds {
+        public final Rect leftTopBounds;
+        public final Rect rightBottomBounds;
+        /** This rect represents the actual gap between the two apps */
+        public final Rect visualDividerBounds;
+        // This class is orientation-agnostic, so we compute both for later use
+        public final float topTaskPercent;
+        public final float leftTaskPercent;
+        /**
+         * If {@code true}, that means at the time of creation of this object, the
+         * split-screened apps were vertically stacked. This is useful in scenarios like
+         * rotation where the bounds won't change, but this variable can indicate what orientation
+         * the bounds were originally in
+         */
+        public final boolean appsStackedVertically;
+
+        public StagedSplitBounds(Rect leftTopBounds, Rect rightBottomBounds) {
+            this.leftTopBounds = leftTopBounds;
+            this.rightBottomBounds = rightBottomBounds;
+
+            if (rightBottomBounds.top > leftTopBounds.top) {
+                // vertical apps, horizontal divider
+                this.visualDividerBounds = new Rect(leftTopBounds.left, leftTopBounds.bottom,
+                        leftTopBounds.right, rightBottomBounds.top);
+                appsStackedVertically = true;
+            } else {
+                // horizontal apps, vertical divider
+                this.visualDividerBounds = new Rect(leftTopBounds.right, leftTopBounds.top,
+                        rightBottomBounds.left, leftTopBounds.bottom);
+                appsStackedVertically = false;
+            }
+
+            leftTaskPercent = this.leftTopBounds.width() / (float) rightBottomBounds.right;
+            topTaskPercent = this.leftTopBounds.height() / (float) rightBottomBounds.bottom;
+        }
+    }
+
+    public static class StagedSplitTaskPosition {
+        public int taskId = -1;
+        @StagePosition
+        public int stagePosition = STAGE_POSITION_UNDEFINED;
+        @StageType
+        public int stageType = STAGE_TYPE_UNDEFINED;
+    }
 }
diff --git a/src/com/android/launcher3/util/UiThreadHelper.java b/src/com/android/launcher3/util/UiThreadHelper.java
index 0f40179..ac5368c 100644
--- a/src/com/android/launcher3/util/UiThreadHelper.java
+++ b/src/com/android/launcher3/util/UiThreadHelper.java
@@ -28,7 +28,7 @@
 import android.view.View;
 import android.view.inputmethod.InputMethodManager;
 
-import com.android.launcher3.Launcher;
+import com.android.launcher3.BaseActivity;
 import com.android.launcher3.views.ActivityContext;
 
 /**
@@ -56,7 +56,7 @@
                 STATS_LOGGER_KEY,
                 Message.obtain(
                         HANDLER.get(root.getContext()),
-                        () -> Launcher.cast(activityContext)
+                        () -> BaseActivity.fromContext(root.getContext())
                                 .getStatsLogManager()
                                 .logger()
                                 .log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED)
diff --git a/src/com/android/launcher3/util/ViewOnDrawExecutor.java b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
index 82e24c2..5d90291 100644
--- a/src/com/android/launcher3/util/ViewOnDrawExecutor.java
+++ b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
@@ -16,28 +16,21 @@
 
 package com.android.launcher3.util;
 
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-
-import android.os.Process;
 import android.view.View;
 import android.view.View.OnAttachStateChangeListener;
 import android.view.ViewTreeObserver.OnDrawListener;
 
-import androidx.annotation.VisibleForTesting;
-
 import com.android.launcher3.Launcher;
 
-import java.util.ArrayList;
-import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
 /**
  * An executor which runs all the tasks after the first onDraw is called on the target view.
  */
-public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable,
+public class ViewOnDrawExecutor implements OnDrawListener, Runnable,
         OnAttachStateChangeListener {
 
-    private final ArrayList<Runnable> mTasks = new ArrayList<>();
+    private final RunnableList mTasks;
 
     private Consumer<ViewOnDrawExecutor> mOnClearCallback;
     private View mAttachedView;
@@ -46,22 +39,16 @@
     private boolean mLoadAnimationCompleted;
     private boolean mFirstDrawCompleted;
 
-    public void attachTo(Launcher launcher) {
-        attachTo(launcher.getWorkspace(), true /* waitForLoadAnimation */,
-                launcher::clearPendingExecutor);
+    private boolean mCancelled;
+
+    public ViewOnDrawExecutor(RunnableList tasks) {
+        mTasks = tasks;
     }
 
-    /**
-     * Attached the executor to the existence of the view
-     */
-    public void attachTo(View attachedView, boolean waitForLoadAnimation,
-            Consumer<ViewOnDrawExecutor> onClearCallback) {
-        mOnClearCallback = onClearCallback;
-        mAttachedView = attachedView;
+    public void attachTo(Launcher launcher) {
+        mOnClearCallback = launcher::clearPendingExecutor;
+        mAttachedView = launcher.getWorkspace();
         mAttachedView.addOnAttachStateChangeListener(this);
-        if (!waitForLoadAnimation) {
-            mLoadAnimationCompleted = true;
-        }
 
         if (mAttachedView.isAttachedToWindow()) {
             attachObserver();
@@ -75,12 +62,6 @@
     }
 
     @Override
-    public void execute(Runnable command) {
-        mTasks.add(command);
-        MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-    }
-
-    @Override
     public void onViewAttachedToWindow(View v) {
         attachObserver();
     }
@@ -105,12 +86,17 @@
     public void run() {
         // Post the pending tasks after both onDraw and onLoadAnimationCompleted have been called.
         if (mLoadAnimationCompleted && mFirstDrawCompleted && !mCompleted) {
-            runAllTasks();
+            markCompleted();
         }
     }
 
+    /**
+     * Executes all tasks immediately
+     */
     public void markCompleted() {
-        mTasks.clear();
+        if (!mCancelled) {
+            mTasks.executeAllAndDestroy();
+        }
         mCompleted = true;
         if (mAttachedView != null) {
             mAttachedView.getViewTreeObserver().removeOnDrawListener(this);
@@ -119,21 +105,10 @@
         if (mOnClearCallback != null) {
             mOnClearCallback.accept(this);
         }
-        MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
     }
 
-    protected boolean isCompleted() {
-        return mCompleted;
-    }
-
-    /**
-     * Executes all tasks immediately
-     */
-    @VisibleForTesting
-    public void runAllTasks() {
-        for (final Runnable r : mTasks) {
-            r.run();
-        }
+    public void cancel() {
+        mCancelled = true;
         markCompleted();
     }
 }
diff --git a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
index b8554e4..8a7cae9 100644
--- a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
+++ b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
@@ -63,31 +63,37 @@
      *
      * TODO: do different behavior if it's  a live wallpaper?
      */
-    private void wallpaperOffsetForScroll(int scroll, int numScrollingPages, final int[] out) {
+    private void wallpaperOffsetForScroll(int scroll, int numScrollableScreens, final int[] out) {
         out[1] = 1;
 
         // To match the default wallpaper behavior in the system, we default to either the left
         // or right edge on initialization
-        if (mLockedToDefaultPage || numScrollingPages <= 1) {
+        if (mLockedToDefaultPage || numScrollableScreens <= 1) {
             out[0] =  mIsRtl ? 1 : 0;
             return;
         }
 
         // Distribute the wallpaper parallax over a minimum of MIN_PARALLAX_PAGE_SPAN workspace
         // screens, not including the custom screen, and empty screens (if > MIN_PARALLAX_PAGE_SPAN)
-        int numPagesForWallpaperParallax = mWallpaperIsLiveWallpaper ? numScrollingPages :
-                        Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages);
+        int numScreensForWallpaperParallax = mWallpaperIsLiveWallpaper ? numScrollableScreens :
+                        Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollableScreens);
 
         // Offset by the custom screen
-        int leftPageIndex;
-        int rightPageIndex;
-        if (mIsRtl) {
-            rightPageIndex = 0;
-            leftPageIndex = rightPageIndex + numScrollingPages - 1;
-        } else {
-            leftPageIndex = 0;
-            rightPageIndex = leftPageIndex + numScrollingPages - 1;
-        }
+
+        // Don't confuse screens & pages in this function. In a phone UI, we often use screens &
+        // pages interchangeably. However, in a n-panels UI, where n > 1, the screen in this class
+        // means the scrollable screen. Each screen can consist of at most n panels.
+        // Each panel has at most 1 page. Take 5 pages in 2 panels UI as an example, the Workspace
+        // looks as follow:
+        //
+        // S: scrollable screen, P: page, <E>: empty
+        //   S0        S1         S2
+        // _______   _______   ________
+        // |P0|P1|   |P2|P3|   |P4|<E>|
+        // ¯¯¯¯¯¯¯   ¯¯¯¯¯¯¯   ¯¯¯¯¯¯¯¯
+        int endIndex = getNumPagesExcludingEmpty() - 1;
+        final int leftPageIndex = mIsRtl ? endIndex : 0;
+        final int rightPageIndex = mIsRtl ? 0 : endIndex;
 
         // Calculate the scroll range
         int leftPageScrollX = mWorkspace.getScrollForPage(leftPageIndex);
@@ -103,34 +109,56 @@
         int adjustedScroll = scroll - leftPageScrollX -
                 mWorkspace.getLayoutTransitionOffsetForPage(0);
         adjustedScroll = Utilities.boundToRange(adjustedScroll, 0, scrollRange);
-        out[1] = (numPagesForWallpaperParallax - 1) * scrollRange;
+        out[1] = (numScreensForWallpaperParallax - 1) * scrollRange;
 
         // The offset is now distributed 0..1 between the left and right pages that we care about,
         // so we just map that between the pages that we are using for parallax
         int rtlOffset = 0;
         if (mIsRtl) {
             // In RTL, the pages are right aligned, so adjust the offset from the end
-            rtlOffset = out[1] - (numScrollingPages - 1) * scrollRange;
+            rtlOffset = out[1] - (numScrollableScreens - 1) * scrollRange;
         }
-        out[0] = rtlOffset + adjustedScroll * (numScrollingPages - 1);
+        out[0] = rtlOffset + adjustedScroll * (numScrollableScreens - 1);
     }
 
     public float wallpaperOffsetForScroll(int scroll) {
-        wallpaperOffsetForScroll(scroll, getNumScreensExcludingEmpty(), sTempInt);
+        wallpaperOffsetForScroll(scroll, getNumScrollableScreensExcludingEmpty(), sTempInt);
         return ((float) sTempInt[0]) / sTempInt[1];
     }
 
-    private int getNumScreensExcludingEmpty() {
-        int numScrollingPages = mWorkspace.getChildCount();
-        if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && mWorkspace.hasExtraEmptyScreen()) {
-            return numScrollingPages - 1;
+    /**
+     * Returns the number of screens that can be scrolled.
+     *
+     * <p>In an usual phone UI, the number of scrollable screens is equal to the number of
+     * CellLayouts because each screen has exactly 1 CellLayout.
+     *
+     * <p>In a n-panels UI, a screen shows n panels. Each panel has at most 1 CellLayout. Take
+     * 2-panels UI as an example: let's say there are 5 CellLayouts in the Workspace. the number of
+     * scrollable screens will be 3 = ⌈5 / 2⌉.
+     */
+    private int getNumScrollableScreensExcludingEmpty() {
+        float numOfPages = getNumPagesExcludingEmpty();
+        return (int) Math.ceil(numOfPages / mWorkspace.getPanelCount());
+    }
+
+    /**
+     * Returns the number of non-empty pages in the Workspace.
+     *
+     * <p>If a user starts dragging on the rightmost (or leftmost in RTL), an empty CellLayout is
+     * added to the Workspace. This empty CellLayout add as a hover-over target for adding a new
+     * page. To avoid janky motion effect, we ignore this empty CellLayout.
+     */
+    private int getNumPagesExcludingEmpty() {
+        int numOfPages = mWorkspace.getChildCount();
+        if (numOfPages >= MIN_PARALLAX_PAGE_SPAN && mWorkspace.hasExtraEmptyScreens()) {
+            return numOfPages - mWorkspace.getPanelCount();
         } else {
-            return numScrollingPages;
+            return numOfPages;
         }
     }
 
     public void syncWithScroll() {
-        int numScreens = getNumScreensExcludingEmpty();
+        int numScreens = getNumScrollableScreensExcludingEmpty();
         wallpaperOffsetForScroll(mWorkspace.getScrollX(), numScreens, sTempInt);
         Message msg = Message.obtain(mHandler, MSG_UPDATE_OFFSET, sTempInt[0], sTempInt[1],
                 mWindowToken);
diff --git a/src/com/android/launcher3/util/WindowManagerCompat.java b/src/com/android/launcher3/util/WindowManagerCompat.java
index 38a63de..e1b9478 100644
--- a/src/com/android/launcher3/util/WindowManagerCompat.java
+++ b/src/com/android/launcher3/util/WindowManagerCompat.java
@@ -24,6 +24,7 @@
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.os.Build;
+import android.util.ArraySet;
 import android.view.WindowInsets;
 import android.view.WindowInsets.Type;
 import android.view.WindowManager;
@@ -31,14 +32,14 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.util.DisplayController.PortraitSize;
 
-import java.util.Collection;
-import java.util.HashSet;
+import java.util.Collections;
 import java.util.Set;
 
 /**
- * Utility class to simulate window manager APIs until proper APIs are available
+ * Utility class to estimate window manager values
  */
 @TargetApi(Build.VERSION_CODES.S)
 public class WindowManagerCompat {
@@ -46,51 +47,51 @@
     public static final int MIN_TABLET_WIDTH = 600;
 
     /**
-     * Returns a set of supported render sizes for a set of internal displays.
-     * This is a temporary workaround which assumes only nav-bar insets change across displays
+     * Returns a set of supported render sizes for a internal display.
+     * This is a temporary workaround which assumes only nav-bar insets change across displays, and
+     * is only used until we eventually get the real values
      * @param consumeTaskBar if true, it assumes that task bar is part of the app window
      *                       and ignores any insets because of task bar.
      */
-    public static Set<WindowMetrics> getDisplayProfiles(
-            Context windowContext, Collection<PortraitSize> allDisplaySizes,
-            int densityDpi, boolean consumeTaskBar) {
-        WindowInsets metrics = windowContext.getSystemService(WindowManager.class)
+    public static Set<WindowBounds> estimateDisplayProfiles(
+            Context windowContext, PortraitSize size, int densityDpi, boolean consumeTaskBar) {
+        if (!Utilities.ATLEAST_S) {
+            return Collections.emptySet();
+        }
+        WindowInsets defaultInsets = windowContext.getSystemService(WindowManager.class)
                 .getMaximumWindowMetrics().getWindowInsets();
-        boolean hasNavbar = ResourceUtils.getIntegerByName(
+        boolean isGesturalMode = ResourceUtils.getIntegerByName(
                 "config_navBarInteractionMode",
                 windowContext.getResources(),
-                INVALID_RESOURCE_HANDLE) != 0;
+                INVALID_RESOURCE_HANDLE) == 2;
 
-        WindowInsets.Builder insetsBuilder = new WindowInsets.Builder(metrics);
+        WindowInsets.Builder insetsBuilder = new WindowInsets.Builder(defaultInsets);
+        Set<WindowBounds> result = new ArraySet<>();
+        int swDP = (int) dpiFromPx(size.width, densityDpi);
+        boolean isTablet = swDP >= MIN_TABLET_WIDTH;
 
-        Set<WindowMetrics> result = new HashSet<>();
-        for (PortraitSize size : allDisplaySizes) {
-            int swDP = (int) dpiFromPx(size.width, densityDpi);
-            boolean isTablet = swDP >= MIN_TABLET_WIDTH;
-
-            final Insets portraitNav, landscapeNav;
-            if (isTablet && !consumeTaskBar) {
-                portraitNav = landscapeNav = Insets.of(0, 0, 0, windowContext.getResources()
-                        .getDimensionPixelSize(R.dimen.taskbar_size));
-            } else if (hasNavbar) {
-                portraitNav = Insets.of(0, 0, 0,
-                        getSystemResource(windowContext, "navigation_bar_height", swDP));
-                landscapeNav = isTablet
-                        ? Insets.of(0, 0, 0, getSystemResource(windowContext,
-                                "navigation_bar_height_landscape", swDP))
-                        : Insets.of(0, 0, getSystemResource(windowContext,
-                                "navigation_bar_width", swDP), 0);
-            } else {
-                portraitNav = landscapeNav = Insets.of(0, 0, 0, 0);
-            }
-
-            result.add(new WindowMetrics(
-                    new Rect(0, 0, size.width, size.height),
-                    insetsBuilder.setInsets(Type.navigationBars(), portraitNav).build()));
-            result.add(new WindowMetrics(
-                    new Rect(0, 0, size.height, size.width),
-                    insetsBuilder.setInsets(Type.navigationBars(), landscapeNav).build()));
+        final Insets portraitNav, landscapeNav;
+        if (isTablet && !consumeTaskBar) {
+            portraitNav = landscapeNav = Insets.of(0, 0, 0, windowContext.getResources()
+                    .getDimensionPixelSize(R.dimen.taskbar_size));
+        } else if (!isGesturalMode) {
+            portraitNav = Insets.of(0, 0, 0,
+                    getSystemResource(windowContext, "navigation_bar_height", swDP));
+            landscapeNav = isTablet
+                    ? Insets.of(0, 0, 0, getSystemResource(windowContext,
+                            "navigation_bar_height_landscape", swDP))
+                    : Insets.of(0, 0, getSystemResource(windowContext,
+                            "navigation_bar_width", swDP), 0);
+        } else {
+            portraitNav = landscapeNav = Insets.of(0, 0, 0, 0);
         }
+
+        result.add(WindowBounds.fromWindowMetrics(new WindowMetrics(
+                new Rect(0, 0, size.width, size.height),
+                insetsBuilder.setInsets(Type.navigationBars(), portraitNav).build())));
+        result.add(WindowBounds.fromWindowMetrics(new WindowMetrics(
+                new Rect(0, 0, size.height, size.width),
+                insetsBuilder.setInsets(Type.navigationBars(), landscapeNav).build())));
         return result;
     }
 
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index 92ca8a1..8ac40b8 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -28,6 +28,7 @@
 import android.util.Property;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.animation.Interpolator;
 
 import com.android.launcher3.AbstractFloatingView;
@@ -68,7 +69,7 @@
     protected final SingleAxisSwipeDetector mSwipeDetector;
     protected final ObjectAnimator mOpenCloseAnimator;
 
-    protected View mContent;
+    protected ViewGroup mContent;
     protected final View mColorScrim;
     protected Interpolator mScrollInterpolator;
 
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index 646b669..a2e4ad6 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -19,11 +19,17 @@
 import android.content.ContextWrapper;
 import android.graphics.Rect;
 import android.view.LayoutInflater;
+import android.view.View;
 import android.view.View.AccessibilityDelegate;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.ViewCache;
 
@@ -100,15 +106,64 @@
     }
 
     /**
-     * Returns the ActivityContext associated with the given Context.
+     * Returns the FolderIcon with the given item id, if it exists.
+     */
+    default @Nullable FolderIcon findFolderIcon(final int folderIconId) {
+        return null;
+    }
+
+    default StatsLogManager getStatsLogManager() {
+        return StatsLogManager.newInstance((Context) this);
+    }
+
+    /**
+     * Returns {@code true} if popups should use color extraction.
+     */
+    default boolean shouldUseColorExtractionForPopup() {
+        return true;
+    }
+
+    /**
+     * Returns whether we can show the IME for elements hosted by this ActivityContext.
+     */
+    default boolean supportsIme() {
+        return true;
+    }
+
+    /**
+     * Called just before logging the given item.
+     */
+    default void applyOverwritesToLogItem(LauncherAtom.ItemInfo.Builder itemInfoBuilder) { }
+
+    /**
+     * Returns the ActivityContext associated with the given Context, or throws an exception if
+     * the Context is not associated with any ActivityContext.
      */
     static <T extends Context & ActivityContext> T lookupContext(Context context) {
+        T activityContext = lookupContextNoThrow(context);
+        if (activityContext == null) {
+            throw new IllegalArgumentException("Cannot find ActivityContext in parent tree");
+        }
+        return activityContext;
+    }
+
+    /**
+     * Returns the ActivityContext associated with the given Context, or null if
+     * the Context is not associated with any ActivityContext.
+     */
+    static <T extends Context & ActivityContext> T lookupContextNoThrow(Context context) {
         if (context instanceof ActivityContext) {
             return (T) context;
         } else if (context instanceof ContextWrapper) {
-            return lookupContext(((ContextWrapper) context).getBaseContext());
+            return lookupContextNoThrow(((ContextWrapper) context).getBaseContext());
         } else {
-            throw new IllegalArgumentException("Cannot find ActivityContext in parent tree");
+            return null;
         }
     }
+
+    default View.OnClickListener getItemOnClickListener() {
+        return v -> {
+            // No op.
+        };
+    }
 }
diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java
index e449a4b..ce26a66 100644
--- a/src/com/android/launcher3/views/ArrowTipView.java
+++ b/src/com/android/launcher3/views/ArrowTipView.java
@@ -37,6 +37,7 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.dragndrop.DragLayer;
@@ -56,6 +57,7 @@
     protected final BaseDraggingActivity mActivity;
     private final Handler mHandler = new Handler();
     private final int mArrowWidth;
+    private final int mArrowMinOffset;
     private boolean mIsPointingUp;
     private Runnable mOnClosed;
     private View mArrowView;
@@ -69,6 +71,8 @@
         mActivity = BaseDraggingActivity.fromContext(context);
         mIsPointingUp = isPointingUp;
         mArrowWidth = context.getResources().getDimensionPixelSize(R.dimen.arrow_toast_arrow_width);
+        mArrowMinOffset = context.getResources().getDimensionPixelSize(
+                R.dimen.dynamic_grid_cell_border_spacing);
         init(context);
     }
 
@@ -126,10 +130,10 @@
     /**
      * Show the ArrowTipView (tooltip) center, start, or end aligned.
      *
-     * @param text The text to be shown in the tooltip.
-     * @param gravity The gravity aligns the tooltip center, start, or end.
+     * @param text             The text to be shown in the tooltip.
+     * @param gravity          The gravity aligns the tooltip center, start, or end.
      * @param arrowMarginStart The margin from start to place arrow (ignored if center)
-     * @param top The Y coordinate of the bottom of tooltip.
+     * @param top              The Y coordinate of the bottom of tooltip.
      * @return The tooltip.
      */
     public ArrowTipView show(String text, int gravity, int arrowMarginStart, int top) {
@@ -137,23 +141,28 @@
         ViewGroup parent = mActivity.getDragLayer();
         parent.addView(this);
 
+        DeviceProfile grid = mActivity.getDeviceProfile();
+
         DragLayer.LayoutParams params = (DragLayer.LayoutParams) getLayoutParams();
         params.gravity = gravity;
+        params.leftMargin = mArrowMinOffset + grid.getInsets().left;
+        params.rightMargin = mArrowMinOffset + grid.getInsets().right;
         LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mArrowView.getLayoutParams();
+
         lp.gravity = gravity;
 
         if (parent.getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
             arrowMarginStart = parent.getMeasuredWidth() - arrowMarginStart;
         }
         if (gravity == Gravity.END) {
-            lp.setMarginEnd(parent.getMeasuredWidth() - arrowMarginStart - mArrowWidth);
+            lp.setMarginEnd(Math.max(mArrowMinOffset,
+                    parent.getMeasuredWidth() - params.rightMargin - arrowMarginStart
+                            - mArrowWidth / 2));
         } else if (gravity == Gravity.START) {
-            lp.setMarginStart(arrowMarginStart - mArrowWidth / 2);
+            lp.setMarginStart(Math.max(mArrowMinOffset,
+                    arrowMarginStart - params.leftMargin - mArrowWidth / 2));
         }
         requestLayout();
-
-        params.leftMargin = mActivity.getDeviceProfile().workspacePadding.left;
-        params.rightMargin = mActivity.getDeviceProfile().workspacePadding.right;
         post(() -> setY(top - (mIsPointingUp ? 0 : getHeight())));
 
         mIsOpen = true;
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 01c0b56..76dfb3c 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -430,18 +430,20 @@
     }
 
     public void getViewRectRelativeToSelf(View v, Rect r) {
+        int[] loc = getViewLocationRelativeToSelf(v);
+        r.set(loc[0], loc[1], loc[0] + v.getMeasuredWidth(), loc[1] + v.getMeasuredHeight());
+    }
+
+    protected int[] getViewLocationRelativeToSelf(View v) {
         int[] loc = new int[2];
         getLocationInWindow(loc);
         int x = loc[0];
         int y = loc[1];
 
         v.getLocationInWindow(loc);
-        int vX = loc[0];
-        int vY = loc[1];
-
-        int left = vX - x;
-        int top = vY - y;
-        r.set(left, top, left + v.getMeasuredWidth(), top + v.getMeasuredHeight());
+        loc[0] -= x;
+        loc[1] -= y;
+        return loc;
     }
 
     @Override
diff --git a/src/com/android/launcher3/views/BubbleTextHolder.java b/src/com/android/launcher3/views/BubbleTextHolder.java
index 47d3563..1cb27e1 100644
--- a/src/com/android/launcher3/views/BubbleTextHolder.java
+++ b/src/com/android/launcher3/views/BubbleTextHolder.java
@@ -16,10 +16,20 @@
 package com.android.launcher3.views;
 
 import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 
 /**
  * Views that contain {@link BubbleTextView} should implement this interface.
  */
 public interface BubbleTextHolder {
     BubbleTextView getBubbleText();
+
+    /**
+     * Called when new {@link ItemInfo} is set to {@link BubbleTextView}
+     *
+     * @param itemInfo the new itemInfo
+     */
+    default void onItemInfoUpdated(ItemInfoWithIcon itemInfo) {
+    }
 }
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index ecdd206..33ab0d2 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -59,7 +59,7 @@
 /**
  * Popup shown on long pressing an empty space in launcher
  */
-public class OptionsPopupView extends ArrowPopup
+public class OptionsPopupView extends ArrowPopup<Launcher>
         implements OnClickListener, OnLongClickListener {
 
     private final ArrayMap<View, OptionItem> mItemMap = new ArrayMap<>();
@@ -74,6 +74,10 @@
         super(context, attrs, defStyleAttr);
     }
 
+    public void setTargetRect(RectF targetRect) {
+        mTargetRect = targetRect;
+    }
+
     @Override
     public void onClick(View view) {
         handleViewClick(view);
@@ -90,7 +94,7 @@
             return false;
         }
         if (item.eventId.getId() > 0) {
-            mLauncher.getStatsLogManager().logger().log(item.eventId);
+            mActivityContext.getStatsLogManager().logger().log(item.eventId);
         }
         if (item.clickListener.onLongClick(view)) {
             close(true);
@@ -176,16 +180,6 @@
         return launcher.findViewById(R.id.popup_container);
     }
 
-    public static void showDefaultOptions(Launcher launcher, float x, float y) {
-        float halfSize = launcher.getResources().getDimension(R.dimen.options_menu_thumb_size) / 2;
-        if (x < 0 || y < 0) {
-            x = launcher.getDragLayer().getWidth() / 2;
-            y = launcher.getDragLayer().getHeight() / 2;
-        }
-        RectF target = new RectF(x - halfSize, y - halfSize, x + halfSize, y + halfSize);
-        show(launcher, target, getOptions(launcher), false);
-    }
-
     /**
      * Returns the list of supported actions
      */
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 1c2534d..a982786 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -40,7 +40,6 @@
 import android.view.WindowInsets;
 import android.widget.TextView;
 
-import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -114,7 +113,6 @@
     private boolean mIsThumbDetached;
     private final boolean mCanThumbDetach;
     private boolean mIgnoreDragGesture;
-    private boolean mIsRecyclerViewFirstChildInParent = true;
     private long mDownTimeStampMillis;
 
     // This is the offset from the top of the scrollbar when the user first starts touching.  To
@@ -131,7 +129,6 @@
 
     protected BaseRecyclerView mRv;
     private RecyclerView.OnScrollListener mOnScrollListener;
-    @Nullable private OnFastScrollChangeListener mOnFastScrollChangeListener;
 
     private int mDownX;
     private int mDownY;
@@ -208,7 +205,6 @@
             int rvCurrentOffsetY = mRv.getCurrentScrollY();
             if (mRvOffsetY != rvCurrentOffsetY) {
                 mRvOffsetY = mRv.getCurrentScrollY();
-                notifyScrollChanged();
             }
             return;
         }
@@ -216,7 +212,6 @@
         mThumbOffsetY = y;
         invalidate();
         mRvOffsetY = mRv.getCurrentScrollY();
-        notifyScrollChanged();
     }
 
     public int getThumbOffsetY() {
@@ -442,9 +437,7 @@
             return false;
         }
         getHitRect(sTempRect);
-        if (mIsRecyclerViewFirstChildInParent) {
-            sTempRect.top += mRv.getScrollBarTop();
-        }
+        sTempRect.top += mRv.getScrollBarTop();
         if (outOffset != null) {
             outOffset.set(sTempRect.left, sTempRect.top);
         }
@@ -457,27 +450,4 @@
         // alpha is so low, it does not matter.
         return false;
     }
-
-    public void setIsRecyclerViewFirstChildInParent(boolean isRecyclerViewFirstChildInParent) {
-        mIsRecyclerViewFirstChildInParent = isRecyclerViewFirstChildInParent;
-    }
-
-    public void setOnFastScrollChangeListener(
-            @Nullable OnFastScrollChangeListener onFastScrollChangeListener) {
-        mOnFastScrollChangeListener = onFastScrollChangeListener;
-    }
-
-    private void notifyScrollChanged() {
-        if (mOnFastScrollChangeListener != null) {
-            mOnFastScrollChangeListener.onScrollChanged();
-        }
-    }
-
-    /**
-     * A callback that is invoked when there is a scroll change in {@link RecyclerViewFastScroller}.
-     */
-    public interface OnFastScrollChangeListener {
-        /** Called when the recycler view scroll has changed. */
-        void onScrollChanged();
-    }
 }
diff --git a/src/com/android/launcher3/views/TopRoundedCornerView.java b/src/com/android/launcher3/views/TopRoundedCornerView.java
deleted file mode 100644
index 92cce92..0000000
--- a/src/com/android/launcher3/views/TopRoundedCornerView.java
+++ /dev/null
@@ -1,61 +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.views;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Path;
-import android.graphics.RectF;
-import android.util.AttributeSet;
-
-import com.android.launcher3.util.Themes;
-
-/**
- * View with top rounded corners.
- */
-public class TopRoundedCornerView extends SpringRelativeLayout {
-
-    private final RectF mRect = new RectF();
-    private final Path mClipPath = new Path();
-    private float[] mRadii;
-
-    public TopRoundedCornerView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        float radius = Themes.getDialogCornerRadius(context);
-        mRadii = new float[] {radius, radius, radius, radius, 0, 0, 0, 0};
-    }
-
-    public TopRoundedCornerView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        canvas.save();
-        canvas.clipPath(mClipPath);
-        super.draw(canvas);
-        canvas.restore();
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        mRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
-        mClipPath.reset();
-        mClipPath.addRoundRect(mRect, mRadii, Path.Direction.CW);
-    }
-}
diff --git a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
index 1cc7f53..d2d569f 100644
--- a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
@@ -18,6 +18,7 @@
 
 import static com.android.launcher3.Utilities.ATLEAST_R;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.launcher3.widget.BaseWidgetSheet.MAX_WIDTH_SCALE_FOR_LARGER_SCREEN;
 
 import android.animation.PropertyValuesHolder;
 import android.annotation.SuppressLint;
@@ -25,10 +26,12 @@
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.view.WindowInsets;
+import android.widget.ScrollView;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
@@ -44,6 +47,9 @@
     private static final int DEFAULT_CLOSE_DURATION = 200;
 
     private final Rect mInsets;
+    private ScrollView mWidgetPreviewScrollView;
+
+    private int mContentHorizontalMarginInPx;
 
     public AddItemWidgetsBottomSheet(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -52,6 +58,8 @@
     public AddItemWidgetsBottomSheet(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         mInsets = new Rect();
+        mContentHorizontalMarginInPx = getResources().getDimensionPixelSize(
+                R.dimen.widget_list_horizontal_margin);
     }
 
     /**
@@ -68,6 +76,19 @@
     }
 
     @Override
+    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            mNoIntercept = false;
+            // Suppress drag to dismiss gesture if the scroll view is being scrolled.
+            if (getPopupContainer().isEventOverView(mWidgetPreviewScrollView, ev)
+                    && mWidgetPreviewScrollView.getScrollY() > 0) {
+                mNoIntercept = true;
+            }
+        }
+        return super.onControllerInterceptTouchEvent(ev);
+    }
+
+    @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         int width = r - l;
         int height = b - t;
@@ -93,6 +114,15 @@
                     2 * (mInsets.left + mInsets.right));
         }
 
+        if (deviceProfile.isTablet || deviceProfile.isTwoPanels) {
+            // In large screen devices, we restrict the width of the widgets picker to show part of
+            // the home screen. Let's ensure the minimum width used is at least the minimum width
+            // that isn't taken by the widgets picker.
+            int minUsedWidth = (int) (deviceProfile.availableWidthPx
+                    * (1 - MAX_WIDTH_SCALE_FOR_LARGER_SCREEN));
+            widthUsed = Math.max(widthUsed, minUsedWidth);
+        }
+
         int heightUsed = mInsets.top + deviceProfile.edgeMarginPx;
         measureChildWithMargins(mContent, widthMeasureSpec,
                 widthUsed, heightMeasureSpec, heightUsed);
@@ -104,6 +134,7 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mContent = findViewById(R.id.add_item_bottom_sheet_content);
+        mWidgetPreviewScrollView = findViewById(R.id.widget_preview_scroll_view);
     }
 
     private void animateOpen() {
@@ -146,6 +177,26 @@
         }
         mContent.setPadding(mContent.getPaddingStart(),
                 mContent.getPaddingTop(), mContent.getPaddingEnd(), mInsets.bottom);
+
+        int contentHorizontalMarginInPx = getResources().getDimensionPixelSize(
+                R.dimen.widget_list_horizontal_margin);
+        if (contentHorizontalMarginInPx != mContentHorizontalMarginInPx) {
+            setContentHorizontalMargin(findViewById(R.id.widget_appName),
+                    contentHorizontalMarginInPx);
+            setContentHorizontalMargin(findViewById(R.id.widget_drag_instruction),
+                    contentHorizontalMarginInPx);
+            setContentHorizontalMargin(findViewById(R.id.widget_cell), contentHorizontalMarginInPx);
+            setContentHorizontalMargin(findViewById(R.id.actions_container),
+                    contentHorizontalMarginInPx);
+            mContentHorizontalMarginInPx = contentHorizontalMarginInPx;
+        }
         return windowInsets;
     }
+
+    private static void setContentHorizontalMargin(View view, int contentHorizontalMargin) {
+        ViewGroup.MarginLayoutParams layoutParams =
+                ((ViewGroup.MarginLayoutParams) view.getLayoutParams());
+        layoutParams.setMarginStart(contentHorizontalMargin);
+        layoutParams.setMarginEnd(contentHorizontalMargin);
+    }
 }
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 9f0b9d9..00a0050 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -19,6 +19,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnLongClickListener;
@@ -51,6 +52,13 @@
 public abstract class BaseWidgetSheet extends AbstractSlideInView<Launcher>
         implements OnClickListener, OnLongClickListener, DragSource,
         PopupDataProvider.PopupDataChangeListener, Insettable {
+    /** The default number of cells that can fit horizontally in a widget sheet. */
+    protected static final int DEFAULT_MAX_HORIZONTAL_SPANS = 4;
+    /**
+     * The maximum scale, [0, 1], of the device screen width that the widgets picker can consume
+     * on large screen devices.
+     */
+    protected static final float MAX_WIDTH_SCALE_FOR_LARGER_SCREEN = 0.89f;
 
     protected static final String KEY_WIDGETS_EDUCATION_TIP_SEEN =
             "launcher.widgets_education_tip_seen";
@@ -59,8 +67,12 @@
     /* Touch handling related member variables. */
     private Toast mWidgetInstructionToast;
 
+    private int mContentHorizontalMarginInPx;
+
     public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
+        mContentHorizontalMarginInPx = getResources().getDimensionPixelSize(
+                R.dimen.widget_list_horizontal_margin);
     }
 
     protected int getScrimColor(Context context) {
@@ -97,6 +109,9 @@
 
     @Override
     public boolean onLongClick(View v) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_DROP_TARGET, "1");
+        }
         TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Widgets.onLongClick");
         v.cancelLongPress();
         if (!ItemLongClickListener.canStartDrag(mActivityContext)) return false;
@@ -112,8 +127,16 @@
     @Override
     public void setInsets(Rect insets) {
         mInsets.set(insets);
+        int contentHorizontalMarginInPx = getResources().getDimensionPixelSize(
+                R.dimen.widget_list_horizontal_margin);
+        if (contentHorizontalMarginInPx != mContentHorizontalMarginInPx) {
+            onContentHorizontalMarginChanged(contentHorizontalMarginInPx);
+            mContentHorizontalMarginInPx = contentHorizontalMarginInPx;
+        }
     }
 
+    /** Called when the horizontal margin of the content view has changed. */
+    protected abstract void onContentHorizontalMarginChanged(int contentHorizontalMarginInPx);
 
     /**
      * Measures the dimension of this view and its children by taking system insets, navigation bar,
@@ -131,6 +154,15 @@
                     2 * (mInsets.left + mInsets.right));
         }
 
+        if (deviceProfile.isTablet || deviceProfile.isTwoPanels) {
+            // In large screen devices, we restrict the width of the widgets picker to show part of
+            // the home screen. Let's ensure the minimum width used is at least the minimum width
+            // that isn't taken by the widgets picker.
+            int minUsedWidth = (int) (deviceProfile.availableWidthPx
+                    * (1 - MAX_WIDTH_SCALE_FOR_LARGER_SCREEN));
+            widthUsed = Math.max(widthUsed, minUsedWidth);
+        }
+
         int heightUsed = mInsets.top + deviceProfile.edgeMarginPx;
         measureChildWithMargins(mContent, widthMeasureSpec,
                 widthUsed, heightMeasureSpec, heightUsed);
@@ -138,7 +170,21 @@
                 MeasureSpec.getSize(heightMeasureSpec));
     }
 
+    /** Returns the number of cells that can fit horizontally in a given {@code content}. */
+    protected int computeMaxHorizontalSpans(View content, int contentHorizontalPaddingPx) {
+        DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
+        int availableWidth = content.getMeasuredWidth() - contentHorizontalPaddingPx;
+        Point cellSize = deviceProfile.getCellSize();
+        if (cellSize.x > 0) {
+            return availableWidth / cellSize.x;
+        }
+        return DEFAULT_MAX_HORIZONTAL_SPANS;
+    }
+
     private boolean beginDraggingWidget(WidgetCell v) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_DROP_TARGET, "2");
+        }
         // Get the widget preview as the drag representation
         WidgetImageView image = v.getWidgetView();
 
@@ -149,7 +195,9 @@
         }
 
         PendingItemDragHelper dragHelper = new PendingItemDragHelper(v);
-        dragHelper.setRemoteViewsPreview(v.getRemoteViewsPreview());
+        // RemoteViews are being rendered in AppWidgetHostView in WidgetCell. And thus, the scale of
+        // RemoteViews is equivalent to the AppWidgetHostView scale.
+        dragHelper.setRemoteViewsPreview(v.getRemoteViewsPreview(), v.getAppWidgetHostViewScale());
         dragHelper.setAppWidgetHostViewPreview(v.getAppWidgetHostViewPreview());
 
         if (image.getDrawable() != null) {
@@ -159,11 +207,11 @@
             dragHelper.startDrag(image.getBitmapBounds(), image.getDrawable().getIntrinsicWidth(),
                     image.getWidth(), new Point(loc[0], loc[1]), this, new DragOptions());
         } else {
-            View preview = v.getAppWidgetHostViewPreview();
+            NavigableAppWidgetHostView preview = v.getAppWidgetHostViewPreview();
             int[] loc = new int[2];
             getPopupContainer().getLocationInDragLayer(preview, loc);
-
-            Rect r = new Rect(0, 0, preview.getWidth(), preview.getHeight());
+            Rect r = new Rect();
+            preview.getWorkspaceVisualDragBounds(r);
             dragHelper.startDrag(r, preview.getMeasuredWidth(), preview.getMeasuredWidth(),
                     new Point(loc[0], loc[1]), this, new DragOptions());
         }
diff --git a/src/com/android/launcher3/widget/CachingWidgetPreviewLoader.java b/src/com/android/launcher3/widget/CachingWidgetPreviewLoader.java
deleted file mode 100644
index afceadd..0000000
--- a/src/com/android/launcher3/widget/CachingWidgetPreviewLoader.java
+++ /dev/null
@@ -1,289 +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.widget;
-
-import android.graphics.Bitmap;
-import android.os.CancellationSignal;
-import android.util.Size;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
-import androidx.collection.ArrayMap;
-import androidx.collection.ArraySet;
-
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.util.ComponentKey;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-/** Wrapper around {@link DatabaseWidgetPreviewLoader} that contains caching logic. */
-public class CachingWidgetPreviewLoader implements WidgetPreviewLoader {
-
-    @NonNull private final WidgetPreviewLoader mDelegate;
-    @NonNull private final Map<ComponentKey, Map<Size, CacheResult>> mCache = new ArrayMap<>();
-
-    public CachingWidgetPreviewLoader(@NonNull WidgetPreviewLoader delegate) {
-        mDelegate = delegate;
-    }
-
-    /** Returns whether the preview is loaded for the item and size. */
-    public boolean isPreviewLoaded(@NonNull WidgetItem item, @NonNull Size previewSize) {
-        return getPreview(item, previewSize) != null;
-    }
-
-    /** Returns the cached preview for the item and size, or null if there is none. */
-    @Nullable
-    public Bitmap getPreview(@NonNull WidgetItem item, @NonNull Size previewSize) {
-        CacheResult cacheResult = getCacheResult(item, previewSize);
-        if (cacheResult instanceof CacheResult.Loaded) {
-            return ((CacheResult.Loaded) cacheResult).mBitmap;
-        } else {
-            return null;
-        }
-    }
-
-    @NonNull
-    private CacheResult getCacheResult(@NonNull WidgetItem item, @NonNull Size previewSize) {
-        synchronized (mCache) {
-            Map<Size, CacheResult> cacheResults = mCache.get(toComponentKey(item));
-            if (cacheResults == null) {
-                return CacheResult.MISS;
-            }
-
-            return cacheResults.getOrDefault(previewSize, CacheResult.MISS);
-        }
-    }
-
-    /**
-     * Puts the result in the cache for the item and size. Returns the value previously in the
-     * cache, or null if there was none.
-     */
-    @Nullable
-    private CacheResult putCacheResult(
-            @NonNull WidgetItem item,
-            @NonNull Size previewSize,
-            @Nullable CacheResult cacheResult) {
-        ComponentKey key = toComponentKey(item);
-        synchronized (mCache) {
-            Map<Size, CacheResult> cacheResults = mCache.getOrDefault(key, new ArrayMap<>());
-            CacheResult previous;
-            if (cacheResult == null) {
-                previous = cacheResults.remove(previewSize);
-                if (cacheResults.isEmpty()) {
-                    mCache.remove(key);
-                } else {
-                    previous = cacheResults.put(previewSize, cacheResult);
-                    mCache.put(key, cacheResults);
-                }
-            } else {
-                previous = cacheResults.put(previewSize, cacheResult);
-                mCache.put(key, cacheResults);
-            }
-            return previous;
-        }
-    }
-
-    private void removeCacheResult(@NonNull WidgetItem item, @NonNull Size previewSize) {
-        ComponentKey key = toComponentKey(item);
-        synchronized (mCache) {
-            Map<Size, CacheResult> cacheResults = mCache.getOrDefault(key, new ArrayMap<>());
-            cacheResults.remove(previewSize);
-            mCache.put(key, cacheResults);
-        }
-    }
-
-    /**
-     * Gets the preview for the widget item and size, using the value in the cache if stored.
-     *
-     * @return a {@link CancellationSignal}, which can cancel the request before it loads
-     */
-    @Override
-    @UiThread
-    @NonNull
-    public CancellationSignal loadPreview(
-            @NonNull BaseActivity activity, @NonNull WidgetItem item, @NonNull Size previewSize,
-            @NonNull WidgetPreviewLoadedCallback callback) {
-        CancellationSignal signal = new CancellationSignal();
-        signal.setOnCancelListener(() -> {
-            synchronized (mCache) {
-                CacheResult cacheResult = getCacheResult(item, previewSize);
-                if (!(cacheResult instanceof CacheResult.Loading)) {
-                    // If the key isn't actively loading, then this is a no-op. Cancelling loading
-                    // shouldn't clear the cache if we've already loaded.
-                    return;
-                }
-
-                CacheResult.Loading prev = (CacheResult.Loading) cacheResult;
-                CacheResult.Loading updated = prev.withoutCallback(callback);
-
-                if (updated.mCallbacks.isEmpty()) {
-                    // If the last callback was removed, then cancel the underlying request in the
-                    // delegate.
-                    prev.mCancellationSignal.cancel();
-                    removeCacheResult(item, previewSize);
-                } else {
-                    // If there are other callbacks still active, then don't cancel the delegate's
-                    // request, just remove this callback from the set.
-                    putCacheResult(item, previewSize, updated);
-                }
-            }
-        });
-
-        synchronized (mCache) {
-            CacheResult cacheResult = getCacheResult(item, previewSize);
-            if (cacheResult instanceof CacheResult.Loaded) {
-                // If the bitmap is already present in the cache, invoke the callback immediately.
-                callback.onPreviewLoaded(((CacheResult.Loaded) cacheResult).mBitmap);
-                return signal;
-            }
-
-            if (cacheResult instanceof CacheResult.Loading) {
-                // If we're already loading the preview for this key, then just add the callback
-                // to the set we'll call after it loads.
-                CacheResult.Loading prev = (CacheResult.Loading) cacheResult;
-                putCacheResult(item, previewSize, prev.withCallback(callback));
-                return signal;
-            }
-
-            CancellationSignal delegateCancellationSignal =
-                    mDelegate.loadPreview(
-                            activity,
-                            item,
-                            previewSize,
-                            preview -> {
-                                CacheResult prev;
-                                synchronized (mCache) {
-                                    prev = putCacheResult(
-                                            item, previewSize, new CacheResult.Loaded(preview));
-                                }
-                                if (prev instanceof CacheResult.Loading) {
-                                    // Notify each stored callback that the preview has loaded.
-                                    ((CacheResult.Loading) prev).mCallbacks
-                                            .forEach(c -> c.onPreviewLoaded(preview));
-                                } else {
-                                    // If there isn't a loading object in the cache, then we were
-                                    // notified before adding this signal to the cache. Just
-                                    // call back to the provided callback, there can't be others.
-                                    callback.onPreviewLoaded(preview);
-                                }
-                            });
-            ArraySet<WidgetPreviewLoadedCallback> callbacks = new ArraySet<>();
-            callbacks.add(callback);
-            putCacheResult(
-                    item,
-                    previewSize,
-                    new CacheResult.Loading(delegateCancellationSignal, callbacks));
-        }
-
-        return signal;
-    }
-
-    /** Clears all cached previews for {@code items}, cancelling any in-progress preview loading. */
-    public void clearPreviews(Iterable<WidgetItem> items) {
-        List<CacheResult> previousCacheResults = new ArrayList<>();
-        synchronized (mCache) {
-            for (WidgetItem item : items) {
-                Map<Size, CacheResult> previousMap = mCache.remove(toComponentKey(item));
-                if (previousMap != null) {
-                    previousCacheResults.addAll(previousMap.values());
-                }
-            }
-        }
-
-        for (CacheResult previousCacheResult : previousCacheResults) {
-            if (previousCacheResult instanceof CacheResult.Loading) {
-                ((CacheResult.Loading) previousCacheResult).mCancellationSignal.cancel();
-            }
-        }
-    }
-
-    /** Clears all cached previews, cancelling any in-progress preview loading. */
-    public void clearAll() {
-        List<CacheResult> previousCacheResults;
-        synchronized (mCache) {
-            previousCacheResults =
-                    mCache
-                    .values()
-                    .stream()
-                    .flatMap(sizeToResult -> sizeToResult.values().stream())
-                    .collect(Collectors.toList());
-            mCache.clear();
-        }
-
-        for (CacheResult previousCacheResult : previousCacheResults) {
-            if (previousCacheResult instanceof CacheResult.Loading) {
-                ((CacheResult.Loading) previousCacheResult).mCancellationSignal.cancel();
-            }
-        }
-    }
-
-    private abstract static class CacheResult {
-        static final CacheResult MISS = new CacheResult() {};
-
-        static final class Loading extends CacheResult {
-            @NonNull final CancellationSignal mCancellationSignal;
-            @NonNull final Set<WidgetPreviewLoadedCallback> mCallbacks;
-
-            Loading(@NonNull CancellationSignal cancellationSignal,
-                    @NonNull Set<WidgetPreviewLoadedCallback> callbacks) {
-                mCancellationSignal = cancellationSignal;
-                mCallbacks = callbacks;
-            }
-
-            @NonNull
-            Loading withCallback(@NonNull WidgetPreviewLoadedCallback callback) {
-                if (mCallbacks.contains(callback)) return this;
-                Set<WidgetPreviewLoadedCallback> newCallbacks =
-                        new ArraySet<>(mCallbacks.size() + 1);
-                newCallbacks.addAll(mCallbacks);
-                newCallbacks.add(callback);
-                return new Loading(mCancellationSignal, newCallbacks);
-            }
-
-            @NonNull
-            Loading withoutCallback(@NonNull WidgetPreviewLoadedCallback callback) {
-                if (!mCallbacks.contains(callback)) return this;
-                Set<WidgetPreviewLoadedCallback> newCallbacks =
-                        new ArraySet<>(mCallbacks.size() - 1);
-                for (WidgetPreviewLoadedCallback existingCallback : mCallbacks) {
-                    if (!existingCallback.equals(callback)) {
-                        newCallbacks.add(existingCallback);
-                    }
-                }
-                return new Loading(mCancellationSignal, newCallbacks);
-            }
-        }
-
-        static final class Loaded extends CacheResult {
-            @NonNull final Bitmap mBitmap;
-
-            Loaded(@NonNull Bitmap bitmap) {
-                mBitmap = bitmap;
-            }
-        }
-    }
-
-    @NonNull
-    private static ComponentKey toComponentKey(@NonNull WidgetItem item) {
-        return new ComponentKey(item.componentName, item.user);
-    }
-}
diff --git a/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java b/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java
index 4ec7e60..aacb9c5 100644
--- a/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java
+++ b/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java
@@ -16,21 +16,10 @@
 package com.android.launcher3.widget;
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
-import android.content.ComponentName;
-import android.content.ContentValues;
 import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
-import android.database.Cursor;
-import android.database.SQLException;
-import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
@@ -39,72 +28,40 @@
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
-import android.os.CancellationSignal;
+import android.os.Handler;
 import android.os.Process;
-import android.os.UserHandle;
 import android.util.Log;
-import android.util.LongSparseArray;
-import android.util.Pair;
 import android.util.Size;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
-import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.icons.GraphicsUtils;
-import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.BitmapRenderer;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.icons.ShadowGenerator;
+import com.android.launcher3.icons.cache.HandlerRunnable;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.pm.ShortcutConfigActivityInfo;
-import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.Executors;
-import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.SQLiteCacheHelper;
-import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.widget.util.WidgetSizes;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.WeakHashMap;
 import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
 
-/** {@link WidgetPreviewLoader} that loads preview images from a {@link CacheDb}. */
-public class DatabaseWidgetPreviewLoader implements WidgetPreviewLoader {
+/** Utility class to load widget previews */
+public class DatabaseWidgetPreviewLoader {
 
     private static final String TAG = "WidgetPreviewLoader";
-    private static final boolean DEBUG = false;
-
-    private final HashMap<String, long[]> mPackageVersions = new HashMap<>();
-
-    /**
-     * Weak reference objects, do not prevent their referents from being made finalizable,
-     * finalized, and then reclaimed.
-     * Note: synchronized block used for this variable is expensive and the block should always
-     * be posted to a background thread.
-     */
-    @Thunk final Set<Bitmap> mUnusedBitmaps = Collections.newSetFromMap(new WeakHashMap<>());
 
     private final Context mContext;
-    private final IconCache mIconCache;
-    private final UserCache mUserCache;
-    private final CacheDb mDb;
     private final float mPreviewBoxCornerRadius;
 
-    public DatabaseWidgetPreviewLoader(Context context, IconCache iconCache) {
+    public DatabaseWidgetPreviewLoader(Context context) {
         mContext = context;
-        mIconCache = iconCache;
-        mUserCache = UserCache.INSTANCE.get(context);
-        mDb = new CacheDb(context);
         float previewCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(context);
         mPreviewBoxCornerRadius = previewCornerRadius > 0
                 ? previewCornerRadius
@@ -117,251 +74,29 @@
      *
      * @return a request id which can be used to cancel the request.
      */
-    @Override
     @NonNull
-    public CancellationSignal loadPreview(
-            @NonNull BaseActivity activity,
+    public HandlerRunnable loadPreview(
             @NonNull WidgetItem item,
             @NonNull Size previewSize,
-            @NonNull WidgetPreviewLoadedCallback callback) {
-        int previewWidth = previewSize.getWidth();
-        int previewHeight = previewSize.getHeight();
-        String size = previewWidth + "x" + previewHeight;
-        WidgetCacheKey key = new WidgetCacheKey(item.componentName, item.user, size);
-
-        PreviewLoadTask task =
-                new PreviewLoadTask(activity, key, item, previewWidth, previewHeight, callback);
-        task.executeOnExecutor(Executors.THREAD_POOL_EXECUTOR);
-
-        CancellationSignal signal = new CancellationSignal();
-        signal.setOnCancelListener(task);
-        return signal;
-    }
-
-    /** Clears the database storing previews. */
-    public void refresh() {
-        mDb.clear();
-    }
-
-    /**
-     * The DB holds the generated previews for various components. Previews can also have different
-     * sizes (landscape vs portrait).
-     */
-    private static class CacheDb extends SQLiteCacheHelper {
-        private static final int DB_VERSION = 9;
-
-        private static final String TABLE_NAME = "shortcut_and_widget_previews";
-        private static final String COLUMN_COMPONENT = "componentName";
-        private static final String COLUMN_USER = "profileId";
-        private static final String COLUMN_SIZE = "size";
-        private static final String COLUMN_PACKAGE = "packageName";
-        private static final String COLUMN_LAST_UPDATED = "lastUpdated";
-        private static final String COLUMN_VERSION = "version";
-        private static final String COLUMN_PREVIEW_BITMAP = "preview_bitmap";
-
-        CacheDb(Context context) {
-            super(context, LauncherFiles.WIDGET_PREVIEWS_DB, DB_VERSION, TABLE_NAME);
-        }
-
-        @Override
-        public void onCreateTable(SQLiteDatabase database) {
-            database.execSQL("CREATE TABLE IF NOT EXISTS "
-                    + TABLE_NAME
-                    + " ("
-                    + COLUMN_COMPONENT
-                    + " TEXT NOT NULL, "
-                    + COLUMN_USER
-                    + " INTEGER NOT NULL, "
-                    + COLUMN_SIZE
-                    + " TEXT NOT NULL, "
-                    + COLUMN_PACKAGE
-                    + " TEXT NOT NULL, "
-                    + COLUMN_LAST_UPDATED
-                    + " INTEGER NOT NULL DEFAULT 0, "
-                    + COLUMN_VERSION
-                    + " INTEGER NOT NULL DEFAULT 0, "
-                    + COLUMN_PREVIEW_BITMAP
-                    + " BLOB, "
-                    + "PRIMARY KEY ("
-                    + COLUMN_COMPONENT
-                    + ", "
-                    + COLUMN_USER
-                    + ", "
-                    + COLUMN_SIZE
-                    + ") "
-                    +
-                    ");");
-        }
-    }
-
-    @Thunk void writeToDb(WidgetCacheKey key, long[] versions, Bitmap preview) {
-        ContentValues values = new ContentValues();
-        values.put(CacheDb.COLUMN_COMPONENT, key.componentName.flattenToShortString());
-        values.put(CacheDb.COLUMN_USER, mUserCache.getSerialNumberForUser(key.user));
-        values.put(CacheDb.COLUMN_SIZE, key.mSize);
-        values.put(CacheDb.COLUMN_PACKAGE, key.componentName.getPackageName());
-        values.put(CacheDb.COLUMN_VERSION, versions[0]);
-        values.put(CacheDb.COLUMN_LAST_UPDATED, versions[1]);
-        values.put(CacheDb.COLUMN_PREVIEW_BITMAP, GraphicsUtils.flattenBitmap(preview));
-        mDb.insertOrReplace(values);
-    }
-
-    /** Removes the package from the preview database. */
-    public void removePackage(String packageName, UserHandle user) {
-        removePackage(packageName, user, mUserCache.getSerialNumberForUser(user));
-    }
-
-    /** Removes the package from the preview database. */
-    public void removePackage(String packageName, UserHandle user, long userSerial) {
-        synchronized (mPackageVersions) {
-            mPackageVersions.remove(packageName);
-        }
-
-        mDb.delete(
-                CacheDb.COLUMN_PACKAGE + " = ? AND " + CacheDb.COLUMN_USER + " = ?",
-                new String[]{packageName, Long.toString(userSerial)});
-    }
-
-    /**
-     * Updates the persistent DB:
-     *   1. Any preview generated for an old package version is removed
-     *   2. Any preview for an absent package is removed
-     * This ensures that we remove entries for packages which changed while the launcher was dead.
-     *
-     * @param packageUser if provided, specifies that list only contains previews for the
-     *                    given package/user, otherwise the list contains all previews
-     */
-    public void removeObsoletePreviews(ArrayList<? extends ComponentKey> list,
-            @Nullable PackageUserKey packageUser) {
-        Preconditions.assertWorkerThread();
-
-        LongSparseArray<HashSet<String>> validPackages = new LongSparseArray<>();
-
-        for (ComponentKey key : list) {
-            final long userId = mUserCache.getSerialNumberForUser(key.user);
-            HashSet<String> packages = validPackages.get(userId);
-            if (packages == null) {
-                packages = new HashSet<>();
-                validPackages.put(userId, packages);
-            }
-            packages.add(key.componentName.getPackageName());
-        }
-
-        LongSparseArray<HashSet<String>> packagesToDelete = new LongSparseArray<>();
-        long passedUserId = packageUser == null ? 0
-                : mUserCache.getSerialNumberForUser(packageUser.mUser);
-        Cursor c = null;
-        try {
-            c = mDb.query(
-                    new String[]{CacheDb.COLUMN_USER, CacheDb.COLUMN_PACKAGE,
-                            CacheDb.COLUMN_LAST_UPDATED, CacheDb.COLUMN_VERSION},
-                    null, null);
-            while (c.moveToNext()) {
-                long userId = c.getLong(0);
-                String pkg = c.getString(1);
-                long lastUpdated = c.getLong(2);
-                long version = c.getLong(3);
-
-                if (packageUser != null && (!pkg.equals(packageUser.mPackageName)
-                        || userId != passedUserId)) {
-                    // This preview is associated with a different package/user, no need to remove.
-                    continue;
-                }
-
-                HashSet<String> packages = validPackages.get(userId);
-                if (packages != null && packages.contains(pkg)) {
-                    long[] versions = getPackageVersion(pkg);
-                    if (versions[0] == version && versions[1] == lastUpdated) {
-                        // Every thing checks out
-                        continue;
-                    }
-                }
-
-                // We need to delete this package.
-                packages = packagesToDelete.get(userId);
-                if (packages == null) {
-                    packages = new HashSet<>();
-                    packagesToDelete.put(userId, packages);
-                }
-                packages.add(pkg);
-            }
-
-            for (int i = 0; i < packagesToDelete.size(); i++) {
-                long userId = packagesToDelete.keyAt(i);
-                UserHandle user = mUserCache.getUserForSerialNumber(userId);
-                for (String pkg : packagesToDelete.valueAt(i)) {
-                    removePackage(pkg, user, userId);
-                }
-            }
-        } catch (SQLException e) {
-            Log.e(TAG, "Error updating widget previews", e);
-        } finally {
-            if (c != null) {
-                c.close();
-            }
-        }
-    }
-
-    /**
-     * Reads the preview bitmap from the DB or null if the preview is not in the DB.
-     */
-    @Thunk Bitmap readFromDb(WidgetCacheKey key, Bitmap recycle, PreviewLoadTask loadTask) {
-        Cursor cursor = null;
-        try {
-            cursor = mDb.query(
-                    new String[]{CacheDb.COLUMN_PREVIEW_BITMAP},
-                    CacheDb.COLUMN_COMPONENT + " = ? AND " + CacheDb.COLUMN_USER + " = ? AND "
-                            + CacheDb.COLUMN_SIZE + " = ?",
-                    new String[]{
-                            key.componentName.flattenToShortString(),
-                            Long.toString(mUserCache.getSerialNumberForUser(key.user)),
-                            key.mSize
-                    });
-            // If cancelled, skip getting the blob and decoding it into a bitmap
-            if (loadTask.isCancelled()) {
-                return null;
-            }
-            if (cursor.moveToNext()) {
-                byte[] blob = cursor.getBlob(0);
-                BitmapFactory.Options opts = new BitmapFactory.Options();
-                opts.inBitmap = recycle;
-                try {
-                    if (!loadTask.isCancelled()) {
-                        return BitmapFactory.decodeByteArray(blob, 0, blob.length, opts);
-                    }
-                } catch (Exception e) {
-                    return null;
-                }
-            }
-        } catch (SQLException e) {
-            Log.w(TAG, "Error loading preview from DB", e);
-        } finally {
-            if (cursor != null) {
-                cursor.close();
-            }
-        }
-        return null;
+            @NonNull Consumer<Bitmap> callback) {
+        Handler handler = Executors.UI_HELPER_EXECUTOR.getHandler();
+        HandlerRunnable<Bitmap> request = new HandlerRunnable<>(handler,
+                () -> generatePreview(item, previewSize.getWidth(), previewSize.getHeight()),
+                MAIN_EXECUTOR,
+                callback);
+        Utilities.postAsyncCallback(handler, request);
+        return request;
     }
 
     /**
      * Returns a generated preview for a widget and if the preview should be saved in persistent
      * storage.
-     * @param launcher
-     * @param item
-     * @param recycle
-     * @param previewWidth
-     * @param previewHeight
-     * @return Pair<Bitmap, Boolean>
      */
-    private Pair<Bitmap, Boolean> generatePreview(BaseActivity launcher, WidgetItem item,
-            Bitmap recycle,
-            int previewWidth, int previewHeight) {
+    private Bitmap generatePreview(WidgetItem item, int previewWidth, int previewHeight) {
         if (item.widgetInfo != null) {
-            return generateWidgetPreview(launcher, item.widgetInfo,
-                    previewWidth, recycle, null);
+            return generateWidgetPreview(item.widgetInfo, previewWidth, null);
         } else {
-            return new Pair<>(generateShortcutPreview(launcher, item.activityInfo,
-                    previewWidth, previewHeight, recycle), false);
+            return generateShortcutPreview(item.activityInfo, previewWidth, previewHeight);
         }
     }
 
@@ -369,16 +104,12 @@
      * Generates the widget preview from either the {@link WidgetManagerHelper} or cache
      * and add badge at the bottom right corner.
      *
-     * @param launcher
      * @param info                        information about the widget
      * @param maxPreviewWidth             width of the preview on either workspace or tray
-     * @param preview                     bitmap that can be recycled
      * @param preScaledWidthOut           return the width of the returned bitmap
-     * @return Pair<Bitmap (the preview) , Boolean (should be stored in db)>
      */
-    public Pair<Bitmap, Boolean> generateWidgetPreview(BaseActivity launcher,
-            LauncherAppWidgetProviderInfo info,
-            int maxPreviewWidth, Bitmap preview, int[] preScaledWidthOut) {
+    public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info,
+            int maxPreviewWidth, int[] preScaledWidthOut) {
         // Load the preview image if possible
         if (maxPreviewWidth < 0) maxPreviewWidth = Integer.MAX_VALUE;
 
@@ -409,117 +140,97 @@
         int previewWidth;
         int previewHeight;
 
-        boolean savePreviewImage = widgetPreviewExists || info.previewImage == 0;
+        DeviceProfile dp = ActivityContext.lookupContext(mContext).getDeviceProfile();
 
         if (widgetPreviewExists && drawable.getIntrinsicWidth() > 0
                 && drawable.getIntrinsicHeight() > 0) {
             previewWidth = drawable.getIntrinsicWidth();
             previewHeight = drawable.getIntrinsicHeight();
         } else {
-            DeviceProfile dp = launcher.getDeviceProfile();
             Size widgetSize = WidgetSizes.getWidgetPaddedSizePx(mContext, info.provider, dp, spanX,
                     spanY);
             previewWidth = widgetSize.getWidth();
             previewHeight = widgetSize.getHeight();
         }
 
-        // Scale to fit width only - let the widget preview be clipped in the
-        // vertical dimension
-        float scale = 1f;
         if (preScaledWidthOut != null) {
             preScaledWidthOut[0] = previewWidth;
         }
-        if (previewWidth > maxPreviewWidth) {
-            scale = maxPreviewWidth / (float) (previewWidth);
-        }
+        // Scale to fit width only - let the widget preview be clipped in the
+        // vertical dimension
+        final float scale = previewWidth > maxPreviewWidth
+                ? (maxPreviewWidth / (float) (previewWidth)) : 1f;
         if (scale != 1f) {
             previewWidth = Math.max((int) (scale * previewWidth), 1);
             previewHeight = Math.max((int) (scale * previewHeight), 1);
         }
 
-        final Canvas c = new Canvas();
-        if (preview == null) {
-            // If no bitmap was provided, then allocate a new one with the right size.
-            preview = Bitmap.createBitmap(previewWidth, previewHeight, Config.ARGB_8888);
-            c.setBitmap(preview);
-        } else {
-            // If a bitmap was passed in, attempt to reconfigure the bitmap to the same dimensions
-            // as the preview.
-            try {
-                preview.reconfigure(previewWidth, previewHeight, preview.getConfig());
-            } catch (IllegalArgumentException e) {
-                // This occurs if the preview can't be reconfigured for any reason. In this case,
-                // allocate a new bitmap with the right size.
-                preview = Bitmap.createBitmap(previewWidth, previewHeight, Config.ARGB_8888);
-            }
+        final int previewWidthF = previewWidth;
+        final int previewHeightF = previewHeight;
+        final Drawable drawableF = drawable;
 
-            c.setBitmap(preview);
-            c.drawColor(0, PorterDuff.Mode.CLEAR);
-        }
-
-        // Draw the scaled preview into the final bitmap
-        if (widgetPreviewExists) {
-            drawable.setBounds(0, 0, previewWidth, previewHeight);
-            drawable.draw(c);
-        } else {
-            RectF boxRect;
-
-            // Draw horizontal and vertical lines to represent individual columns.
-            final Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
-
-            if (Utilities.ATLEAST_S) {
-                boxRect = new RectF(/* left= */ 0, /* top= */ 0, /* right= */
-                        previewWidth, /* bottom= */ previewHeight);
-
-                p.setStyle(Paint.Style.FILL);
-                p.setColor(Color.WHITE);
-                float roundedCorner = mContext.getResources().getDimension(
-                        android.R.dimen.system_app_widget_background_radius);
-                c.drawRoundRect(boxRect, roundedCorner, roundedCorner, p);
+        return BitmapRenderer.createHardwareBitmap(previewWidth, previewHeight, c -> {
+            // Draw the scaled preview into the final bitmap
+            if (widgetPreviewExists) {
+                drawableF.setBounds(0, 0, previewWidthF, previewHeightF);
+                drawableF.draw(c);
             } else {
-                boxRect = drawBoxWithShadow(c, previewWidth, previewHeight);
-            }
+                RectF boxRect;
 
-            p.setStyle(Paint.Style.STROKE);
-            p.setStrokeWidth(mContext.getResources()
-                    .getDimension(R.dimen.widget_preview_cell_divider_width));
-            p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+                // Draw horizontal and vertical lines to represent individual columns.
+                final Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
 
-            float t = boxRect.left;
-            float tileSize = boxRect.width() / spanX;
-            for (int i = 1; i < spanX; i++) {
-                t += tileSize;
-                c.drawLine(t, 0, t, previewHeight, p);
-            }
+                if (Utilities.ATLEAST_S) {
+                    boxRect = new RectF(/* left= */ 0, /* top= */ 0, /* right= */
+                            previewWidthF, /* bottom= */ previewHeightF);
 
-            t = boxRect.top;
-            tileSize = boxRect.height() / spanY;
-            for (int i = 1; i < spanY; i++) {
-                t += tileSize;
-                c.drawLine(0, t, previewWidth, t, p);
-            }
-
-            // Draw icon in the center.
-            try {
-                Drawable icon =
-                        mIconCache.getFullResIcon(info.provider.getPackageName(), info.icon);
-                if (icon != null) {
-                    int appIconSize = launcher.getDeviceProfile().iconSizePx;
-                    int iconSize = (int) Math.min(appIconSize * scale,
-                            Math.min(boxRect.width(), boxRect.height()));
-
-                    icon = mutateOnMainThread(icon);
-                    int hoffset = (previewWidth - iconSize) / 2;
-                    int yoffset = (previewHeight - iconSize) / 2;
-                    icon.setBounds(hoffset, yoffset, hoffset + iconSize, yoffset + iconSize);
-                    icon.draw(c);
+                    p.setStyle(Paint.Style.FILL);
+                    p.setColor(Color.WHITE);
+                    float roundedCorner = mContext.getResources().getDimension(
+                            android.R.dimen.system_app_widget_background_radius);
+                    c.drawRoundRect(boxRect, roundedCorner, roundedCorner, p);
+                } else {
+                    boxRect = drawBoxWithShadow(c, previewWidthF, previewHeightF);
                 }
-            } catch (Resources.NotFoundException e) {
-                savePreviewImage = false;
+
+                p.setStyle(Paint.Style.STROKE);
+                p.setStrokeWidth(mContext.getResources()
+                        .getDimension(R.dimen.widget_preview_cell_divider_width));
+                p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+
+                float t = boxRect.left;
+                float tileSize = boxRect.width() / spanX;
+                for (int i = 1; i < spanX; i++) {
+                    t += tileSize;
+                    c.drawLine(t, 0, t, previewHeightF, p);
+                }
+
+                t = boxRect.top;
+                tileSize = boxRect.height() / spanY;
+                for (int i = 1; i < spanY; i++) {
+                    t += tileSize;
+                    c.drawLine(0, t, previewWidthF, t, p);
+                }
+
+                // Draw icon in the center.
+                try {
+                    Drawable icon = LauncherAppState.getInstance(mContext).getIconCache()
+                            .getFullResIcon(info.provider.getPackageName(), info.icon);
+                    if (icon != null) {
+                        int appIconSize = dp.iconSizePx;
+                        int iconSize = (int) Math.min(appIconSize * scale,
+                                Math.min(boxRect.width(), boxRect.height()));
+
+                        icon = mutateOnMainThread(icon);
+                        int hoffset = (previewWidthF - iconSize) / 2;
+                        int yoffset = (previewHeightF - iconSize) / 2;
+                        icon.setBounds(hoffset, yoffset, hoffset + iconSize, yoffset + iconSize);
+                        icon.draw(c);
+                    }
+                } catch (Resources.NotFoundException e) {
+                }
             }
-            c.setBitmap(null);
-        }
-        return new Pair<>(preview, savePreviewImage);
+        });
     }
 
     private RectF drawBoxWithShadow(Canvas c, int width, int height) {
@@ -537,42 +248,29 @@
         return builder.bounds;
     }
 
-    private Bitmap generateShortcutPreview(BaseActivity launcher, ShortcutConfigActivityInfo info,
-            int maxWidth, int maxHeight, Bitmap preview) {
-        int iconSize = launcher.getDeviceProfile().allAppsIconSizePx;
-        int padding = launcher.getResources()
+    private Bitmap generateShortcutPreview(
+            ShortcutConfigActivityInfo info, int maxWidth, int maxHeight) {
+        int iconSize = ActivityContext.lookupContext(mContext).getDeviceProfile().allAppsIconSizePx;
+        int padding = mContext.getResources()
                 .getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
 
         int size = iconSize + 2 * padding;
         if (maxHeight < size || maxWidth < size) {
             throw new RuntimeException("Max size is too small for preview");
         }
-        final Canvas c = new Canvas();
-        if (preview == null || preview.getWidth() < size || preview.getHeight() < size) {
-            preview = Bitmap.createBitmap(size, size, Config.ARGB_8888);
-            c.setBitmap(preview);
-        } else {
-            if (preview.getWidth() > size || preview.getHeight() > size) {
-                preview.reconfigure(size, size, preview.getConfig());
-            }
+        return BitmapRenderer.createHardwareBitmap(size, size, c -> {
+            drawBoxWithShadow(c, size, size);
 
-            // Reusing bitmap. Clear it.
-            c.setBitmap(preview);
-            c.drawColor(0, PorterDuff.Mode.CLEAR);
-        }
+            LauncherIcons li = LauncherIcons.obtain(mContext);
+            Drawable icon = li.createBadgedIconBitmap(
+                    mutateOnMainThread(info.getFullResIcon(
+                            LauncherAppState.getInstance(mContext).getIconCache())),
+                    Process.myUserHandle(), 0).newIcon(mContext);
+            li.recycle();
 
-        drawBoxWithShadow(c, size, size);
-
-        LauncherIcons li = LauncherIcons.obtain(mContext);
-        Drawable icon = li.createBadgedIconBitmap(
-                mutateOnMainThread(info.getFullResIcon(mIconCache)),
-                Process.myUserHandle(), 0).newIcon(launcher);
-        li.recycle();
-
-        icon.setBounds(padding, padding, padding + iconSize, padding + iconSize);
-        icon.draw(c);
-        c.setBitmap(null);
-        return preview;
+            icon.setBounds(padding, padding, padding + iconSize, padding + iconSize);
+            icon.draw(c);
+        });
     }
 
     private Drawable mutateOnMainThread(final Drawable drawable) {
@@ -585,206 +283,4 @@
             throw new RuntimeException(e);
         }
     }
-
-    /**
-     * @return an array of containing versionCode and lastUpdatedTime for the package.
-     */
-    @Thunk long[] getPackageVersion(String packageName) {
-        synchronized (mPackageVersions) {
-            long[] versions = mPackageVersions.get(packageName);
-            if (versions == null) {
-                versions = new long[2];
-                try {
-                    PackageInfo info = mContext.getPackageManager().getPackageInfo(packageName,
-                            PackageManager.GET_UNINSTALLED_PACKAGES);
-                    versions[0] = info.versionCode;
-                    versions[1] = info.lastUpdateTime;
-                } catch (NameNotFoundException e) {
-                    Log.e(TAG, "PackageInfo not found", e);
-                }
-                mPackageVersions.put(packageName, versions);
-            }
-            return versions;
-        }
-    }
-
-    private class PreviewLoadTask extends AsyncTask<Void, Void, Bitmap>
-            implements CancellationSignal.OnCancelListener {
-        @Thunk final WidgetCacheKey mKey;
-        private final WidgetItem mInfo;
-        private final int mPreviewHeight;
-        private final int mPreviewWidth;
-        private final WidgetPreviewLoadedCallback mCallback;
-        private final BaseActivity mActivity;
-        @Thunk long[] mVersions;
-        @Thunk Bitmap mBitmapToRecycle;
-
-        @Nullable private Bitmap mUnusedPreviewBitmap;
-        private boolean mSaveToDB = false;
-
-        PreviewLoadTask(BaseActivity activity, WidgetCacheKey key, WidgetItem info,
-                int previewWidth, int previewHeight, WidgetPreviewLoadedCallback callback) {
-            mActivity = activity;
-            mKey = key;
-            mInfo = info;
-            mPreviewHeight = previewHeight;
-            mPreviewWidth = previewWidth;
-            mCallback = callback;
-            if (DEBUG) {
-                Log.d(TAG, String.format("%s, %s, %d, %d",
-                        mKey, mInfo, mPreviewHeight, mPreviewWidth));
-            }
-        }
-
-        @Override
-        protected Bitmap doInBackground(Void... params) {
-            Bitmap unusedBitmap = null;
-
-            // If already cancelled before this gets to run in the background, then return early
-            if (isCancelled()) {
-                return null;
-            }
-            synchronized (mUnusedBitmaps) {
-                // Check if we can re-use a bitmap
-                for (Bitmap candidate : mUnusedBitmaps) {
-                    if (candidate != null && candidate.isMutable()
-                            && candidate.getWidth() == mPreviewWidth
-                            && candidate.getHeight() == mPreviewHeight) {
-                        unusedBitmap = candidate;
-                        mUnusedBitmaps.remove(unusedBitmap);
-                        break;
-                    }
-                }
-            }
-
-            // creating a bitmap is expensive. Do not do this inside synchronized block.
-            if (unusedBitmap == null) {
-                unusedBitmap = Bitmap.createBitmap(mPreviewWidth, mPreviewHeight, Config.ARGB_8888);
-            }
-            // If cancelled now, don't bother reading the preview from the DB
-            if (isCancelled()) {
-                return unusedBitmap;
-            }
-            Bitmap preview = readFromDb(mKey, unusedBitmap, this);
-            // Only consider generating the preview if we have not cancelled the task already
-            if (!isCancelled() && preview == null) {
-                // Fetch the version info before we generate the preview, so that, in-case the
-                // app was updated while we are generating the preview, we use the old version info,
-                // which would gets re-written next time.
-                boolean persistable = mInfo.activityInfo == null
-                        || mInfo.activityInfo.isPersistable();
-                mVersions = persistable ? getPackageVersion(mKey.componentName.getPackageName())
-                        : null;
-
-                // it's not in the db... we need to generate it
-                Pair<Bitmap, Boolean> pair = generatePreview(mActivity, mInfo, unusedBitmap,
-                        mPreviewWidth, mPreviewHeight);
-                preview = pair.first;
-
-                if (preview != unusedBitmap) {
-                    mUnusedPreviewBitmap = unusedBitmap;
-                }
-
-                this.mSaveToDB = pair.second;
-            }
-            return preview;
-        }
-
-        @Override
-        protected void onPostExecute(final Bitmap preview) {
-            mCallback.onPreviewLoaded(preview);
-
-            // Write the generated preview to the DB in the worker thread
-            if (mVersions != null) {
-                MODEL_EXECUTOR.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        if (mUnusedPreviewBitmap != null) {
-                            // If we didn't end up using the bitmap, it can be added back into the
-                            // recycled set.
-                            synchronized (mUnusedBitmaps) {
-                                mUnusedBitmaps.add(mUnusedPreviewBitmap);
-                            }
-                        }
-
-                        if (!isCancelled() && mSaveToDB) {
-                            // If we are still using this preview, then write it to the DB and then
-                            // let the normal clear mechanism recycle the bitmap
-                            writeToDb(mKey, mVersions, preview);
-                            mBitmapToRecycle = preview;
-                        } else {
-                            // If we've already cancelled, then skip writing the bitmap to the DB
-                            // and manually add the bitmap back to the recycled set
-                            synchronized (mUnusedBitmaps) {
-                                mUnusedBitmaps.add(preview);
-                            }
-                        }
-                    }
-                });
-            } else {
-                // If we don't need to write to disk, then ensure the preview gets recycled by
-                // the normal clear mechanism
-                mBitmapToRecycle = preview;
-            }
-        }
-
-        @Override
-        protected void onCancelled(final Bitmap preview) {
-            // If we've cancelled while the task is running, then can return the bitmap to the
-            // recycled set immediately. Otherwise, it will be recycled after the preview is written
-            // to disk.
-            if (preview != null) {
-                MODEL_EXECUTOR.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        synchronized (mUnusedBitmaps) {
-                            mUnusedBitmaps.add(preview);
-                        }
-                    }
-                });
-            }
-        }
-
-        @Override
-        public void onCancel() {
-            cancel(true);
-
-            // This only handles the case where the PreviewLoadTask is cancelled after the task has
-            // successfully completed (including having written to disk when necessary).  In the
-            // other cases where it is cancelled while the task is running, it will be cleaned up
-            // in the tasks's onCancelled() call, and if cancelled while the task is writing to
-            // disk, it will be cancelled in the task's onPostExecute() call.
-            if (mBitmapToRecycle != null) {
-                MODEL_EXECUTOR.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        synchronized (mUnusedBitmaps) {
-                            mUnusedBitmaps.add(mBitmapToRecycle);
-                        }
-                        mBitmapToRecycle = null;
-                    }
-                });
-            }
-        }
-    }
-
-    private static final class WidgetCacheKey extends ComponentKey {
-
-        @Thunk final String mSize;
-
-        WidgetCacheKey(ComponentName componentName, UserHandle user, String size) {
-            super(componentName, user);
-            this.mSize = size;
-        }
-
-        @Override
-        public int hashCode() {
-            return super.hashCode() ^ mSize.hashCode();
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            return super.equals(o) && ((WidgetCacheKey) o).mSize.equals(mSize);
-        }
-    }
 }
diff --git a/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
index 149ac57..9c32e42 100644
--- a/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
@@ -25,6 +25,7 @@
 import android.text.TextPaint;
 import android.text.TextUtils;
 import android.util.TypedValue;
+import android.view.View;
 import android.widget.RemoteViews;
 
 import com.android.launcher3.R;
@@ -55,6 +56,11 @@
     }
 
     @Override
+    public void addView(View child) {
+        // Not allowed
+    }
+
+    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index fb6de9f..f0b4ba0 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -19,9 +19,7 @@
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
 import android.content.res.Configuration;
-import android.graphics.Canvas;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.util.SparseBooleanArray;
@@ -35,23 +33,17 @@
 import android.widget.Advanceable;
 import android.widget.RemoteViews;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.CheckLongPressHelper;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace;
 import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.keyboard.ViewGroupFocusHelper;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.BaseDragLayer.TouchCompleteListener;
-import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
-
-import java.util.List;
 
 /**
  * {@inheritDoc}
@@ -60,8 +52,6 @@
         implements TouchCompleteListener, View.OnLongClickListener,
         LocalColorExtractor.Listener {
 
-    private static final String LOG_TAG = "LauncherAppWidgetHostView";
-
     // Related to the auto-advancing of widgets
     private static final long ADVANCE_INTERVAL = 20000;
     private static final long ADVANCE_STAGGER = 250;
@@ -71,9 +61,9 @@
     // Maximum duration for which updates can be deferred.
     private static final long UPDATE_LOCK_TIMEOUT_MILLIS = 1000;
 
+    private final Rect mTempRect = new Rect();
     private final CheckLongPressHelper mLongPressHelper;
     protected final Launcher mLauncher;
-    private final Workspace mWorkspace;
 
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mReinflateOnConfigChange;
@@ -84,28 +74,23 @@
     private boolean mIsScrollable;
     private boolean mIsAttachedToWindow;
     private boolean mIsAutoAdvanceRegistered;
-    private boolean mIsInDragMode = false;
     private Runnable mAutoAdvanceRunnable;
-    private RectF mLastLocationRegistered = null;
-    @Nullable private AppWidgetHostViewDragListener mDragListener;
 
-    // Used to store the widget sizes in drag layer coordinates.
-    private final Rect mCurrentWidgetSize = new Rect();
-    private final Rect mWidgetSizeAtDrag = new Rect();
-
-    private final RectF mTempRectF = new RectF();
-    private final Object mUpdateLock = new Object();
-    private final ViewGroupFocusHelper mDragLayerRelativeCoordinateHelper;
     private long mDeferUpdatesUntilMillis = 0;
     private RemoteViews mDeferredRemoteViews;
     private boolean mHasDeferredColorChange = false;
     private @Nullable SparseIntArray mDeferredColorChange = null;
-    private boolean mEnableColorExtraction = true;
+
+    // The following member variables are only used during drag-n-drop.
+    private boolean mIsInDragMode = false;
+    /** The drag content width which is only set when the drag content scale is not 1f. */
+    private int mDragContentWidth = 0;
+    /** The drag content height which is only set when the drag content scale is not 1f. */
+    private int mDragContentHeight = 0;
 
     public LauncherAppWidgetHostView(Context context) {
         super(context);
         mLauncher = Launcher.getLauncher(context);
-        mWorkspace = mLauncher.getWorkspace();
         mLongPressHelper = new CheckLongPressHelper(this, this);
         setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
         setBackgroundResource(R.drawable.widget_internal_focus_bg);
@@ -114,9 +99,6 @@
             setOnLightBackground(true);
         }
         mColorExtractor = LocalColorExtractor.newInstance(getContext());
-        mColorExtractor.setListener(this);
-
-        mDragLayerRelativeCoordinateHelper = new ViewGroupFocusHelper(mLauncher.getDragLayer());
     }
 
     @Override
@@ -129,14 +111,6 @@
     }
 
     @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-        if (mIsInDragMode && mDragListener != null) {
-            mDragListener.onDragContentChanged();
-        }
-    }
-
-    @Override
     public boolean onLongClick(View view) {
         if (mIsScrollable) {
             DragLayer dragLayer = mLauncher.getDragLayer();
@@ -148,13 +122,11 @@
 
     @Override
     public void updateAppWidget(RemoteViews remoteViews) {
-        synchronized (mUpdateLock) {
-            if (isDeferringUpdates()) {
-                mDeferredRemoteViews = remoteViews;
-                return;
-            }
-            mDeferredRemoteViews = null;
+        if (isDeferringUpdates()) {
+            mDeferredRemoteViews = remoteViews;
+            return;
         }
+        mDeferredRemoteViews = null;
 
         super.updateAppWidget(remoteViews);
 
@@ -205,9 +177,7 @@
      * {@link #onColorsChanged} call after {@link #UPDATE_LOCK_TIMEOUT_MILLIS} have elapsed.
      */
     public void beginDeferringUpdates() {
-        synchronized (mUpdateLock) {
-            mDeferUpdatesUntilMillis = SystemClock.uptimeMillis() + UPDATE_LOCK_TIMEOUT_MILLIS;
-        }
+        mDeferUpdatesUntilMillis = SystemClock.uptimeMillis() + UPDATE_LOCK_TIMEOUT_MILLIS;
     }
 
     /**
@@ -219,20 +189,19 @@
         RemoteViews remoteViews;
         SparseIntArray deferredColors;
         boolean hasDeferredColors;
-        synchronized (mUpdateLock) {
-            mDeferUpdatesUntilMillis = 0;
-            remoteViews = mDeferredRemoteViews;
-            mDeferredRemoteViews = null;
-            deferredColors = mDeferredColorChange;
-            hasDeferredColors = mHasDeferredColorChange;
-            mDeferredColorChange = null;
-            mHasDeferredColorChange = false;
-        }
+        mDeferUpdatesUntilMillis = 0;
+        remoteViews = mDeferredRemoteViews;
+        mDeferredRemoteViews = null;
+        deferredColors = mDeferredColorChange;
+        hasDeferredColors = mHasDeferredColorChange;
+        mDeferredColorChange = null;
+        mHasDeferredColorChange = false;
+
         if (remoteViews != null) {
             updateAppWidget(remoteViews);
         }
         if (hasDeferredColors) {
-            onColorsChanged(null /* rectF */, deferredColors);
+            onColorsChanged(deferredColors);
         }
     }
 
@@ -257,13 +226,9 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-
         mIsAttachedToWindow = true;
         checkIfAutoAdvance();
-
-        if (mLastLocationRegistered != null) {
-            mColorExtractor.addLocation(List.of(mLastLocationRegistered));
-        }
+        mColorExtractor.setListener(this);
     }
 
     @Override
@@ -274,7 +239,7 @@
         // state is updated. So isAttachedToWindow() will return true until next frame.
         mIsAttachedToWindow = false;
         checkIfAutoAdvance();
-        mColorExtractor.removeLocations();
+        mColorExtractor.setListener(null);
     }
 
     @Override
@@ -305,107 +270,62 @@
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
-
         mIsScrollable = checkScrollableRecursively(this);
-        updateColorExtraction();
+
+        if (!mIsInDragMode && getTag() instanceof LauncherAppWidgetInfo) {
+            LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
+            mTempRect.set(left, top, right, bottom);
+            mColorExtractor.setWorkspaceLocation(mTempRect, (View) getParent(), info.screenId);
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        if (mIsInDragMode && mDragContentWidth > 0 && mDragContentHeight > 0
+                && getChildCount() == 1) {
+            measureChild(getChildAt(0), MeasureSpec.getSize(mDragContentWidth),
+                    MeasureSpec.getSize(mDragContentHeight));
+        }
     }
 
     /** Starts the drag mode. */
-    public void startDrag(AppWidgetHostViewDragListener dragListener) {
+    public void startDrag() {
         mIsInDragMode = true;
-        mDragListener = dragListener;
+        // In the case of dragging a scaled preview from widgets picker, we should reuse the
+        // previously measured dimension from WidgetCell#measureAndComputeWidgetPreviewScale, which
+        // measures the dimension of a widget preview without its parent's bound before scaling
+        // down.
+        if ((getScaleX() != 1f || getScaleY() != 1f) && getChildCount() == 1) {
+            mDragContentWidth = getChildAt(0).getMeasuredWidth();
+            mDragContentHeight = getChildAt(0).getMeasuredHeight();
+        }
     }
 
-    /** Handles a drag event occurred on a workspace page, {@code pageId}. */
-    public void handleDrag(Rect rectInDragLayer, int pageId) {
-        mWidgetSizeAtDrag.set(rectInDragLayer);
-        updateColorExtraction(mWidgetSizeAtDrag, pageId);
+    /** Handles a drag event occurred on a workspace page corresponding to the {@code screenId}. */
+    public void handleDrag(Rect rectInView, View view, int screenId) {
+        if (mIsInDragMode) {
+            mColorExtractor.setWorkspaceLocation(rectInView, view, screenId);
+        }
     }
 
     /** Ends the drag mode. */
     public void endDrag() {
         mIsInDragMode = false;
-        mDragListener = null;
-        mWidgetSizeAtDrag.setEmpty();
-    }
-
-    /**
-     * @param rectInDragLayer Rect of widget in drag layer coordinates.
-     * @param pageId The workspace page the widget is on.
-     */
-    private void updateColorExtraction(Rect rectInDragLayer, int pageId) {
-        if (!mEnableColorExtraction) return;
-        mColorExtractor.getExtractedRectForViewRect(mLauncher, pageId, rectInDragLayer, mTempRectF);
-
-        if (mTempRectF.isEmpty()) {
-            return;
-        }
-        if (!isSameLocation(mTempRectF, mLastLocationRegistered, /* epsilon= */ 1e-6f)) {
-            if (mLastLocationRegistered != null) {
-                mColorExtractor.removeLocations();
-            }
-            mLastLocationRegistered = new RectF(mTempRectF);
-            mColorExtractor.addLocation(List.of(mLastLocationRegistered));
-        }
-    }
-
-    /**
-     * Update the color extraction, using the current position of the app widget.
-     */
-    private void updateColorExtraction() {
-        if (!mIsInDragMode && getTag() instanceof LauncherAppWidgetInfo) {
-            LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
-            mDragLayerRelativeCoordinateHelper.viewToRect(this, mCurrentWidgetSize);
-            updateColorExtraction(mCurrentWidgetSize,
-                    mWorkspace.getPageIndexForScreenId(info.screenId));
-        }
-    }
-
-    /**
-     * Enables the local color extraction.
-     *
-     * @param updateColors If true, this will update the color extraction using the current location
-     *                    of the App Widget.
-     */
-    public void enableColorExtraction(boolean updateColors) {
-        mEnableColorExtraction = true;
-        if (updateColors) {
-            updateColorExtraction();
-        }
-    }
-
-    /**
-     * Disables the local color extraction.
-     */
-    public void disableColorExtraction() {
-        mEnableColorExtraction = false;
-    }
-
-    // Compare two location rectangles. Locations are always in the [0;1] range.
-    private static boolean isSameLocation(@NonNull RectF rect1, @Nullable RectF rect2,
-            float epsilon) {
-        if (rect2 == null) return false;
-        return isSameCoordinate(rect1.left, rect2.left, epsilon)
-                && isSameCoordinate(rect1.right, rect2.right, epsilon)
-                && isSameCoordinate(rect1.top, rect2.top, epsilon)
-                && isSameCoordinate(rect1.bottom, rect2.bottom, epsilon);
-    }
-
-    private static boolean isSameCoordinate(float c1, float c2, float epsilon) {
-        return Math.abs(c1 - c2) < epsilon;
+        mDragContentWidth = 0;
+        mDragContentHeight = 0;
+        requestLayout();
     }
 
     @Override
-    public void onColorsChanged(RectF rectF, SparseIntArray colors) {
-        synchronized (mUpdateLock) {
-            if (isDeferringUpdates()) {
-                mDeferredColorChange = colors;
-                mHasDeferredColorChange = true;
-                return;
-            }
-            mDeferredColorChange = null;
-            mHasDeferredColorChange = false;
+    public void onColorsChanged(SparseIntArray colors) {
+        if (isDeferringUpdates()) {
+            mDeferredColorChange = colors;
+            mHasDeferredColorChange = true;
+            return;
         }
+        mDeferredColorChange = null;
+        mHasDeferredColorChange = false;
 
         // setColorResources will reapply the view, which must happen in the UI thread.
         post(() -> setColorResources(colors));
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
index d77d99d..bba1016 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
@@ -121,29 +121,29 @@
                 localPadding.set(widgetPadding);
             }
             minSpanX = Math.max(minSpanX,
-                    getSpanX(localPadding, minResizeWidth, dp.cellLayoutBorderSpacingPx,
+                    getSpanX(localPadding, minResizeWidth, dp.cellLayoutBorderSpacePx.x,
                             cellSize.x));
             minSpanY = Math.max(minSpanY,
-                    getSpanY(localPadding, minResizeHeight, dp.cellLayoutBorderSpacingPx,
+                    getSpanY(localPadding, minResizeHeight, dp.cellLayoutBorderSpacePx.y,
                             cellSize.y));
 
             if (ATLEAST_S) {
                 if (maxResizeWidth > 0) {
-                    maxSpanX = Math.min(maxSpanX,
-                            getSpanX(localPadding, maxResizeWidth, dp.cellLayoutBorderSpacingPx,
-                                    cellSize.x));
+                    maxSpanX = Math.min(maxSpanX, getSpanX(localPadding, maxResizeWidth,
+                            dp.cellLayoutBorderSpacePx.x, cellSize.x));
                 }
                 if (maxResizeHeight > 0) {
-                    maxSpanY = Math.min(maxSpanY,
-                            getSpanY(localPadding, maxResizeHeight, dp.cellLayoutBorderSpacingPx,
-                                    cellSize.y));
+                    maxSpanY = Math.min(maxSpanY, getSpanY(localPadding, maxResizeHeight,
+                            dp.cellLayoutBorderSpacePx.y, cellSize.y));
                 }
             }
 
             spanX = Math.max(spanX,
-                    getSpanX(localPadding, minWidth, dp.cellLayoutBorderSpacingPx, cellSize.x));
+                    getSpanX(localPadding, minWidth, dp.cellLayoutBorderSpacePx.x,
+                            cellSize.x));
             spanY = Math.max(spanY,
-                    getSpanY(localPadding, minHeight, dp.cellLayoutBorderSpacingPx, cellSize.y));
+                    getSpanY(localPadding, minHeight, dp.cellLayoutBorderSpacePx.y,
+                            cellSize.y));
         }
 
         if (ATLEAST_S) {
diff --git a/src/com/android/launcher3/widget/LocalColorExtractor.java b/src/com/android/launcher3/widget/LocalColorExtractor.java
index 23d9e15..96e7531 100644
--- a/src/com/android/launcher3/widget/LocalColorExtractor.java
+++ b/src/com/android/launcher3/widget/LocalColorExtractor.java
@@ -20,18 +20,14 @@
 import android.appwidget.AppWidgetHostView;
 import android.content.Context;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.util.SparseIntArray;
 import android.view.View;
 
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.util.ResourceBasedOverride;
 
-import java.util.List;
-
 /** Extracts the colors we need from the wallpaper at given locations. */
 public class LocalColorExtractor implements ResourceBasedOverride {
 
@@ -44,7 +40,7 @@
          * their value, in a format that can be passed directly to
          * {@link AppWidgetHostView#setColorResources(SparseIntArray)}.
          */
-        void onColorsChanged(RectF rect, SparseIntArray extractedColors);
+        void onColorsChanged(SparseIntArray extractedColors);
     }
 
     /**
@@ -60,15 +56,13 @@
         // no-op
     }
 
-    /** Adds a list of locations to track with this listener. */
-    public void addLocation(List<RectF> locations) {
-        // no-op
-    }
-
-    /** Stops tracking any locations. */
-    public void removeLocations() {
-        // no-op
-    }
+    /**
+     * Sets the location used for color extraction
+     * @param pos position to use for color extraction
+     * @param child view whose coordinate space is used for {@code pos}
+     * @param screenId the workspace screenId
+     */
+    public void setWorkspaceLocation(Rect pos, View child, int screenId) { }
 
     /**
      * Updates the base context to contain the colors override
@@ -83,32 +77,4 @@
         return null;
     }
 
-    /**
-     * Takes a view and returns its rect that can be used by the wallpaper local color extractor.
-     *
-     * @param launcher Launcher class class.
-     * @param pageId The page the workspace item is on.
-     * @param v The view.
-     * @param colorExtractionRectOut The location rect, but converted to a format expected by the
-     *                               wallpaper local color extractor.
-     */
-    public void getExtractedRectForView(Launcher launcher, int pageId, View v,
-            RectF colorExtractionRectOut) {
-        // no-op
-    }
-
-    /**
-     * Takes a rect in drag layer coordinates and returns the rect that can be used by the wallpaper
-     * local color extractor.
-     *
-     * @param launcher Launcher class.
-     * @param pageId The page the workspace item is on.
-     * @param rectInDragLayer The relevant bounds of the view in drag layer coordinates.
-     * @param colorExtractionRectOut The location rect, but converted to a format expected by the
-     *                               wallpaper local color extractor.
-     */
-    public void getExtractedRectForViewRect(Launcher launcher, int pageId, Rect rectInDragLayer,
-            RectF colorExtractionRectOut) {
-        // no-op
-    }
 }
diff --git a/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java b/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
index d12fe74..241c937 100644
--- a/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
@@ -49,6 +49,8 @@
      */
     private final PointF mTranslationForCentering = new PointF(0, 0);
 
+    private final PointF mTranslationForMoveFromCenterAnimation = new PointF(0, 0);
+
     private final PointF mTranslationForReorderBounce = new PointF(0, 0);
     private final PointF mTranslationForReorderPreview = new PointF(0, 0);
     private float mScaleForReorderBounce = 1f;
@@ -167,9 +169,9 @@
 
     private void updateTranslation() {
         super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x
-                + mTranslationForCentering.x);
+                + mTranslationForCentering.x + mTranslationForMoveFromCenterAnimation.x);
         super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y
-                + mTranslationForCentering.y);
+                + mTranslationForCentering.y + mTranslationForMoveFromCenterAnimation.y);
     }
 
     public void setTranslationForCentering(float x, float y) {
@@ -177,6 +179,11 @@
         updateTranslation();
     }
 
+    public void setTranslationForMoveFromCenterAnimation(float x, float y) {
+        mTranslationForMoveFromCenterAnimation.set(x, y);
+        updateTranslation();
+    }
+
     public void setReorderBounceOffset(float x, float y) {
         mTranslationForReorderBounce.set(x, y);
         updateTranslation();
diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index b6bb6aa..553ba13 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -17,7 +17,7 @@
 package com.android.launcher3.widget;
 
 import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
-import static com.android.launcher3.model.data.PackageItemInfo.CONVERSATIONS;
+import static com.android.launcher3.widget.WidgetSections.getWidgetSections;
 
 import android.content.Context;
 import android.graphics.Canvas;
@@ -89,8 +89,8 @@
         setOnClickListener(ItemClickHandler.INSTANCE);
 
         if (info.pendingItemInfo == null) {
-            info.pendingItemInfo = new PackageItemInfo(info.providerName.getPackageName());
-            info.pendingItemInfo.user = info.user;
+            info.pendingItemInfo = new PackageItemInfo(info.providerName.getPackageName(),
+                    info.user);
             cache.updateIconInBackground(this, info.pendingItemInfo);
         } else {
             reapplyItemInfo(info.pendingItemInfo);
@@ -338,10 +338,11 @@
      */
     @Nullable
     private Drawable getWidgetCategoryIcon() {
-        switch (mInfo.pendingItemInfo.category) {
-            case CONVERSATIONS:
-                return getContext().getDrawable(R.drawable.ic_conversations_widget_category);
+        if (mInfo.pendingItemInfo.widgetCategory == WidgetSections.NO_CATEGORY) {
+            return null;
         }
-        return null;
+        Context context = getContext();
+        return context.getDrawable(getWidgetSections(context).get(
+                mInfo.pendingItemInfo.widgetCategory).mSectionDrawable);
     }
 }
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index cea4de7..463f4ac 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -22,6 +22,8 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.util.Size;
 import android.view.View;
 import android.view.View.MeasureSpec;
 import android.widget.RemoteViews;
@@ -40,7 +42,9 @@
 import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.icons.RoundDrawableWrapper;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
+import com.android.launcher3.widget.util.WidgetSizes;
 
 /**
  * Extension of {@link DragPreviewProvider} with logic specific to pending widgets/shortcuts
@@ -54,6 +58,7 @@
     private int[] mEstimatedCellSize;
 
     @Nullable private RemoteViews mRemoteViewsPreview;
+    private float mRemoteViewsPreviewScale = 1f;
     @Nullable private NavigableAppWidgetHostView mAppWidgetHostViewPreview;
     private final float mEnforcedRoundedCornersForWidget;
 
@@ -68,8 +73,10 @@
      * Sets a {@link RemoteViews} which shows an app widget preview provided by app developers in
      * the pin widget flow.
      */
-    public void setRemoteViewsPreview(@Nullable RemoteViews remoteViewsPreview) {
+    public void setRemoteViewsPreview(@Nullable RemoteViews remoteViewsPreview,
+            float previewScale) {
         mRemoteViewsPreview = remoteViewsPreview;
+        mRemoteViewsPreviewScale = previewScale;
     }
 
     /** Sets a {@link NavigableAppWidgetHostView} which shows a preview layout of an app widget. */
@@ -89,6 +96,9 @@
      */
     public void startDrag(Rect previewBounds, int previewBitmapWidth, int previewViewWidth,
             Point screenPos, DragSource source, DragOptions options) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_DROP_TARGET, "3");
+        }
         final Launcher launcher = Launcher.getLauncher(mView.getContext());
         LauncherAppState app = LauncherAppState.getInstance(launcher);
 
@@ -120,13 +130,14 @@
                 mAppWidgetHostViewPreview.setPadding(padding.left, padding.top, padding.right,
                         padding.bottom);
                 mAppWidgetHostViewPreview.updateAppWidget(/* remoteViews= */ mRemoteViewsPreview);
-                int width =
-                        deviceProfile.cellWidthPx * mAddInfo.spanX + padding.left + padding.right;
-                int height =
-                        deviceProfile.cellHeightPx * mAddInfo.spanY + padding.top + padding.bottom;
+                Size widgetSizes = WidgetSizes.getWidgetPaddedSizePx(launcher,
+                        mAddInfo.componentName, deviceProfile, mAddInfo.spanX, mAddInfo.spanY);
                 mAppWidgetHostViewPreview.measure(
-                        MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+                        MeasureSpec.makeMeasureSpec(widgetSizes.getWidth(), MeasureSpec.EXACTLY),
+                        MeasureSpec.makeMeasureSpec(widgetSizes.getHeight(), MeasureSpec.EXACTLY));
+                mAppWidgetHostViewPreview.setClipChildren(false);
+                mAppWidgetHostViewPreview.setClipToPadding(false);
+                mAppWidgetHostViewPreview.setScaleToFit(mRemoteViewsPreviewScale);
             }
             if (mAppWidgetHostViewPreview != null) {
                 previewSizeBeforeScale[0] = mAppWidgetHostViewPreview.getMeasuredWidth();
@@ -134,10 +145,9 @@
                         .addDragListener(new AppWidgetHostViewDragListener(launcher));
             }
             if (preview == null && mAppWidgetHostViewPreview == null) {
-                Drawable p = new FastBitmapDrawable(
-                        app.getWidgetCache().generateWidgetPreview(launcher,
-                                createWidgetInfo.info, maxWidth, null,
-                                previewSizeBeforeScale).first);
+                Drawable p = new FastBitmapDrawable(new DatabaseWidgetPreviewLoader(launcher)
+                        .generateWidgetPreview(
+                                createWidgetInfo.info, maxWidth, previewSizeBeforeScale));
                 if (RoundedCornerEnforcement.isRoundedCornerEnabled()) {
                     p = new RoundDrawableWrapper(p, mEnforcedRoundedCornersForWidget);
                 }
@@ -199,10 +209,6 @@
             draggableView = DraggableView.ofType(DraggableView.DRAGGABLE_ICON);
         }
 
-        // Since we are not going through the workspace for starting the drag, set drag related
-        // information on the workspace before starting the drag.
-        launcher.getWorkspace().prepareDragWithProvider(this);
-
         int dragLayerX = screenPos.x + previewBounds.left
                 + (int) ((scale * previewWidth - previewWidth) / 2);
         int dragLayerY = screenPos.y + previewBounds.top
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 5769ba0..f1ac656 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -16,21 +16,23 @@
 
 package com.android.launcher3.widget;
 
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
 import static com.android.launcher3.Utilities.ATLEAST_S;
 
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.os.CancellationSignal;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Size;
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.View.OnLayoutChangeListener;
+import android.view.ViewGroup;
 import android.view.ViewPropertyAnimator;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
@@ -38,18 +40,22 @@
 import android.widget.RemoteViews;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.BaseActivity;
 import com.android.launcher3.CheckLongPressHelper;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.icons.RoundDrawableWrapper;
+import com.android.launcher3.icons.cache.HandlerRunnable;
 import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.widget.util.WidgetSizes;
 
+import java.util.function.Consumer;
+
 /**
  * Represents the individual cell of the widget inside the widget tray. The preview is drawn
  * horizontally centered, and scaled down if needed.
@@ -59,7 +65,7 @@
  * transition from the view to drag view, so when adding padding support, DnD would need to
  * consider the appropriate scaling factor.
  */
-public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
+public class WidgetCell extends LinearLayout {
 
     private static final String TAG = "WidgetCell";
     private static final boolean DEBUG = false;
@@ -72,11 +78,36 @@
     /** Widget preview width is calculated by multiplying this factor to the widget cell width. */
     private static final float PREVIEW_SCALE = 0.8f;
 
-    protected int mPreviewWidth;
-    protected int mPreviewHeight;
+    /**
+     * The maximum dimension that can be used as the size in
+     * {@link android.view.View.MeasureSpec#makeMeasureSpec(int, int)}.
+     *
+     * <p>This is equal to (1 << MeasureSpec.MODE_SHIFT) - 1.
+     */
+    private static final int MAX_MEASURE_SPEC_DIMENSION = (1 << 30) - 1;
+
+    /**
+     * The target preview width, in pixels, of a widget or a shortcut.
+     *
+     * <p>The actual preview width may be smaller than or equal to this value subjected to scaling.
+     */
+    protected int mTargetPreviewWidth;
+
+    /**
+     * The target preview height, in pixels, of a widget or a shortcut.
+     *
+     * <p>The actual preview height may be smaller than or equal to this value subjected to scaling.
+     */
+    protected int mTargetPreviewHeight;
+
     protected int mPresetPreviewSize;
+
     private int mCellSize;
-    private float mPreviewScale = 1f;
+
+    /**
+     * The scale of the preview container.
+     */
+    private float mPreviewContainerScale = 1f;
 
     private FrameLayout mWidgetImageContainer;
     private WidgetImageView mWidgetImage;
@@ -86,21 +117,18 @@
 
     protected WidgetItem mItem;
 
-    private WidgetPreviewLoader mWidgetPreviewLoader;
+    private final DatabaseWidgetPreviewLoader mWidgetPreviewLoader;
 
-    protected CancellationSignal mActiveRequest;
+    protected HandlerRunnable mActiveRequest;
     private boolean mAnimatePreview = true;
 
-    private boolean mApplyBitmapDeferred = false;
-    private Drawable mDeferredDrawable;
-
-    protected final BaseActivity mActivity;
+    protected final ActivityContext mActivity;
     private final CheckLongPressHelper mLongPressHelper;
     private final float mEnforcedCornerRadius;
-    private final int mShortcutPreviewPadding;
 
     private RemoteViews mRemoteViewsPreview;
     private NavigableAppWidgetHostView mAppWidgetHostViewPreview;
+    private float mAppWidgetHostViewScale = 1f;
     private int mSourceContainer = CONTAINER_WIDGETS_TRAY;
 
     public WidgetCell(Context context) {
@@ -114,7 +142,8 @@
     public WidgetCell(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
-        mActivity = BaseActivity.fromContext(context);
+        mActivity = ActivityContext.lookupContext(context);
+        mWidgetPreviewLoader = new DatabaseWidgetPreviewLoader(context);
         mLongPressHelper = new CheckLongPressHelper(this);
         mLongPressHelper.setLongPressTimeoutFactor(1);
 
@@ -123,14 +152,12 @@
         setClipToPadding(false);
         setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
         mEnforcedCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(context);
-        mShortcutPreviewPadding =
-                2 * getResources().getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
     }
 
     private void setContainerWidth() {
         mCellSize = (int) (mActivity.getDeviceProfile().allAppsIconSizePx * WIDTH_SCALE);
         mPresetPreviewSize = (int) (mCellSize * PREVIEW_SCALE);
-        mPreviewWidth = mPreviewHeight = mPresetPreviewSize;
+        mTargetPreviewWidth = mTargetPreviewHeight = mPresetPreviewSize;
     }
 
     @Override
@@ -153,6 +180,11 @@
         return mRemoteViewsPreview;
     }
 
+    /** Returns the app widget host view scale, which is a value between [0f, 1f]. */
+    public float getAppWidgetHostViewScale() {
+        return mAppWidgetHostViewScale;
+    }
+
     /**
      * Called to clear the view and free attached resources. (e.g., {@link Bitmap}
      */
@@ -167,7 +199,7 @@
         mWidgetDims.setText(null);
         mWidgetDescription.setText(null);
         mWidgetDescription.setVisibility(GONE);
-        mPreviewWidth = mPreviewHeight = mPresetPreviewSize;
+        mTargetPreviewWidth = mTargetPreviewHeight = mPresetPreviewSize;
 
         if (mActiveRequest != null) {
             mActiveRequest.cancel();
@@ -178,6 +210,7 @@
             mWidgetImageContainer.removeView(mAppWidgetHostViewPreview);
         }
         mAppWidgetHostViewPreview = null;
+        mAppWidgetHostViewScale = 1f;
         mItem = null;
     }
 
@@ -185,7 +218,36 @@
         this.mSourceContainer = sourceContainer;
     }
 
-    public void applyFromCellItem(WidgetItem item, WidgetPreviewLoader loader) {
+    /**
+     * Applies the item to this view
+     */
+    public void applyFromCellItem(WidgetItem item) {
+        applyFromCellItem(item, 1f);
+    }
+
+    /**
+     * Applies the item to this view
+     */
+    public void applyFromCellItem(WidgetItem item, float previewScale) {
+        applyFromCellItem(item, previewScale, this::applyPreview, null);
+    }
+
+    /**
+     * Applies the item to this view
+     * @param item item to apply
+     * @param previewScale factor to scale the preview
+     * @param callback callback when preview is loaded in case the preview is being loaded or cached
+     * @param cachedPreview previously cached preview bitmap is present
+     */
+    public void applyFromCellItem(WidgetItem item, float previewScale,
+            @NonNull Consumer<Bitmap> callback, @Nullable Bitmap cachedPreview) {
+        // setPreviewSize
+        DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+        Size widgetSize = WidgetSizes.getWidgetItemSizePx(getContext(), deviceProfile, item);
+        mTargetPreviewWidth = widgetSize.getWidth();
+        mTargetPreviewHeight = widgetSize.getHeight();
+        mPreviewContainerScale = previewScale;
+
         applyPreviewOnAppWidgetHostView(item);
 
         Context context = getContext();
@@ -207,14 +269,14 @@
             }
         }
 
-        mWidgetPreviewLoader = loader;
         if (item.activityInfo != null) {
             setTag(new PendingAddShortcutInfo(item.activityInfo));
         } else {
             setTag(new PendingAddWidgetInfo(item.widgetInfo, mSourceContainer));
         }
-    }
 
+        ensurePreviewWithCallback(callback, cachedPreview);
+    }
 
     private void applyPreviewOnAppWidgetHostView(WidgetItem item) {
         if (mRemoteViewsPreview != null) {
@@ -249,16 +311,6 @@
             @Nullable RemoteViews remoteViews) {
         appWidgetHostViewPreview.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
         appWidgetHostViewPreview.setAppWidget(/* appWidgetId= */ -1, providerInfo);
-        Rect padding;
-        DeviceProfile deviceProfile = mActivity.getDeviceProfile();
-        if (deviceProfile.shouldInsetWidgets()) {
-            padding = new Rect();
-            appWidgetHostViewPreview.getWidgetInset(deviceProfile, padding);
-        } else {
-            padding = deviceProfile.inv.defaultWidgetPadding;
-        }
-        appWidgetHostViewPreview.setPadding(padding.left, padding.top, padding.right,
-                padding.bottom);
         appWidgetHostViewPreview.updateAppWidget(remoteViews);
     }
 
@@ -271,47 +323,25 @@
         return mAppWidgetHostViewPreview;
     }
 
-    /**
-     * Sets if applying bitmap preview should be deferred. The UI will still load the bitmap, but
-     * will not cause invalidate, so that when deferring is disabled later, all the bitmaps are
-     * ready.
-     * This prevents invalidates while the animation is running.
-     */
-    public void setApplyBitmapDeferred(boolean isDeferred) {
-        if (mApplyBitmapDeferred != isDeferred) {
-            mApplyBitmapDeferred = isDeferred;
-            if (!mApplyBitmapDeferred && mDeferredDrawable != null) {
-                applyPreview(mDeferredDrawable);
-                mDeferredDrawable = null;
-            }
-        }
-    }
-
     public void setAnimatePreview(boolean shouldAnimate) {
         mAnimatePreview = shouldAnimate;
     }
 
-    public void applyPreview(Bitmap bitmap) {
-        FastBitmapDrawable drawable = new FastBitmapDrawable(bitmap);
-        applyPreview(new RoundDrawableWrapper(drawable, mEnforcedCornerRadius));
-    }
+    private void applyPreview(Bitmap bitmap) {
+        if (bitmap != null) {
+            Drawable drawable = new RoundDrawableWrapper(
+                    new FastBitmapDrawable(bitmap), mEnforcedCornerRadius);
 
-    private void applyPreview(Drawable drawable) {
-        if (mApplyBitmapDeferred) {
-            mDeferredDrawable = drawable;
-            return;
-        }
-        if (drawable != null) {
+            // Scale down the preview size if it's wider than the cell.
             float scale = 1f;
-            if (getWidth() > 0 && getHeight() > 0) {
-                // Scale down the preview size if it's wider than the cell.
-                float maxWidth = getWidth();
-                float previewWidth = drawable.getIntrinsicWidth() * mPreviewScale;
+            if (mTargetPreviewWidth > 0) {
+                float maxWidth = mTargetPreviewWidth;
+                float previewWidth = drawable.getIntrinsicWidth() * mPreviewContainerScale;
                 scale = Math.min(maxWidth / previewWidth, 1);
             }
             setContainerSize(
-                    Math.round(drawable.getIntrinsicWidth() * scale),
-                    Math.round(drawable.getIntrinsicHeight() * scale));
+                    Math.round(drawable.getIntrinsicWidth() * scale * mPreviewContainerScale),
+                    Math.round(drawable.getIntrinsicHeight() * scale * mPreviewContainerScale));
             mWidgetImage.setDrawable(drawable);
             mWidgetImage.setVisibility(View.VISIBLE);
             if (mAppWidgetHostViewPreview != null) {
@@ -326,55 +356,61 @@
         } else {
             mWidgetImageContainer.setAlpha(1f);
         }
+        if (mActiveRequest != null) {
+            mActiveRequest.cancel();
+            mActiveRequest = null;
+        }
     }
 
     private void setContainerSize(int width, int height) {
         LayoutParams layoutParams = (LayoutParams) mWidgetImageContainer.getLayoutParams();
-        layoutParams.width = (int) (width * mPreviewScale);
-        layoutParams.height = (int) (height * mPreviewScale);
+        layoutParams.width = width;
+        layoutParams.height = height;
         mWidgetImageContainer.setLayoutParams(layoutParams);
     }
 
-    public void ensurePreview() {
+    /**
+     * Ensures that the preview is already loaded or being loaded. If the preview is not loaded,
+     * it applies the provided cachedPreview. If that is null, it starts a loader and notifies the
+     * callback on successful load.
+     */
+    private void ensurePreviewWithCallback(Consumer<Bitmap> callback,
+            @Nullable Bitmap cachedPreview) {
         if (mAppWidgetHostViewPreview != null) {
-            setContainerSize(mPreviewWidth, mPreviewHeight);
+            int containerWidth = (int) (mTargetPreviewWidth * mPreviewContainerScale);
+            int containerHeight = (int) (mTargetPreviewHeight * mPreviewContainerScale);
+            setContainerSize(containerWidth, containerHeight);
+            if (mAppWidgetHostViewPreview.getChildCount() == 1) {
+                View widgetContent = mAppWidgetHostViewPreview.getChildAt(0);
+                ViewGroup.LayoutParams layoutParams = widgetContent.getLayoutParams();
+                // We only scale preview if both the width & height of the outermost view group are
+                // not set to MATCH_PARENT.
+                boolean shouldScale =
+                        layoutParams.width != MATCH_PARENT && layoutParams.height != MATCH_PARENT;
+                if (shouldScale) {
+                    setNoClip(mWidgetImageContainer);
+                    setNoClip(mAppWidgetHostViewPreview);
+                    mAppWidgetHostViewScale = measureAndComputeWidgetPreviewScale();
+                    mAppWidgetHostViewPreview.setScaleToFit(mAppWidgetHostViewScale);
+                }
+            }
             FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
-                    mPreviewWidth, mPreviewHeight, Gravity.FILL);
+                    containerWidth, containerHeight, Gravity.FILL);
             mAppWidgetHostViewPreview.setLayoutParams(params);
             mWidgetImageContainer.addView(mAppWidgetHostViewPreview, /* index= */ 0);
             mWidgetImage.setVisibility(View.GONE);
-            applyPreview((Drawable) null);
+            applyPreview(null);
+            return;
+        }
+        if (cachedPreview != null) {
+            applyPreview(cachedPreview);
             return;
         }
         if (mActiveRequest != null) {
             return;
         }
         mActiveRequest = mWidgetPreviewLoader.loadPreview(
-                BaseActivity.fromContext(getContext()), mItem,
-                new Size(mPreviewWidth, mPreviewHeight),
-                this::applyPreview);
-    }
-
-    /** Sets the widget preview image size in number of cells. */
-    public Size setPreviewSize(WidgetItem widgetItem) {
-        return setPreviewSize(widgetItem, 1f);
-    }
-
-    /** Sets the widget preview image size, in number of cells, and preview scale. */
-    public Size setPreviewSize(WidgetItem widgetItem, float previewScale) {
-        DeviceProfile deviceProfile = mActivity.getDeviceProfile();
-        Size widgetSize = WidgetSizes.getWidgetItemSizePx(getContext(), deviceProfile, widgetItem);
-        mPreviewWidth = widgetSize.getWidth();
-        mPreviewHeight = widgetSize.getHeight();
-        mPreviewScale = previewScale;
-        return widgetSize;
-    }
-
-    @Override
-    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
-            int oldTop, int oldRight, int oldBottom) {
-        removeOnLayoutChangeListener(this);
-        ensurePreview();
+                mItem, new Size(mTargetPreviewWidth, mTargetPreviewHeight), callback);
     }
 
     @Override
@@ -390,17 +426,6 @@
         mLongPressHelper.cancelLongPress();
     }
 
-    /**
-     * Helper method to get the string info of the tag.
-     */
-    private String getTagToString() {
-        if (getTag() instanceof PendingAddWidgetInfo ||
-                getTag() instanceof PendingAddShortcutInfo) {
-            return getTag().toString();
-        }
-        return "";
-    }
-
     private static NavigableAppWidgetHostView createAppWidgetHostView(Context context) {
         return new NavigableAppWidgetHostView(context) {
             @Override
@@ -411,12 +436,7 @@
     }
 
     private static boolean isLauncherContext(Context context) {
-        try {
-            Launcher.getLauncher(context);
-            return true;
-        } catch (Exception e) {
-            return false;
-        }
+        return ActivityContext.lookupContext(context) instanceof Launcher;
     }
 
     @Override
@@ -429,4 +449,62 @@
         super.onInitializeAccessibilityNodeInfo(info);
         info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
     }
+
+    private static void setNoClip(ViewGroup view) {
+        view.setClipChildren(false);
+        view.setClipToPadding(false);
+    }
+
+    private float measureAndComputeWidgetPreviewScale() {
+        if (mAppWidgetHostViewPreview.getChildCount() != 1) {
+            return 1f;
+        }
+
+        // Measure the largest possible width & height that the app widget wants to display.
+        mAppWidgetHostViewPreview.measure(
+                makeMeasureSpec(MAX_MEASURE_SPEC_DIMENSION, MeasureSpec.UNSPECIFIED),
+                makeMeasureSpec(MAX_MEASURE_SPEC_DIMENSION, MeasureSpec.UNSPECIFIED));
+        if (mRemoteViewsPreview != null) {
+            // If RemoteViews contains multiple sizes, the best fit sized RemoteViews will be
+            // selected in onLayout. To work out the right measurement, let's layout and then
+            // measure again.
+            mAppWidgetHostViewPreview.layout(
+                    /* left= */ 0,
+                    /* top= */ 0,
+                    /* right= */ mTargetPreviewWidth,
+                    /* bottom= */ mTargetPreviewHeight);
+            mAppWidgetHostViewPreview.measure(
+                    makeMeasureSpec(mTargetPreviewWidth, MeasureSpec.UNSPECIFIED),
+                    makeMeasureSpec(mTargetPreviewHeight, MeasureSpec.UNSPECIFIED));
+
+        }
+        View widgetContent = mAppWidgetHostViewPreview.getChildAt(0);
+        int appWidgetContentWidth = widgetContent.getMeasuredWidth();
+        int appWidgetContentHeight = widgetContent.getMeasuredHeight();
+        if (appWidgetContentWidth == 0 || appWidgetContentHeight == 0) {
+            return 1f;
+        }
+
+        // If the width / height of the widget content is set to wrap content, overrides the width /
+        // height with the measured dimension. This avoids incorrect measurement after scaling.
+        FrameLayout.LayoutParams layoutParam =
+                (FrameLayout.LayoutParams) widgetContent.getLayoutParams();
+        if (layoutParam.width == WRAP_CONTENT) {
+            layoutParam.width = widgetContent.getMeasuredWidth();
+        }
+        if (layoutParam.height == WRAP_CONTENT) {
+            layoutParam.height = widgetContent.getMeasuredHeight();
+        }
+        widgetContent.setLayoutParams(layoutParam);
+
+        int horizontalPadding = mAppWidgetHostViewPreview.getPaddingStart()
+                + mAppWidgetHostViewPreview.getPaddingEnd();
+        int verticalPadding = mAppWidgetHostViewPreview.getPaddingTop()
+                + mAppWidgetHostViewPreview.getPaddingBottom();
+        return Math.min(
+                (mTargetPreviewWidth - horizontalPadding) * mPreviewContainerScale
+                        / appWidgetContentWidth,
+                (mTargetPreviewHeight - verticalPadding) * mPreviewContainerScale
+                        / appWidgetContentHeight);
+    }
 }
diff --git a/src/com/android/launcher3/widget/WidgetPreviewLoader.java b/src/com/android/launcher3/widget/WidgetPreviewLoader.java
deleted file mode 100644
index ff5c82f..0000000
--- a/src/com/android/launcher3/widget/WidgetPreviewLoader.java
+++ /dev/null
@@ -1,47 +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.widget;
-
-import android.graphics.Bitmap;
-import android.os.CancellationSignal;
-import android.util.Size;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.UiThread;
-
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.model.WidgetItem;
-
-/** Asynchronous loader of preview bitmaps for {@link WidgetItem}s. */
-public interface WidgetPreviewLoader {
-    /**
-     * Loads a widget preview and calls back to {@code callback} when complete.
-     *
-     * @return a {@link CancellationSignal} which can be used to cancel the request.
-     */
-    @NonNull
-    @UiThread
-    CancellationSignal loadPreview(
-            @NonNull BaseActivity activity,
-            @NonNull WidgetItem item,
-            @NonNull Size previewSize,
-            @NonNull WidgetPreviewLoadedCallback callback);
-
-    /** Callback class for requests to {@link WidgetPreviewLoader}. */
-    interface WidgetPreviewLoadedCallback {
-        void onPreviewLoaded(@NonNull Bitmap preview);
-    }
-}
diff --git a/src/com/android/launcher3/widget/WidgetSections.java b/src/com/android/launcher3/widget/WidgetSections.java
new file mode 100644
index 0000000..c45b095
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetSections.java
@@ -0,0 +1,134 @@
+/*
+ * 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.widget;
+
+import static android.content.res.Resources.ID_NULL;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.StringRes;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.IntSet;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Map;
+
+/** A helper class to parse widget sections (categories) resource overlay. */
+public final class WidgetSections {
+    /** The package is not categorized in the widget tray. */
+    public static final int NO_CATEGORY = -1;
+
+    private static final String TAG_SECTION_NAME = "section";
+    private static final String TAG_WIDGET_NAME = "widget";
+
+    private static SparseArray<WidgetSection> sWidgetSections;
+    private static Map<ComponentName, IntSet> sWidgetsToCategories;
+
+    /** Returns a list of widget sections that are shown in the widget picker. */
+    public static synchronized SparseArray<WidgetSection> getWidgetSections(Context context) {
+        if (sWidgetSections != null) {
+            return sWidgetSections;
+        }
+        parseWidgetSectionsXml(context);
+        return sWidgetSections;
+    }
+
+    /** Returns a map which maps app widget providers to app widget categories. */
+    public static synchronized Map<ComponentName, IntSet> getWidgetsToCategory(
+            Context context) {
+        if (sWidgetsToCategories != null) {
+            return sWidgetsToCategories;
+        }
+        parseWidgetSectionsXml(context);
+        return sWidgetsToCategories;
+    }
+
+    private static synchronized void parseWidgetSectionsXml(Context context) {
+        SparseArray<WidgetSection> widgetSections = new SparseArray();
+        Map<ComponentName, IntSet> widgetsToCategories = new ArrayMap<>();
+        try (XmlResourceParser parser = context.getResources().getXml(R.xml.widget_sections)) {
+            final int depth = parser.getDepth();
+            int type;
+            while (((type = parser.next()) != XmlPullParser.END_TAG
+                    || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+                if ((type == XmlPullParser.START_TAG)
+                        && TAG_SECTION_NAME.equals(parser.getName())) {
+                    AttributeSet sectionAttributes = Xml.asAttributeSet(parser);
+                    WidgetSection section = new WidgetSection(context, sectionAttributes);
+                    final int sectionDepth = parser.getDepth();
+                    while (((type = parser.next()) != XmlPullParser.END_TAG
+                                    || parser.getDepth() > sectionDepth)
+                            && type != XmlPullParser.END_DOCUMENT) {
+                        if ((type == XmlPullParser.START_TAG)
+                                && TAG_WIDGET_NAME.equals(parser.getName())) {
+                            TypedArray a = context.obtainStyledAttributes(
+                                    Xml.asAttributeSet(parser), R.styleable.WidgetSections);
+                            ComponentName provider = ComponentName.unflattenFromString(
+                                    a.getString(R.styleable.WidgetSections_provider));
+                            boolean alsoKeepInApp = a.getBoolean(
+                                    R.styleable.WidgetSections_alsoKeepInApp,
+                                    /* defValue= */ false);
+                            final IntSet categories;
+                            if (widgetsToCategories.containsKey(provider)) {
+                                categories = widgetsToCategories.get(provider);
+                            } else {
+                                categories = new IntSet();
+                                widgetsToCategories.put(provider, categories);
+                            }
+                            if (alsoKeepInApp) {
+                                categories.add(NO_CATEGORY);
+                            }
+                            categories.add(section.mCategory);
+                        }
+                    }
+                    widgetSections.put(section.mCategory, section);
+                }
+            }
+            sWidgetSections = widgetSections;
+            sWidgetsToCategories = widgetsToCategories;
+        } catch (IOException | XmlPullParserException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** A data class which contains a widget section's information. */
+    public static final class WidgetSection {
+        public final int mCategory;
+        @StringRes
+        public final int mSectionTitle;
+        @DrawableRes
+        public final int mSectionDrawable;
+
+        public WidgetSection(Context context, AttributeSet attrs) {
+            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WidgetSections);
+            mCategory = a.getInt(R.styleable.WidgetSections_category, NO_CATEGORY);
+            mSectionTitle = a.getResourceId(R.styleable.WidgetSections_sectionTitle, ID_NULL);
+            mSectionDrawable = a.getResourceId(R.styleable.WidgetSections_sectionDrawable, ID_NULL);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index dedcc65..b152ddc 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -36,8 +36,6 @@
 import android.widget.TableRow;
 import android.widget.TextView;
 
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.model.WidgetItem;
@@ -71,8 +69,8 @@
     private static final long EDUCATION_TIP_DELAY_MS = 300;
 
     private ItemInfo mOriginalItemInfo;
-    private final int mMaxTableHeight;
-    private int mMaxHorizontalSpan = 4;
+    private int mMaxHorizontalSpan = DEFAULT_MAX_HORIZONTAL_SPANS;
+    private final int mWidgetCellHorizontalPadding;
 
     private final OnLayoutChangeListener mLayoutChangeListenerToShowTips =
             new OnLayoutChangeListener() {
@@ -110,13 +108,11 @@
     public WidgetsBottomSheet(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         setWillNotDraw(false);
-        DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
-        // Set the max table height to 2 / 3 of the grid height so that the bottom picker won't
-        // take over the entire view vertically.
-        mMaxTableHeight = deviceProfile.inv.numRows * 2 / 3  * deviceProfile.cellHeightPx;
         if (!hasSeenEducationTip()) {
             addOnLayoutChangeListener(mLayoutChangeListenerToShowTips);
         }
+        mWidgetCellHorizontalPadding = getResources().getDimensionPixelSize(
+                R.dimen.widget_cell_horizontal_padding);
     }
 
     @Override
@@ -137,10 +133,7 @@
     private boolean updateMaxSpansPerRow() {
         if (getMeasuredWidth() == 0) return false;
 
-        int paddingPx = 2 * getResources().getDimensionPixelOffset(
-                R.dimen.widget_cell_horizontal_padding);
-        int maxHorizontalSpan = findViewById(R.id.widgets_table).getMeasuredWidth()
-                / (mActivityContext.getDeviceProfile().cellWidthPx + paddingPx);
+        int maxHorizontalSpan = computeMaxHorizontalSpans(mContent, mWidgetCellHorizontalPadding);
         if (mMaxHorizontalSpan != maxHorizontalSpan) {
             // Ensure the table layout is showing widgets in the right column after measure.
             mMaxHorizontalSpan = maxHorizontalSpan;
@@ -163,13 +156,9 @@
 
         setTranslationShift(mTranslationShift);
 
-        // Ensure the scroll view height is not larger than mMaxTableHeight, which is a value
-        // smaller than the entire screen height.
         ScrollView widgetsTableScrollView = findViewById(R.id.widgets_table_scroll_view);
-        if (widgetsTableScrollView.getMeasuredHeight() > mMaxTableHeight) {
-            ViewGroup.LayoutParams layoutParams = widgetsTableScrollView.getLayoutParams();
-            layoutParams.height = mMaxTableHeight;
-            widgetsTableScrollView.setLayoutParams(layoutParams);
+        TableLayout widgetsTable = findViewById(R.id.widgets_table);
+        if (widgetsTable.getMeasuredHeight() > widgetsTableScrollView.getMeasuredHeight()) {
             findViewById(R.id.collapse_handle).setVisibility(VISIBLE);
         }
     }
@@ -194,19 +183,16 @@
         TableLayout widgetsTable = findViewById(R.id.widgets_table);
         widgetsTable.removeAllViews();
 
-        WidgetsTableUtils.groupWidgetItemsIntoTable(widgets, mMaxHorizontalSpan).forEach(row -> {
-            TableRow tableRow = new TableRow(getContext());
-            tableRow.setGravity(Gravity.TOP);
-            row.forEach(widgetItem -> {
-                WidgetCell widget = addItemCell(tableRow);
-                widget.setPreviewSize(widgetItem);
-                widget.applyFromCellItem(widgetItem, LauncherAppState.getInstance(mActivityContext)
-                        .getWidgetCache());
-                widget.ensurePreview();
-                widget.setVisibility(View.VISIBLE);
-            });
-            widgetsTable.addView(tableRow);
-        });
+        WidgetsTableUtils.groupWidgetItemsIntoTableWithReordering(widgets, mMaxHorizontalSpan)
+                .forEach(row -> {
+                    TableRow tableRow = new TableRow(getContext());
+                    tableRow.setGravity(Gravity.TOP);
+                    row.forEach(widgetItem -> {
+                        WidgetCell widget = addItemCell(tableRow);
+                        widget.applyFromCellItem(widgetItem);
+                    });
+                    widgetsTable.addView(tableRow);
+                });
     }
 
     @Override
@@ -272,6 +258,14 @@
     }
 
     @Override
+    protected void onContentHorizontalMarginChanged(int contentHorizontalMarginInPx) {
+        ViewGroup.MarginLayoutParams layoutParams =
+                ((ViewGroup.MarginLayoutParams) findViewById(R.id.widgets_table).getLayoutParams());
+        layoutParams.setMarginStart(contentHorizontalMarginInPx);
+        layoutParams.setMarginEnd(contentHorizontalMarginInPx);
+    }
+
+    @Override
     protected Pair<View, String> getAccessibilityTarget() {
         return Pair.create(findViewById(R.id.title),  getContext().getString(
                 mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed));
diff --git a/src/com/android/launcher3/widget/custom/CustomWidgetManager.java b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
index 329a444..2e2a968 100644
--- a/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
+++ b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.systemui.plugins.CustomWidgetPlugin;
@@ -46,7 +47,7 @@
 /**
  * CustomWidgetManager handles custom widgets implemented as a plugin.
  */
-public class CustomWidgetManager implements PluginListener<CustomWidgetPlugin> {
+public class CustomWidgetManager implements PluginListener<CustomWidgetPlugin>, SafeCloseable {
 
     public static final MainThreadInitializedObject<CustomWidgetManager> INSTANCE =
             new MainThreadInitializedObject<>(CustomWidgetManager::new);
@@ -71,7 +72,8 @@
                 .addPluginListener(this, CustomWidgetPlugin.class, true);
     }
 
-    public void onDestroy() {
+    @Override
+    public void close() {
         PluginManagerWrapper.INSTANCE.get(mContext).removePluginListener(this);
     }
 
diff --git a/src/com/android/launcher3/widget/dragndrop/AppWidgetHostViewDragListener.java b/src/com/android/launcher3/widget/dragndrop/AppWidgetHostViewDragListener.java
index 4a60983..3e54b33 100644
--- a/src/com/android/launcher3/widget/dragndrop/AppWidgetHostViewDragListener.java
+++ b/src/com/android/launcher3/widget/dragndrop/AppWidgetHostViewDragListener.java
@@ -24,7 +24,6 @@
 /** A drag listener of {@link LauncherAppWidgetHostView}. */
 public final class AppWidgetHostViewDragListener implements DragController.DragListener {
     private final Launcher mLauncher;
-    private DropTarget.DragObject mDragObject;
     private LauncherAppWidgetHostView mAppWidgetHostView;
 
     public AppWidgetHostViewDragListener(Launcher launcher) {
@@ -34,9 +33,8 @@
     @Override
     public void onDragStart(DropTarget.DragObject dragObject, DragOptions unused) {
         if (dragObject.dragView.getContentView() instanceof LauncherAppWidgetHostView) {
-            mDragObject = dragObject;
             mAppWidgetHostView = (LauncherAppWidgetHostView) dragObject.dragView.getContentView();
-            mAppWidgetHostView.startDrag(this);
+            mAppWidgetHostView.startDrag();
         } else {
             mLauncher.getDragController().removeDragListener(this);
         }
@@ -47,11 +45,4 @@
         mAppWidgetHostView.endDrag();
         mLauncher.getDragController().removeDragListener(this);
     }
-
-    /** Notifies when there is a content change in the drag view. */
-    public void onDragContentChanged() {
-        if (mDragObject.dragView != null) {
-            mDragObject.dragView.invalidate();
-        }
-    }
 }
diff --git a/src/com/android/launcher3/widget/model/WidgetListSpaceEntry.java b/src/com/android/launcher3/widget/model/WidgetListSpaceEntry.java
new file mode 100644
index 0000000..7f24905
--- /dev/null
+++ b/src/com/android/launcher3/widget/model/WidgetListSpaceEntry.java
@@ -0,0 +1,41 @@
+/*
+ * 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.widget.model;
+
+import android.os.Process;
+
+import com.android.launcher3.model.data.PackageItemInfo;
+
+import java.util.Collections;
+
+/**
+ * Entry representing the top empty space
+ */
+public class WidgetListSpaceEntry extends WidgetsListBaseEntry {
+
+    public WidgetListSpaceEntry() {
+        super(new PackageItemInfo(/* packageName= */ "", Process.myUserHandle()),
+                /* titleSectionName= */ "",
+                Collections.EMPTY_LIST);
+        mPkgItem.title = "";
+    }
+
+    @Override
+    public int getRank() {
+        return RANK_WIDGETS_TOP_SPACE;
+    }
+}
diff --git a/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java b/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java
index abc79ff..1d1c9dc 100644
--- a/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java
+++ b/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java
@@ -73,11 +73,13 @@
     }
 
     @Retention(SOURCE)
-    @IntDef({RANK_WIDGETS_LIST_HEADER, RANK_WIDGETS_LIST_SEARCH_HEADER, RANK_WIDGETS_LIST_CONTENT})
+    @IntDef({RANK_WIDGETS_TOP_SPACE, RANK_WIDGETS_LIST_HEADER, RANK_WIDGETS_LIST_SEARCH_HEADER,
+            RANK_WIDGETS_LIST_CONTENT})
     public @interface Rank {
     }
 
-    public static final int RANK_WIDGETS_LIST_HEADER = 1;
-    public static final int RANK_WIDGETS_LIST_SEARCH_HEADER = 2;
-    public static final int RANK_WIDGETS_LIST_CONTENT = 3;
+    public static final int RANK_WIDGETS_TOP_SPACE = 1;
+    public static final int RANK_WIDGETS_LIST_HEADER = 2;
+    public static final int RANK_WIDGETS_LIST_SEARCH_HEADER = 3;
+    public static final int RANK_WIDGETS_LIST_CONTENT = 4;
 }
diff --git a/src/com/android/launcher3/widget/picker/OnHeaderClickListener.java b/src/com/android/launcher3/widget/picker/OnHeaderClickListener.java
index 7372751..35f11bd 100644
--- a/src/com/android/launcher3/widget/picker/OnHeaderClickListener.java
+++ b/src/com/android/launcher3/widget/picker/OnHeaderClickListener.java
@@ -24,5 +24,5 @@
     /**
      * Calls when a header is clicked to show / hide widgets for a package.
      */
-    void onHeaderClicked(boolean showWidgets, PackageUserKey packageUserKey);
+    void onHeaderClicked(boolean showWidgets, PackageUserKey key);
 }
diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
index 6643779..716dcf3 100644
--- a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
+++ b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
@@ -15,297 +15,148 @@
  */
 package com.android.launcher3.widget.picker;
 
-import android.animation.ValueAnimator;
-import android.graphics.Point;
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.util.FloatProperty;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewGroup.MarginLayoutParams;
-import android.widget.RelativeLayout;
+import android.view.ViewGroup;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
 
-import com.android.launcher3.views.RecyclerViewFastScroller;
-import com.android.launcher3.widget.picker.WidgetsFullSheet.SearchAndRecommendationViewHolder;
-import com.android.launcher3.workprofile.PersonalWorkPagedView;
+import com.android.launcher3.R;
+import com.android.launcher3.widget.picker.WidgetsSpaceViewHolderBinder.EmptySpaceView;
+import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
 
 /**
  * A controller which measures & updates {@link WidgetsFullSheet}'s views padding, margin and
  * vertical displacement upon scrolling.
  */
 final class SearchAndRecommendationsScrollController implements
-        RecyclerViewFastScroller.OnFastScrollChangeListener, ValueAnimator.AnimatorUpdateListener {
-    private final boolean mHasWorkProfile;
-    private final SearchAndRecommendationViewHolder mViewHolder;
-    private final View mSearchAndRecommendationViewParent;
-    private final WidgetsRecyclerView mPrimaryRecyclerView;
-    private final WidgetsRecyclerView mSearchRecyclerView;
-    private final TextView mNoWidgetsView;
-    private final int mTabsHeight;
-    private final ValueAnimator mAnimator = ValueAnimator.ofInt(0, 0);
-    private final Point mTempOffset = new Point();
-    private int mBottomInset;
+        RecyclerView.OnChildAttachStateChangeListener {
 
-    // The following are only non null if mHasWorkProfile is true.
-    @Nullable private final WidgetsRecyclerView mWorkRecyclerView;
-    @Nullable private final View mPrimaryWorkTabsView;
-    @Nullable private final PersonalWorkPagedView mPrimaryWorkViewPager;
+    private static final FloatProperty<SearchAndRecommendationsScrollController> SCROLL_OFFSET =
+            new FloatProperty<SearchAndRecommendationsScrollController>("scrollAnimOffset") {
+        @Override
+        public void setValue(SearchAndRecommendationsScrollController controller, float offset) {
+            controller.mScrollOffset = offset;
+            controller.updateHeaderScroll();
+        }
+
+        @Override
+        public Float get(SearchAndRecommendationsScrollController controller) {
+            return controller.mScrollOffset;
+        }
+    };
+
+    private static final MotionEventProxyMethod INTERCEPT_PROXY = ViewGroup::onInterceptTouchEvent;
+    private static final MotionEventProxyMethod TOUCH_PROXY = ViewGroup::onTouchEvent;
+
+    final SearchAndRecommendationsView mContainer;
+    final View mSearchBarContainer;
+    final WidgetsSearchBar mSearchBar;
+    final TextView mHeaderTitle;
+    final WidgetsRecommendationTableLayout mRecommendedWidgetsTable;
+    @Nullable final View mTabBar;
 
     private WidgetsRecyclerView mCurrentRecyclerView;
-    private int mCurrentRecyclerViewScrollY = 0;
+    private EmptySpaceView mCurrentEmptySpaceView;
 
-    private OnContentChangeListener mOnContentChangeListener = () -> onScrollChanged();
-
-    /**
-     * The vertical distance, in pixels, until the search is pinned at the top of the screen when
-     * the user scrolls down the recycler view.
-     */
-    private int mCollapsibleHeightForSearch = 0;
-    /**
-     * The vertical distance, in pixels, until the recommendation table disappears from the top of
-     * the screen when the user scrolls down the recycler view.
-     */
-    private int mCollapsibleHeightForRecommendation = 0;
-    /**
-     * The vertical distance, in pixels, until the tabs is pinned at the top of the screen when the
-     * user scrolls down the recycler view.
-     *
-     * <p>Always 0 if there is no work profile.
-     */
-    private int mCollapsibleHeightForTabs = 0;
+    private float mLastScroll = 0;
+    private float mScrollOffset = 0;
+    private Animator mOffsetAnimator;
 
     private boolean mShouldForwardToRecyclerView = false;
 
+    private int mHeaderHeight;
+
     SearchAndRecommendationsScrollController(
-            boolean hasWorkProfile,
-            int tabsHeight,
-            SearchAndRecommendationViewHolder viewHolder,
-            WidgetsRecyclerView primaryRecyclerView,
-            @Nullable WidgetsRecyclerView workRecyclerView,
-            WidgetsRecyclerView searchRecyclerView,
-            @Nullable View personalWorkTabsView,
-            @Nullable PersonalWorkPagedView primaryWorkViewPager,
-            TextView noWidgetsView) {
-        mHasWorkProfile = hasWorkProfile;
-        mViewHolder = viewHolder;
-        mViewHolder.mContainer.setSearchAndRecommendationScrollController(this);
-        mSearchAndRecommendationViewParent = (View) mViewHolder.mContainer.getParent();
-        mPrimaryRecyclerView = primaryRecyclerView;
-        mWorkRecyclerView = workRecyclerView;
-        mSearchRecyclerView = searchRecyclerView;
-        mPrimaryWorkTabsView = personalWorkTabsView;
-        mPrimaryWorkViewPager = primaryWorkViewPager;
-        mTabsHeight = tabsHeight;
-        mNoWidgetsView = noWidgetsView;
-        setCurrentRecyclerView(mPrimaryRecyclerView, /* animateReset= */ false);
+            SearchAndRecommendationsView searchAndRecommendationContainer) {
+        mContainer = searchAndRecommendationContainer;
+        mSearchBarContainer = mContainer.findViewById(R.id.search_bar_container);
+        mSearchBar = mContainer.findViewById(R.id.widgets_search_bar);
+        mHeaderTitle = mContainer.findViewById(R.id.title);
+        mRecommendedWidgetsTable = mContainer.findViewById(R.id.recommended_widget_table);
+        mTabBar = mContainer.findViewById(R.id.tabs);
+
+        mContainer.setSearchAndRecommendationScrollController(this);
     }
 
     public void setCurrentRecyclerView(WidgetsRecyclerView currentRecyclerView) {
-        setCurrentRecyclerView(currentRecyclerView, /* animateReset= */ true);
-    }
-
-    /** Sets the current active {@link WidgetsRecyclerView}. */
-    private void setCurrentRecyclerView(WidgetsRecyclerView currentRecyclerView,
-            boolean animateReset) {
-        if (mCurrentRecyclerView == currentRecyclerView) {
-            return;
-        }
+        boolean animateReset = mCurrentRecyclerView != null;
         if (mCurrentRecyclerView != null) {
-            mCurrentRecyclerView.setOnContentChangeListener(null);
+            mCurrentRecyclerView.removeOnChildAttachStateChangeListener(this);
         }
         mCurrentRecyclerView = currentRecyclerView;
-        mCurrentRecyclerView.setOnContentChangeListener(mOnContentChangeListener);
+        mCurrentRecyclerView.addOnChildAttachStateChangeListener(this);
+        findCurrentEmptyView();
         reset(animateReset);
     }
 
-    /**
-     * Updates padding of {@link WidgetsFullSheet} contents to include {@code bottomInset} wherever
-     * necessary.
-     */
-    public boolean updateBottomInset(int bottomInset) {
-        mBottomInset = bottomInset;
-        return updateMarginAndPadding();
+    public int getHeaderHeight() {
+        return mHeaderHeight;
+    }
+
+    private void updateHeaderScroll() {
+        mLastScroll = getCurrentScroll();
+        mHeaderTitle.setTranslationY(mLastScroll);
+        mRecommendedWidgetsTable.setTranslationY(mLastScroll);
+
+        float searchYDisplacement = Math.max(mLastScroll, -mSearchBarContainer.getTop());
+        mSearchBarContainer.setTranslationY(searchYDisplacement);
+
+        if (mTabBar != null) {
+            float tabsDisplacement = Math.max(mLastScroll, -mTabBar.getTop()
+                    + mSearchBarContainer.getHeight());
+            mTabBar.setTranslationY(tabsDisplacement);
+        }
+    }
+
+    private float getCurrentScroll() {
+        return mScrollOffset + (mCurrentEmptySpaceView == null ? 0 : mCurrentEmptySpaceView.getY());
     }
 
     /**
-     * Updates the margin and padding of {@link WidgetsFullSheet} to accumulate collapsible views.
+     * Updates the scrollable header height
      *
-     * @return {@code true} if margins or/and padding of views in the search and recommendations
-     * container have been updated.
+     * @return {@code true} if the header height or dependent property changed.
      */
-    public boolean updateMarginAndPadding() {
-        boolean hasMarginOrPaddingUpdated = false;
-        mCollapsibleHeightForSearch = measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle);
-        mCollapsibleHeightForRecommendation =
-                measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle)
-                        + measureHeightWithVerticalMargins(mViewHolder.mCollapseHandle)
-                        + measureHeightWithVerticalMargins((View) mViewHolder.mSearchBarContainer)
-                        + measureHeightWithVerticalMargins(mViewHolder.mRecommendedWidgetsTable);
+    public boolean updateHeaderHeight() {
+        boolean hasSizeUpdated = false;
 
-        int topContainerHeight = measureHeightWithVerticalMargins(mViewHolder.mContainer);
-        int noWidgetsViewHeight =  topContainerHeight - mBottomInset;
-
-        if (mHasWorkProfile) {
-            mCollapsibleHeightForTabs = measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle)
-                    + measureHeightWithVerticalMargins(mViewHolder.mRecommendedWidgetsTable);
-            // In a work profile setup, the full widget sheet contains the following views:
-            //           ------- (pinned)           -|
-            //          Widgets (collapsible)       -|---> LinearLayout for search & recommendations
-            //          Search bar (pinned)         -|
-            //  Widgets recommendation (collapsible)-|
-            //      Personal | Work (pinned)
-            //           View Pager
-            //
-            // Views after the search & recommendations are not bound by RelativelyLayout param.
-            // To position them on the expected location, padding & margin are added to these views
-
-            // Tabs should have a padding of the height of the search & recommendations container.
-            RelativeLayout.LayoutParams tabsLayoutParams =
-                    (RelativeLayout.LayoutParams) mPrimaryWorkTabsView.getLayoutParams();
-            tabsLayoutParams.topMargin = topContainerHeight;
-            mPrimaryWorkTabsView.setLayoutParams(tabsLayoutParams);
-
-            // Instead of setting the top offset directly, we split the top offset into two values:
-            // 1. topOffsetAfterAllViewsCollapsed: this is the top offset after all collapsible
-            //    views are no longer visible on the screen.
-            //    This value is set as the margin for the view pager.
-            // 2. mMaxCollapsibleDistance
-            //    This value is set as the padding for the recycler views in order to work with
-            //    clipToPadding="false", which is an attribute for not showing top / bottom padding
-            //    when a recycler view has not reached the top or bottom of the list.
-            //    e.g. a list of 10 entries, only 3 entries are visible at a time.
-            //         case 1: recycler view is scrolled to the top. Top padding is visible/
-            //         (top padding)
-            //         item 1
-            //         item 2
-            //         item 3
-            //
-            //         case 2: recycler view is scrolled to the middle. No padding is visible.
-            //         item 4
-            //         item 5
-            //         item 6
-            //
-            //         case 3: recycler view is scrolled to the end. bottom padding is visible.
-            //         item 8
-            //         item 9
-            //         item 10
-            //         (bottom padding): not set in this case.
-            //
-            // When the views are first inflated, the sum of topOffsetAfterAllViewsCollapsed and
-            // mMaxCollapsibleDistance should equal to the top container height.
-            int topOffsetAfterAllViewsCollapsed =
-                    topContainerHeight + mTabsHeight - mCollapsibleHeightForTabs;
-
-            if (mPrimaryWorkTabsView.getVisibility() == View.VISIBLE) {
-                noWidgetsViewHeight += mTabsHeight;
-            }
-
-            RelativeLayout.LayoutParams viewPagerLayoutParams =
-                    (RelativeLayout.LayoutParams) mPrimaryWorkViewPager.getLayoutParams();
-            if (viewPagerLayoutParams.topMargin != topOffsetAfterAllViewsCollapsed) {
-                viewPagerLayoutParams.topMargin = topOffsetAfterAllViewsCollapsed;
-                mPrimaryWorkViewPager.setLayoutParams(viewPagerLayoutParams);
-                hasMarginOrPaddingUpdated = true;
-            }
-
-            if (mPrimaryRecyclerView.getPaddingTop() != mCollapsibleHeightForTabs) {
-                mPrimaryRecyclerView.setPadding(
-                        mPrimaryRecyclerView.getPaddingLeft(),
-                        mCollapsibleHeightForTabs,
-                        mPrimaryRecyclerView.getPaddingRight(),
-                        mPrimaryRecyclerView.getPaddingBottom());
-                hasMarginOrPaddingUpdated = true;
-            }
-            if (mWorkRecyclerView.getPaddingTop() != mCollapsibleHeightForTabs) {
-                mWorkRecyclerView.setPadding(
-                        mWorkRecyclerView.getPaddingLeft(),
-                        mCollapsibleHeightForTabs,
-                        mWorkRecyclerView.getPaddingRight(),
-                        mWorkRecyclerView.getPaddingBottom());
-                hasMarginOrPaddingUpdated = true;
-            }
-        } else {
-            if (mPrimaryRecyclerView.getPaddingTop() != topContainerHeight) {
-                mPrimaryRecyclerView.setPadding(
-                        mPrimaryRecyclerView.getPaddingLeft(),
-                        topContainerHeight,
-                        mPrimaryRecyclerView.getPaddingRight(),
-                        mPrimaryRecyclerView.getPaddingBottom());
-                hasMarginOrPaddingUpdated = true;
-            }
-        }
-        if (mSearchRecyclerView.getPaddingTop() != topContainerHeight) {
-            mSearchRecyclerView.setPadding(
-                    mSearchRecyclerView.getPaddingLeft(),
-                    topContainerHeight,
-                    mSearchRecyclerView.getPaddingRight(),
-                    mSearchRecyclerView.getPaddingBottom());
-            hasMarginOrPaddingUpdated = true;
-        }
-        if (mNoWidgetsView.getPaddingTop() != noWidgetsViewHeight) {
-            mNoWidgetsView.setPadding(
-                    mNoWidgetsView.getPaddingLeft(),
-                    noWidgetsViewHeight,
-                    mNoWidgetsView.getPaddingRight(),
-                    mNoWidgetsView.getPaddingBottom());
-            hasMarginOrPaddingUpdated = true;
-        }
-        return hasMarginOrPaddingUpdated;
-    }
-
-    @Override
-    public void onScrollChanged() {
-        int recyclerViewYOffset = mCurrentRecyclerView.getCurrentScrollY();
-        if (recyclerViewYOffset < 0) return;
-        mCurrentRecyclerViewScrollY = recyclerViewYOffset;
-        if (mAnimator.isStarted()) {
-            mAnimator.cancel();
-        }
-        applyVerticalTransition();
-    }
-
-    /**
-     * Changes the displacement of collapsible views (e.g. title & widget recommendations) and fixed
-     * views (e.g. recycler views, tabs) upon scrolling / content changes in the recycler view.
-     */
-    private void applyVerticalTransition() {
-        if (mCollapsibleHeightForRecommendation > 0) {
-            int yDisplacement = Math.max(-mCurrentRecyclerViewScrollY,
-                    -mCollapsibleHeightForRecommendation);
-            mViewHolder.mHeaderTitle.setTranslationY(yDisplacement);
-            mViewHolder.mRecommendedWidgetsTable.setTranslationY(yDisplacement);
+        int headerHeight = mContainer.getMeasuredHeight();
+        if (headerHeight != mHeaderHeight) {
+            mHeaderHeight = headerHeight;
+            hasSizeUpdated = true;
         }
 
-        if (mCollapsibleHeightForSearch > 0) {
-            int searchYDisplacement = Math.max(-mCurrentRecyclerViewScrollY,
-                    -mCollapsibleHeightForSearch);
-            mViewHolder.mSearchBarContainer.setTranslationY(searchYDisplacement);
+        if (mCurrentEmptySpaceView != null
+                && mCurrentEmptySpaceView.setFixedHeight(mHeaderHeight)) {
+            hasSizeUpdated = true;
         }
-
-        if (mHasWorkProfile && mCollapsibleHeightForTabs > 0) {
-            int yDisplacementForTabs = Math.max(-mCurrentRecyclerViewScrollY,
-                    -mCollapsibleHeightForTabs);
-            mPrimaryWorkTabsView.setTranslationY(yDisplacementForTabs);
-        }
+        return hasSizeUpdated;
     }
 
     /** Resets any previous view translation. */
     public void reset(boolean animate) {
-        if (mCurrentRecyclerViewScrollY == 0) {
-            return;
-        }
-        if (mAnimator.isStarted()) {
-            mAnimator.cancel();
+        if (mOffsetAnimator != null) {
+            mOffsetAnimator.cancel();
+            mOffsetAnimator = null;
         }
 
-        if (animate) {
-            mAnimator.setIntValues(mCurrentRecyclerViewScrollY, 0);
-            mAnimator.addUpdateListener(this);
-            mAnimator.setDuration(300);
-            mAnimator.start();
+        mScrollOffset = 0;
+        if (!animate) {
+            updateHeaderScroll();
         } else {
-            mCurrentRecyclerViewScrollY = 0;
-            applyVerticalTransition();
+            float startValue = mLastScroll - getCurrentScroll();
+            mOffsetAnimator = ObjectAnimator.ofFloat(this, SCROLL_OFFSET, startValue, 0);
+            mOffsetAnimator.addListener(forEndCallback(() -> mOffsetAnimator = null));
+            mOffsetAnimator.start();
         }
     }
 
@@ -313,61 +164,60 @@
      * Returns {@code true} if a touch event should be intercepted by this controller.
      */
     public boolean onInterceptTouchEvent(MotionEvent event) {
-        calculateMotionEventOffset(mTempOffset);
-        event.offsetLocation(mTempOffset.x, mTempOffset.y);
-        try {
-            mShouldForwardToRecyclerView = mCurrentRecyclerView.onInterceptTouchEvent(event);
-            return mShouldForwardToRecyclerView;
-        } finally {
-            event.offsetLocation(-mTempOffset.x, -mTempOffset.y);
-        }
+        return (mShouldForwardToRecyclerView = proxyMotionEvent(event, INTERCEPT_PROXY));
     }
 
     /**
      * Returns {@code true} if this controller has intercepted and consumed a touch event.
      */
     public boolean onTouchEvent(MotionEvent event) {
-        if (mShouldForwardToRecyclerView) {
-            calculateMotionEventOffset(mTempOffset);
-            event.offsetLocation(mTempOffset.x, mTempOffset.y);
-            try {
-                return mCurrentRecyclerView.onTouchEvent(event);
-            } finally {
-                event.offsetLocation(-mTempOffset.x, -mTempOffset.y);
-            }
-        }
-        return false;
+        return mShouldForwardToRecyclerView && proxyMotionEvent(event, TOUCH_PROXY);
     }
 
-    private void calculateMotionEventOffset(Point p) {
-        p.x = mViewHolder.mContainer.getLeft() - mCurrentRecyclerView.getLeft()
-                - mSearchAndRecommendationViewParent.getLeft();
-        p.y = mViewHolder.mContainer.getTop() - mCurrentRecyclerView.getTop()
-                - mSearchAndRecommendationViewParent.getTop();
-    }
-
-    /** private the height, in pixel, + the vertical margins of a given view. */
-    private static int measureHeightWithVerticalMargins(View view) {
-        if (view.getVisibility() != View.VISIBLE) {
-            return 0;
+    private boolean proxyMotionEvent(MotionEvent event, MotionEventProxyMethod method) {
+        float dx = mCurrentRecyclerView.getLeft() - mContainer.getLeft();
+        float dy = mCurrentRecyclerView.getTop() - mContainer.getTop();
+        event.offsetLocation(dx, dy);
+        try {
+            return method.proxyEvent(mCurrentRecyclerView, event);
+        } finally {
+            event.offsetLocation(-dx, -dy);
         }
-        MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams();
-        return view.getMeasuredHeight() + marginLayoutParams.bottomMargin
-                + marginLayoutParams.topMargin;
     }
 
     @Override
-    public void onAnimationUpdate(ValueAnimator animation) {
-        mCurrentRecyclerViewScrollY = (Integer) animation.getAnimatedValue();
-        applyVerticalTransition();
+    public void onChildViewAttachedToWindow(@NonNull View view) {
+        if (view instanceof EmptySpaceView) {
+            findCurrentEmptyView();
+        }
     }
 
-    /**
-     * A listener to be notified when there is a content change in the recycler view that may affect
-     * the relative position of the search and recommendation container.
-     */
-    public interface OnContentChangeListener {
-        /** Notifies a content change in the recycler view. */
-        void onContentChanged();
+    @Override
+    public void onChildViewDetachedFromWindow(@NonNull View view) {
+        if (view == mCurrentEmptySpaceView) {
+            findCurrentEmptyView();
+        }
+    }
+
+    private void findCurrentEmptyView() {
+        if (mCurrentEmptySpaceView != null) {
+            mCurrentEmptySpaceView.setOnYChangeCallback(null);
+            mCurrentEmptySpaceView = null;
+        }
+        int childCount = mCurrentRecyclerView.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View view = mCurrentRecyclerView.getChildAt(i);
+            if (view instanceof EmptySpaceView) {
+                mCurrentEmptySpaceView = (EmptySpaceView) view;
+                mCurrentEmptySpaceView.setFixedHeight(getHeaderHeight());
+                mCurrentEmptySpaceView.setOnYChangeCallback(this::updateHeaderScroll);
+                return;
+            }
+        }
+    }
+
+    private interface MotionEventProxyMethod {
+
+        boolean proxyEvent(ViewGroup view, MotionEvent event);
     }
 }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index f2fee0a..894c4c9 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -27,6 +27,7 @@
 import android.content.Context;
 import android.content.pm.LauncherApps;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Rect;
 import android.os.Process;
 import android.os.UserHandle;
@@ -56,13 +57,12 @@
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.views.ArrowTipView;
 import com.android.launcher3.views.RecyclerViewFastScroller;
-import com.android.launcher3.views.TopRoundedCornerView;
+import com.android.launcher3.views.SpringRelativeLayout;
 import com.android.launcher3.views.WidgetsEduView;
 import com.android.launcher3.widget.BaseWidgetSheet;
 import com.android.launcher3.widget.LauncherAppWidgetHost.ProviderChangedListener;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 import com.android.launcher3.widget.picker.search.SearchModeListener;
-import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
 import com.android.launcher3.widget.util.WidgetsTableUtils;
 import com.android.launcher3.workprofile.PersonalWorkPagedView;
 import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
@@ -78,7 +78,6 @@
 public class WidgetsFullSheet extends BaseWidgetSheet
         implements ProviderChangedListener, OnActivePageChangedListener,
         WidgetsRecyclerView.HeaderViewDimensionsProvider, SearchModeListener {
-    private static final String TAG = WidgetsFullSheet.class.getSimpleName();
 
     private static final long DEFAULT_OPEN_DURATION = 267;
     private static final long FADE_IN_DURATION = 150;
@@ -148,19 +147,15 @@
             };
 
     private final int mTabsHeight;
-    private final int mViewPagerTopPadding;
-    private final int mSearchAndRecommendationContainerBottomMargin;
-    private final int mWidgetCellHorizontalPadding;
+    private final int mWidgetSheetContentHorizontalPadding;
 
     @Nullable private WidgetsRecyclerView mCurrentWidgetsRecyclerView;
     @Nullable private PersonalWorkPagedView mViewPager;
     private boolean mIsInSearchMode;
     private boolean mIsNoWidgetsViewNeeded;
-    private int mMaxSpansPerRow = 4;
-    private View mTabsView;
+    private int mMaxSpansPerRow = DEFAULT_MAX_HORIZONTAL_SPANS;
     private TextView mNoWidgetsView;
-    private SearchAndRecommendationViewHolder mSearchAndRecommendationViewHolder;
-    private SearchAndRecommendationsScrollController mSearchAndRecommendationsScrollController;
+    private SearchAndRecommendationsScrollController mSearchScrollController;
 
     public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
@@ -168,19 +163,12 @@
         mAdapters.put(AdapterHolder.PRIMARY, new AdapterHolder(AdapterHolder.PRIMARY));
         mAdapters.put(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK));
         mAdapters.put(AdapterHolder.SEARCH, new AdapterHolder(AdapterHolder.SEARCH));
+
+        Resources resources = getResources();
         mTabsHeight = mHasWorkProfile
-                ? getContext().getResources()
-                        .getDimensionPixelSize(R.dimen.all_apps_header_pill_height)
+                ? resources.getDimensionPixelSize(R.dimen.all_apps_header_pill_height)
                 : 0;
-        mViewPagerTopPadding = mHasWorkProfile
-                ? getContext().getResources()
-                    .getDimensionPixelSize(R.dimen.widget_picker_view_pager_top_padding)
-                : 0;
-        mSearchAndRecommendationContainerBottomMargin = getContext().getResources()
-                .getDimensionPixelSize(mHasWorkProfile
-                        ? R.dimen.search_and_recommended_widgets_container_small_bottom_margin
-                        : R.dimen.search_and_recommended_widgets_container_bottom_margin);
-        mWidgetCellHorizontalPadding = 2 * getResources().getDimensionPixelOffset(
+        mWidgetSheetContentHorizontalPadding = 2 * resources.getDimensionPixelSize(
                 R.dimen.widget_cell_horizontal_padding);
     }
 
@@ -192,12 +180,11 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mContent = findViewById(R.id.container);
-        TopRoundedCornerView springLayout = (TopRoundedCornerView) mContent;
 
         LayoutInflater layoutInflater = LayoutInflater.from(getContext());
         int contentLayoutRes = mHasWorkProfile ? R.layout.widgets_full_sheet_paged_view
                 : R.layout.widgets_full_sheet_recyclerview;
-        layoutInflater.inflate(contentLayoutRes, springLayout, true);
+        layoutInflater.inflate(contentLayoutRes, mContent, true);
 
         RecyclerViewFastScroller fastScroller = findViewById(R.id.fast_scroller);
         mAdapters.get(AdapterHolder.PRIMARY).setup(findViewById(R.id.primary_widgets_list_view));
@@ -207,44 +194,27 @@
             mViewPager.initParentViews(this);
             mViewPager.getPageIndicator().setOnActivePageChangedListener(this);
             mViewPager.getPageIndicator().setActiveMarker(AdapterHolder.PRIMARY);
-            mTabsView = findViewById(R.id.tabs);
             findViewById(R.id.tab_personal)
                     .setOnClickListener((View view) -> mViewPager.snapToPage(0));
             findViewById(R.id.tab_work)
                     .setOnClickListener((View view) -> mViewPager.snapToPage(1));
-            fastScroller.setIsRecyclerViewFirstChildInParent(false);
             mAdapters.get(AdapterHolder.WORK).setup(findViewById(R.id.work_widgets_list_view));
         } else {
             mViewPager = null;
         }
 
-        layoutInflater.inflate(R.layout.widgets_full_sheet_search_and_recommendations, springLayout,
-                true);
         mNoWidgetsView = findViewById(R.id.no_widgets_text);
-        mSearchAndRecommendationViewHolder = new SearchAndRecommendationViewHolder(
+        mSearchScrollController = new SearchAndRecommendationsScrollController(
                 findViewById(R.id.search_and_recommendations_container));
-        TopRoundedCornerView.LayoutParams layoutParams =
-                (TopRoundedCornerView.LayoutParams)
-                        mSearchAndRecommendationViewHolder.mContainer.getLayoutParams();
-        layoutParams.bottomMargin = mSearchAndRecommendationContainerBottomMargin;
-        mSearchAndRecommendationViewHolder.mContainer.setLayoutParams(layoutParams);
-        mSearchAndRecommendationsScrollController = new SearchAndRecommendationsScrollController(
-                mHasWorkProfile,
-                mTabsHeight,
-                mSearchAndRecommendationViewHolder,
-                findViewById(R.id.primary_widgets_list_view),
-                mHasWorkProfile ? findViewById(R.id.work_widgets_list_view) : null,
-                findViewById(R.id.search_widgets_list_view),
-                mTabsView,
-                mViewPager,
-                mNoWidgetsView);
-        fastScroller.setOnFastScrollChangeListener(mSearchAndRecommendationsScrollController);
-
+        mSearchScrollController.setCurrentRecyclerView(
+                findViewById(R.id.primary_widgets_list_view));
+        mSearchScrollController.mRecommendedWidgetsTable.setWidgetCellLongClickListener(this);
+        mSearchScrollController.mRecommendedWidgetsTable.setWidgetCellOnClickListener(this);
 
         onRecommendedWidgetsBound();
         onWidgetsBound();
 
-        mSearchAndRecommendationViewHolder.mSearchBar.initialize(
+        mSearchScrollController.mSearchBar.initialize(
                 mActivityContext.getPopupDataProvider(), /* searchModeListener= */ this);
 
         setUpEducationViewsIfNeeded();
@@ -268,12 +238,13 @@
             reset();
             resetExpandedHeaders();
             mCurrentWidgetsRecyclerView = recyclerView;
-            mSearchAndRecommendationsScrollController.setCurrentRecyclerView(recyclerView);
+            mSearchScrollController.setCurrentRecyclerView(recyclerView);
         }
     }
 
     private void updateRecyclerViewVisibility(AdapterHolder adapterHolder) {
-        boolean isWidgetAvailable = adapterHolder.mWidgetsListAdapter.getItemCount() > 0;
+        // The first item is always an empty space entry. Look for any more items.
+        boolean isWidgetAvailable = adapterHolder.mWidgetsListAdapter.hasVisibleEntries();
         adapterHolder.mWidgetsRecyclerView.setVisibility(isWidgetAvailable ? VISIBLE : GONE);
 
         mNoWidgetsView.setText(
@@ -289,7 +260,7 @@
             mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView.scrollToTop();
         }
         mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.scrollToTop();
-        mSearchAndRecommendationsScrollController.reset(/* animate= */ true);
+        mSearchScrollController.reset(/* animate= */ true);
     }
 
     @VisibleForTesting
@@ -338,7 +309,8 @@
         if (mHasWorkProfile) {
             setBottomPadding(mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView, insets.bottom);
         }
-        mSearchAndRecommendationsScrollController.updateBottomInset(insets.bottom);
+        ((MarginLayoutParams) mNoWidgetsView.getLayoutParams()).bottomMargin = insets.bottom;
+
         if (insets.bottom > 0) {
             setupNavBarColor();
         } else {
@@ -357,17 +329,50 @@
     }
 
     @Override
+    protected void onContentHorizontalMarginChanged(int contentHorizontalMarginInPx) {
+        setContentViewChildHorizontalMargin(mSearchScrollController.mContainer,
+                contentHorizontalMarginInPx);
+        if (mViewPager == null) {
+            setContentViewChildHorizontalPadding(
+                    mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView,
+                    contentHorizontalMarginInPx);
+        } else {
+            setContentViewChildHorizontalPadding(
+                    mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView,
+                    contentHorizontalMarginInPx);
+            setContentViewChildHorizontalPadding(
+                    mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView,
+                    contentHorizontalMarginInPx);
+        }
+        setContentViewChildHorizontalPadding(
+                mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView,
+                contentHorizontalMarginInPx);
+    }
+
+    private static void setContentViewChildHorizontalMargin(View view, int horizontalMarginInPx) {
+        ViewGroup.MarginLayoutParams layoutParams =
+                (ViewGroup.MarginLayoutParams) view.getLayoutParams();
+        layoutParams.setMarginStart(horizontalMarginInPx);
+        layoutParams.setMarginEnd(horizontalMarginInPx);
+    }
+
+    private static void setContentViewChildHorizontalPadding(View view, int horizontalPaddingInPx) {
+        view.setPadding(horizontalPaddingInPx, view.getPaddingTop(), horizontalPaddingInPx,
+                view.getPaddingBottom());
+    }
+
+    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         doMeasure(widthMeasureSpec, heightMeasureSpec);
 
-        if (mSearchAndRecommendationsScrollController.updateMarginAndPadding()) {
+        if (mSearchScrollController.updateHeaderHeight()) {
             doMeasure(widthMeasureSpec, heightMeasureSpec);
         }
 
         if (updateMaxSpansPerRow()) {
             doMeasure(widthMeasureSpec, heightMeasureSpec);
 
-            if (mSearchAndRecommendationsScrollController.updateMarginAndPadding()) {
+            if (mSearchScrollController.updateHeaderHeight()) {
                 doMeasure(widthMeasureSpec, heightMeasureSpec);
             }
         }
@@ -377,11 +382,13 @@
     private boolean updateMaxSpansPerRow() {
         if (getMeasuredWidth() == 0) return false;
 
-        int previousMaxSpansPerRow = mMaxSpansPerRow;
-        mMaxSpansPerRow = getMeasuredWidth()
-                / (mActivityContext.getDeviceProfile().cellWidthPx + mWidgetCellHorizontalPadding);
-
-        if (previousMaxSpansPerRow != mMaxSpansPerRow) {
+        View content = mHasWorkProfile
+                ? mViewPager
+                : mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView;
+        int maxHorizontalSpans = computeMaxHorizontalSpans(content,
+                mWidgetSheetContentHorizontalPadding);
+        if (mMaxSpansPerRow != maxHorizontalSpans) {
+            mMaxSpansPerRow = maxHorizontalSpans;
             mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
                     mMaxSpansPerRow);
             mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
@@ -428,7 +435,7 @@
 
         if (mHasWorkProfile) {
             mViewPager.setVisibility(VISIBLE);
-            mTabsView.setVisibility(VISIBLE);
+            mSearchScrollController.mTabBar.setVisibility(VISIBLE);
             AdapterHolder workUserAdapterHolder = mAdapters.get(AdapterHolder.WORK);
             workUserAdapterHolder.mWidgetsListAdapter.setWidgets(allWidgets);
             onActivePageChanged(mViewPager.getCurrentPage());
@@ -438,9 +445,9 @@
         // Update recommended widgets section so that it occupies appropriate space on screen to
         // leave enough space for presence/absence of mNoWidgetsView.
         boolean isNoWidgetsViewNeeded =
-                mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.getItemCount() == 0
+                !mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.hasVisibleEntries()
                         || (mHasWorkProfile && mAdapters.get(AdapterHolder.WORK)
-                                .mWidgetsListAdapter.getItemCount() == 0);
+                        .mWidgetsListAdapter.hasVisibleEntries());
         if (mIsNoWidgetsViewNeeded != isNoWidgetsViewNeeded) {
             mIsNoWidgetsViewNeeded = isNoWidgetsViewNeeded;
             onRecommendedWidgetsBound();
@@ -464,8 +471,6 @@
             mViewPager.snapToPage(AdapterHolder.PRIMARY);
         }
         attachScrollbarToRecyclerView(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView);
-
-        mSearchAndRecommendationsScrollController.updateMarginAndPadding();
     }
 
     @Override
@@ -478,10 +483,10 @@
     private void setViewVisibilityBasedOnSearch(boolean isInSearchMode) {
         mIsInSearchMode = isInSearchMode;
         if (isInSearchMode) {
-            mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable.setVisibility(GONE);
+            mSearchScrollController.mRecommendedWidgetsTable.setVisibility(GONE);
             if (mHasWorkProfile) {
                 mViewPager.setVisibility(GONE);
-                mTabsView.setVisibility(GONE);
+                mSearchScrollController.mTabBar.setVisibility(GONE);
             } else {
                 mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView.setVisibility(GONE);
             }
@@ -509,8 +514,7 @@
         }
         List<WidgetItem> recommendedWidgets =
                 mActivityContext.getPopupDataProvider().getRecommendedWidgets();
-        WidgetsRecommendationTableLayout table =
-                mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable;
+        WidgetsRecommendationTableLayout table = mSearchScrollController.mRecommendedWidgetsTable;
         if (recommendedWidgets.size() > 0) {
             float noWidgetsViewHeight = 0;
             if (mIsNoWidgetsViewNeeded) {
@@ -527,12 +531,12 @@
                     makeMeasureSpec(mActivityContext.getDeviceProfile().availableHeightPx,
                             MeasureSpec.EXACTLY));
             float maxTableHeight = (mContent.getMeasuredHeight()
-                    - mTabsHeight - mViewPagerTopPadding - getHeaderViewHeight()
+                    - mTabsHeight - getHeaderViewHeight()
                     - noWidgetsViewHeight) * RECOMMENDATION_TABLE_HEIGHT_RATIO;
 
             List<ArrayList<WidgetItem>> recommendedWidgetsInTable =
-                    WidgetsTableUtils.groupWidgetItemsIntoTable(recommendedWidgets,
-                            mMaxSpansPerRow);
+                    WidgetsTableUtils.groupWidgetItemsIntoTableWithoutReordering(
+                            recommendedWidgets, mMaxSpansPerRow);
             table.setRecommendedWidgets(recommendedWidgetsInTable, maxTableHeight);
         } else {
             table.setVisibility(GONE);
@@ -590,10 +594,10 @@
                 mNoIntercept = !getRecyclerView().shouldContainerScroll(ev, getPopupContainer());
             }
 
-            if (mSearchAndRecommendationViewHolder.mSearchBar.isSearchBarFocused()
+            if (mSearchScrollController.mSearchBar.isSearchBarFocused()
                     && !getPopupContainer().isEventOverView(
-                            mSearchAndRecommendationViewHolder.mSearchBarContainer, ev)) {
-                mSearchAndRecommendationViewHolder.mSearchBar.clearSearchBarFocus();
+                    mSearchScrollController.mSearchBarContainer, ev)) {
+                mSearchScrollController.mSearchBar.clearSearchBarFocus();
             }
         }
         return super.onControllerInterceptTouchEvent(ev);
@@ -634,10 +638,8 @@
 
     @Override
     public int getHeaderViewHeight() {
-        return measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mCollapseHandle)
-                + measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mHeaderTitle)
-                + measureHeightWithVerticalMargins(
-                        (View) mSearchAndRecommendationViewHolder.mSearchBarContainer);
+        return measureHeightWithVerticalMargins(mSearchScrollController.mHeaderTitle)
+                + measureHeightWithVerticalMargins(mSearchScrollController.mSearchBarContainer);
     }
 
     /** private the height, in pixel, + the vertical margins of a given view. */
@@ -654,14 +656,14 @@
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         if (mIsInSearchMode) {
-            mSearchAndRecommendationViewHolder.mSearchBar.reset();
+            mSearchScrollController.mSearchBar.reset();
         }
     }
 
     @Override
     public boolean onBackPressed() {
         if (mIsInSearchMode) {
-            mSearchAndRecommendationViewHolder.mSearchBar.reset();
+            mSearchScrollController.mSearchBar.reset();
             return true;
         }
         return super.onBackPressed();
@@ -674,11 +676,10 @@
     }
 
     @Nullable private View getViewToShowEducationTip() {
-        if (mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable.getVisibility() == VISIBLE
-                && mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable.getChildCount() > 0
-        ) {
-            return ((ViewGroup) mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable
-                    .getChildAt(0)).getChildAt(0);
+        if (mSearchScrollController.mRecommendedWidgetsTable.getVisibility() == VISIBLE
+                && mSearchScrollController.mRecommendedWidgetsTable.getChildCount() > 0) {
+            return ((ViewGroup) mSearchScrollController.mRecommendedWidgetsTable.getChildAt(0))
+                    .getChildAt(0);
         }
 
         AdapterHolder adapterHolder = mAdapters.get(mIsInSearchMode
@@ -695,7 +696,7 @@
                         .findFirst()
                         .orElse(null);
         if (viewHolderForTip != null) {
-            return ((ViewGroup) viewHolderForTip.mTableContainer.getChildAt(0)).getChildAt(0);
+            return ((ViewGroup) viewHolderForTip.tableContainer.getChildAt(0)).getChildAt(0);
         }
 
         return null;
@@ -753,8 +754,8 @@
             mWidgetsListAdapter = new WidgetsListAdapter(
                     context,
                     LayoutInflater.from(context),
-                    apps.getWidgetCache(),
                     apps.getIconCache(),
+                    this::getEmptySpaceHeight,
                     /* iconClickListener= */ WidgetsFullSheet.this,
                     /* iconLongClickListener= */ WidgetsFullSheet.this);
             mWidgetsListAdapter.setHasStableIds(true);
@@ -774,46 +775,24 @@
             mWidgetsListItemAnimator.setSupportsChangeAnimations(false);
         }
 
+        private int getEmptySpaceHeight() {
+            return mSearchScrollController.getHeaderHeight();
+        }
+
         void setup(WidgetsRecyclerView recyclerView) {
             mWidgetsRecyclerView = recyclerView;
             mWidgetsRecyclerView.setAdapter(mWidgetsListAdapter);
             mWidgetsRecyclerView.setItemAnimator(mWidgetsListItemAnimator);
             mWidgetsRecyclerView.setHeaderViewDimensionsProvider(WidgetsFullSheet.this);
             mWidgetsRecyclerView.setEdgeEffectFactory(
-                    ((TopRoundedCornerView) mContent).createEdgeEffectFactory());
+                    ((SpringRelativeLayout) mContent).createEdgeEffectFactory());
             // Recycler view binds to fast scroller when it is attached to screen. Make sure
             // search recycler view is bound to fast scroller if user is in search mode at the time
             // of attachment.
             if (mAdapterType == PRIMARY || mAdapterType == WORK) {
                 mWidgetsRecyclerView.addOnAttachStateChangeListener(mBindScrollbarInSearchMode);
             }
-            mWidgetsListAdapter.setApplyBitmapDeferred(false, mWidgetsRecyclerView);
             mWidgetsListAdapter.setMaxHorizontalSpansPerRow(mMaxSpansPerRow);
         }
     }
-
-    final class SearchAndRecommendationViewHolder {
-        final SearchAndRecommendationsView mContainer;
-        final View mCollapseHandle;
-        final View mSearchBarContainer;
-        final WidgetsSearchBar mSearchBar;
-        final TextView mHeaderTitle;
-        final WidgetsRecommendationTableLayout mRecommendedWidgetsTable;
-
-        SearchAndRecommendationViewHolder(
-                SearchAndRecommendationsView searchAndRecommendationContainer) {
-            mContainer = searchAndRecommendationContainer;
-            mCollapseHandle = mContainer.findViewById(R.id.collapse_handle);
-            mSearchBarContainer = mContainer.findViewById(R.id.search_bar_container);
-            mSearchBar = mContainer.findViewById(R.id.widgets_search_bar);
-            mHeaderTitle = mContainer.findViewById(R.id.title);
-            mRecommendedWidgetsTable = mContainer.findViewById(R.id.recommended_widget_table);
-            mRecommendedWidgetsTable.setWidgetCellOnTouchListener((view, event) -> {
-                getRecyclerView().onTouchEvent(event);
-                return false;
-            });
-            mRecommendedWidgetsTable.setWidgetCellLongClickListener(WidgetsFullSheet.this);
-            mRecommendedWidgetsTable.setWidgetCellOnClickListener(WidgetsFullSheet.this);
-        }
-    }
 }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
index 1125b82..0e5a7d7 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -16,19 +16,20 @@
 package com.android.launcher3.widget.picker;
 
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_APP_EXPANDED;
+import static com.android.launcher3.recyclerview.ViewHolderBinder.POSITION_DEFAULT;
+import static com.android.launcher3.recyclerview.ViewHolderBinder.POSITION_FIRST;
+import static com.android.launcher3.recyclerview.ViewHolderBinder.POSITION_LAST;
 
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Process;
 import android.util.Log;
-import android.util.Size;
 import android.util.SparseArray;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
-import android.widget.TableRow;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -38,32 +39,27 @@
 import androidx.recyclerview.widget.RecyclerView.LayoutParams;
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.recyclerview.ViewHolderBinder;
 import com.android.launcher3.util.LabelComparator;
 import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.widget.CachingWidgetPreviewLoader;
-import com.android.launcher3.widget.DatabaseWidgetPreviewLoader;
-import com.android.launcher3.widget.WidgetCell;
-import com.android.launcher3.widget.WidgetPreviewLoader.WidgetPreviewLoadedCallback;
+import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.widget.model.WidgetListSpaceEntry;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 import com.android.launcher3.widget.model.WidgetsListContentEntry;
 import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
 import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
-import com.android.launcher3.widget.util.WidgetSizes;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.OptionalInt;
+import java.util.function.IntSupplier;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
@@ -84,67 +80,59 @@
     private static final boolean DEBUG = false;
 
     /** Uniquely identifies widgets list view type within the app. */
+    private static final int VIEW_TYPE_WIDGETS_SPACE = R.id.view_type_widgets_space;
     private static final int VIEW_TYPE_WIDGETS_LIST = R.id.view_type_widgets_list;
     private static final int VIEW_TYPE_WIDGETS_HEADER = R.id.view_type_widgets_header;
     private static final int VIEW_TYPE_WIDGETS_SEARCH_HEADER = R.id.view_type_widgets_search_header;
 
     private final Context mContext;
-    private final Launcher mLauncher;
-    private final CachingWidgetPreviewLoader mCachingPreviewLoader;
     private final WidgetsDiffReporter mDiffReporter;
     private final SparseArray<ViewHolderBinder> mViewHolderBinders = new SparseArray<>();
-    private final WidgetsListTableViewHolderBinder mWidgetsListTableViewHolderBinder;
     private final WidgetListBaseRowEntryComparator mRowComparator =
             new WidgetListBaseRowEntryComparator();
 
-    private List<WidgetsListBaseEntry> mAllEntries = new ArrayList<>();
+    private final List<WidgetsListBaseEntry> mAllEntries = new ArrayList<>();
     private ArrayList<WidgetsListBaseEntry> mVisibleEntries = new ArrayList<>();
     @Nullable private PackageUserKey mWidgetsContentVisiblePackageUserKey = null;
 
     private Predicate<WidgetsListBaseEntry> mHeaderAndSelectedContentFilter = entry ->
             entry instanceof WidgetsListHeaderEntry
                     || entry instanceof WidgetsListSearchHeaderEntry
-                    || new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user)
+                    || PackageUserKey.fromPackageItemInfo(entry.mPkgItem)
                             .equals(mWidgetsContentVisiblePackageUserKey);
     @Nullable private Predicate<WidgetsListBaseEntry> mFilter = null;
     @Nullable private RecyclerView mRecyclerView;
     @Nullable private PackageUserKey mPendingClickHeader;
-    private final int mShortcutPreviewPadding;
     private final int mSpacingBetweenEntries;
     private int mMaxSpanSize = 4;
 
-    private final WidgetPreviewLoadedCallback mPreviewLoadedCallback =
-            ignored -> updateVisibleEntries();
-
     public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
-            DatabaseWidgetPreviewLoader widgetPreviewLoader, IconCache iconCache,
+            IconCache iconCache, IntSupplier emptySpaceHeightProvider,
             OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) {
         mContext = context;
-        mLauncher = Launcher.getLauncher(context);
-        mCachingPreviewLoader = new CachingWidgetPreviewLoader(widgetPreviewLoader);
         mDiffReporter = new WidgetsDiffReporter(iconCache, this);
         WidgetsListDrawableFactory listDrawableFactory = new WidgetsListDrawableFactory(context);
-        mWidgetsListTableViewHolderBinder = new WidgetsListTableViewHolderBinder(
-                layoutInflater, iconClickListener, iconLongClickListener,
-                mCachingPreviewLoader, listDrawableFactory, /* listAdapter= */ this);
-        mViewHolderBinders.put(VIEW_TYPE_WIDGETS_LIST, mWidgetsListTableViewHolderBinder);
+
+        mViewHolderBinders.put(
+                VIEW_TYPE_WIDGETS_LIST,
+                new WidgetsListTableViewHolderBinder(
+                        layoutInflater, iconClickListener, iconLongClickListener,
+                        listDrawableFactory));
         mViewHolderBinders.put(
                 VIEW_TYPE_WIDGETS_HEADER,
                 new WidgetsListHeaderViewHolderBinder(
                         layoutInflater,
                         /* onHeaderClickListener= */ this,
-                        listDrawableFactory,
-                        /* listAdapter= */ this));
+                        listDrawableFactory));
         mViewHolderBinders.put(
                 VIEW_TYPE_WIDGETS_SEARCH_HEADER,
                 new WidgetsListSearchHeaderViewHolderBinder(
                         layoutInflater,
                         /* onHeaderClickListener= */ this,
-                        listDrawableFactory,
-                        /* listAdapter= */ this));
-        mShortcutPreviewPadding =
-                2 * context.getResources()
-                        .getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
+                        listDrawableFactory));
+        mViewHolderBinders.put(
+                VIEW_TYPE_WIDGETS_SPACE,
+                new WidgetsSpaceViewHolderBinder(emptySpaceHeightProvider));
         mSpacingBetweenEntries =
                 context.getResources().getDimensionPixelSize(R.dimen.widget_list_entry_spacing);
     }
@@ -178,33 +166,19 @@
         mFilter = filter;
     }
 
-    /**
-     * Defers applying bitmap on all the {@link WidgetCell} in the {@param rv}.
-     *
-     * @see WidgetCell#setApplyBitmapDeferred(boolean)
-     */
-    public void setApplyBitmapDeferred(boolean isDeferred, RecyclerView rv) {
-        mWidgetsListTableViewHolderBinder.setApplyBitmapDeferred(isDeferred);
-
-        for (int i = rv.getChildCount() - 1; i >= 0; i--) {
-            ViewHolder viewHolder = rv.getChildViewHolder(rv.getChildAt(i));
-            if (viewHolder.getItemViewType() == VIEW_TYPE_WIDGETS_LIST) {
-                WidgetsRowViewHolder holder = (WidgetsRowViewHolder) viewHolder;
-                for (int j = holder.mTableContainer.getChildCount() - 1; j >= 0; j--) {
-                    TableRow row =  (TableRow) holder.mTableContainer.getChildAt(j);
-                    for (int k = row.getChildCount() - 1; k >= 0; k--) {
-                        ((WidgetCell) row.getChildAt(k)).setApplyBitmapDeferred(isDeferred);
-                    }
-                }
-            }
-        }
-    }
-
     @Override
     public int getItemCount() {
         return mVisibleEntries.size();
     }
 
+    /**
+     * Returns true if the adapter has entries which will be visible to the user
+     */
+    public boolean hasVisibleEntries() {
+        // Account for the 1st space entry
+        return getItemCount() > 1;
+    }
+
     /** Returns all items that will be drawn in a recycler view. */
     public List<WidgetsListBaseEntry> getItems() {
         return mVisibleEntries;
@@ -217,9 +191,9 @@
 
     /** Updates the widget list based on {@code tempEntries}. */
     public void setWidgets(List<WidgetsListBaseEntry> tempEntries) {
-        mCachingPreviewLoader.clearAll();
-        mAllEntries = tempEntries.stream().sorted(mRowComparator)
-                .collect(Collectors.toList());
+        mAllEntries.clear();
+        mAllEntries.add(new WidgetListSpaceEntry());
+        tempEntries.stream().sorted(mRowComparator).forEach(mAllEntries::add);
         if (shouldClearVisibleEntries()) {
             mVisibleEntries.clear();
         }
@@ -230,15 +204,10 @@
     public void setWidgetsOnSearch(List<WidgetsListBaseEntry> searchResults) {
         // Forget the expanded package every time widget list is refreshed in search mode.
         mWidgetsContentVisiblePackageUserKey = null;
-        cancelLoadingPreviews();
         setWidgets(searchResults);
     }
 
     private void updateVisibleEntries() {
-        // If not all previews are ready, then defer this update and try again after the preview
-        // loads.
-        if (!ensureAllPreviewsReady()) return;
-
         // Get the current top of the header with the matching key before adjusting the visible
         // entries.
         OptionalInt previousPositionForPackageUserKey =
@@ -247,8 +216,9 @@
                 getOffsetForPosition(previousPositionForPackageUserKey);
 
         List<WidgetsListBaseEntry> newVisibleEntries = mAllEntries.stream()
-                .filter(entry -> (mFilter == null || mFilter.test(entry))
+                .filter(entry -> ((mFilter == null || mFilter.test(entry))
                         && mHeaderAndSelectedContentFilter.test(entry))
+                        || entry instanceof WidgetListSpaceEntry)
                 .map(entry -> {
                     if (entry instanceof WidgetsListBaseEntry.Header<?>
                             && matchesKey(entry, mWidgetsContentVisiblePackageUserKey)) {
@@ -275,54 +245,6 @@
         }
     }
 
-    /**
-     * Checks that all preview images are loaded and starts loading for those that aren't ready.
-     *
-     * @return true if all previews are ready and the data can be updated, false otherwise.
-     */
-    private boolean ensureAllPreviewsReady() {
-        boolean allReady = true;
-        BaseActivity activity = BaseActivity.fromContext(mContext);
-        for (WidgetsListBaseEntry entry : mAllEntries) {
-            if (!(entry instanceof WidgetsListContentEntry)) continue;
-
-            WidgetsListContentEntry contentEntry = (WidgetsListContentEntry) entry;
-            if (!matchesKey(entry, mWidgetsContentVisiblePackageUserKey)) {
-                // If the entry isn't visible, clear any loaded previews.
-                mCachingPreviewLoader.clearPreviews(contentEntry.mWidgets);
-                continue;
-            }
-
-            for (int i = 0; i < entry.mWidgets.size(); i++) {
-                WidgetItem widgetItem = entry.mWidgets.get(i);
-                DeviceProfile deviceProfile = activity.getDeviceProfile();
-                Size widgetSize = WidgetSizes.getWidgetItemSizePx(mContext, deviceProfile,
-                        widgetItem);
-                if (widgetItem.isShortcut()) {
-                    widgetSize =
-                            new Size(
-                                    widgetSize.getWidth() + mShortcutPreviewPadding,
-                                    widgetSize.getHeight() + mShortcutPreviewPadding);
-                }
-
-                if (widgetItem.hasPreviewLayout()
-                        || mCachingPreviewLoader.isPreviewLoaded(widgetItem, widgetSize)) {
-                    // The widget is ready if it can be rendered with a preview layout or if its
-                    // preview bitmap is in the cache.
-                    continue;
-                }
-
-                // If we've reached this point, we should load the preview for the widget.
-                allReady = false;
-                mCachingPreviewLoader.loadPreview(
-                        activity,
-                        widgetItem,
-                        widgetSize,
-                        mPreviewLoadedCallback);
-            }
-        }
-        return allReady;
-    }
 
     /** Returns whether {@code entry} matches {@code key}. */
     private static boolean isHeaderForPackageUserKey(
@@ -330,10 +252,11 @@
         return entry instanceof WidgetsListBaseEntry.Header && matchesKey(entry, key);
     }
 
-    private static boolean matchesKey(
-            @NonNull WidgetsListBaseEntry entry, @Nullable PackageUserKey key) {
+    private static boolean matchesKey(@NonNull WidgetsListBaseEntry entry,
+            @Nullable PackageUserKey key) {
         if (key == null) return false;
         return entry.mPkgItem.packageName.equals(key.mPackageName)
+                && entry.mPkgItem.widgetCategory == key.mWidgetCategory
                 && entry.mPkgItem.user.equals(key.mUser);
     }
 
@@ -343,16 +266,26 @@
     public void resetExpandedHeader() {
         if (mWidgetsContentVisiblePackageUserKey != null) {
             mWidgetsContentVisiblePackageUserKey = null;
-            cancelLoadingPreviews();
             updateVisibleEntries();
         }
     }
 
     @Override
-    public void onBindViewHolder(ViewHolder holder, int pos) {
+    public void onBindViewHolder(ViewHolder holder, int position) {
+        onBindViewHolder(holder, position, Collections.EMPTY_LIST);
+    }
+
+    @Override
+    public void onBindViewHolder(ViewHolder holder, int pos, List<Object> payloads) {
         ViewHolderBinder viewHolderBinder = mViewHolderBinders.get(getItemViewType(pos));
         WidgetsListBaseEntry entry = mVisibleEntries.get(pos);
-        viewHolderBinder.bindViewHolder(holder, mVisibleEntries.get(pos), pos);
+
+        // The first entry has an empty space, count from second entries.
+        int listPos = (pos > 1) ? POSITION_DEFAULT : POSITION_FIRST;
+        if (pos == (getItemCount() - 1)) {
+            listPos |= POSITION_LAST;
+        }
+        viewHolderBinder.bindViewHolder(holder, mVisibleEntries.get(pos), listPos, payloads);
         holder.itemView.setTag(R.id.tag_widget_entry, entry);
     }
 
@@ -395,6 +328,8 @@
             return VIEW_TYPE_WIDGETS_HEADER;
         } else if (entry instanceof WidgetsListSearchHeaderEntry) {
             return VIEW_TYPE_WIDGETS_SEARCH_HEADER;
+        } else if (entry instanceof WidgetListSpaceEntry) {
+            return VIEW_TYPE_WIDGETS_SPACE;
         }
         throw new UnsupportedOperationException("ViewHolderBinder not found for " + entry);
     }
@@ -404,11 +339,10 @@
         // Ignore invalid clicks, such as collapsing a package that isn't currently expanded.
         if (!showWidgets && !packageUserKey.equals(mWidgetsContentVisiblePackageUserKey)) return;
 
-        cancelLoadingPreviews();
-
         if (showWidgets) {
             mWidgetsContentVisiblePackageUserKey = packageUserKey;
-            mLauncher.getStatsLogManager().logger().log(LAUNCHER_WIDGETSTRAY_APP_EXPANDED);
+            ActivityContext.lookupContext(mContext)
+                    .getStatsLogManager().logger().log(LAUNCHER_WIDGETSTRAY_APP_EXPANDED);
         } else {
             mWidgetsContentVisiblePackageUserKey = null;
         }
@@ -420,16 +354,6 @@
         updateVisibleEntries();
     }
 
-    private void cancelLoadingPreviews() {
-        mCachingPreviewLoader.clearAll();
-    }
-
-    /** Returns the position of the currently expanded header, or empty if it's not present. */
-    public OptionalInt getSelectedHeaderPosition() {
-        if (mWidgetsContentVisiblePackageUserKey == null) return OptionalInt.empty();
-        return getPositionForPackageUserKey(mWidgetsContentVisiblePackageUserKey);
-    }
-
     /**
      * Returns the position of {@code key} in {@link #mVisibleEntries}, or  empty if it's not
      * present.
@@ -511,11 +435,10 @@
                         .filter(entry -> entry instanceof WidgetsListHeaderEntry)
                         .map(entry -> entry.mPkgItem)
                         .collect(Collectors.toMap(
-                                entry -> new PackageUserKey(entry.packageName, entry.user),
+                                entry -> PackageUserKey.fromPackageItemInfo(entry),
                                 entry -> entry));
         for (WidgetsListBaseEntry visibleEntry: mVisibleEntries) {
-            PackageUserKey key = new PackageUserKey(visibleEntry.mPkgItem.packageName,
-                    visibleEntry.mPkgItem.user);
+            PackageUserKey key = PackageUserKey.fromPackageItemInfo(visibleEntry.mPkgItem);
             PackageItemInfo packageItemInfo = packagesInfo.get(key);
             if (packageItemInfo != null
                     && !visibleEntry.mPkgItem.title.equals(packageItemInfo.title)) {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
index ef2adbb..932e06d 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.widget.picker;
 
+import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -39,7 +41,10 @@
 import com.android.launcher3.icons.cache.HandlerRunnable;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.util.PluralMessageFormat;
 import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.widget.WidgetSections;
+import com.android.launcher3.widget.WidgetSections.WidgetSection;
 import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
 import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
 
@@ -172,12 +177,12 @@
 
     private void setIcon(PackageItemInfo info) {
         Drawable icon;
-        switch (info.category) {
-            case PackageItemInfo.CONVERSATIONS:
-                icon = getContext().getDrawable(R.drawable.ic_conversations_widget_category);
-                break;
-            default:
-                icon = info.newIcon(getContext());
+        if (info.widgetCategory == NO_CATEGORY) {
+            icon = info.newIcon(getContext());
+        } else {
+            WidgetSection widgetSection = WidgetSections.getWidgetSections(getContext())
+                    .get(info.widgetCategory);
+            icon = getContext().getDrawable(widgetSection.mSectionDrawable);
         }
         applyDrawables(icon);
         mIconDrawable = icon;
@@ -217,18 +222,18 @@
 
         String subtitle;
         if (entry.widgetsCount > 0 && entry.shortcutsCount > 0) {
-            String widgetsCount = resources.getQuantityString(R.plurals.widgets_count,
-                    entry.widgetsCount, entry.widgetsCount);
-            String shortcutsCount = resources.getQuantityString(R.plurals.shortcuts_count,
-                    entry.shortcutsCount, entry.shortcutsCount);
+            String widgetsCount = PluralMessageFormat.getIcuPluralString(getContext(),
+                    R.string.widgets_count, entry.widgetsCount);
+            String shortcutsCount = PluralMessageFormat.getIcuPluralString(getContext(),
+                    R.string.shortcuts_count, entry.shortcutsCount);
             subtitle = resources.getString(R.string.widgets_and_shortcuts_count, widgetsCount,
                     shortcutsCount);
         } else if (entry.widgetsCount > 0) {
-            subtitle = resources.getQuantityString(R.plurals.widgets_count,
-                    entry.widgetsCount, entry.widgetsCount);
+            subtitle = PluralMessageFormat.getIcuPluralString(getContext(),
+                    R.string.widgets_count, entry.widgetsCount);
         } else {
-            subtitle = resources.getQuantityString(R.plurals.shortcuts_count,
-                    entry.shortcutsCount, entry.shortcutsCount);
+            subtitle = PluralMessageFormat.getIcuPluralString(getContext(),
+                    R.string.shortcuts_count, entry.shortcutsCount);
         }
         mSubtitle.setText(subtitle);
         mSubtitle.setVisibility(VISIBLE);
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
index 2f8f1ba..c6a7285 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
@@ -23,6 +23,8 @@
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
 
+import java.util.List;
+
 /**
  * Binds data from {@link WidgetsListHeaderEntry} to UI elements in {@link WidgetsListHeaderHolder}.
  */
@@ -31,16 +33,13 @@
     private final LayoutInflater mLayoutInflater;
     private final OnHeaderClickListener mOnHeaderClickListener;
     private final WidgetsListDrawableFactory mListDrawableFactory;
-    private final WidgetsListAdapter mWidgetsListAdapter;
 
     public WidgetsListHeaderViewHolderBinder(LayoutInflater layoutInflater,
             OnHeaderClickListener onHeaderClickListener,
-            WidgetsListDrawableFactory listDrawableFactory,
-            WidgetsListAdapter listAdapter) {
+            WidgetsListDrawableFactory listDrawableFactory) {
         mLayoutInflater = layoutInflater;
         mOnHeaderClickListener = onHeaderClickListener;
         mListDrawableFactory = listDrawableFactory;
-        mWidgetsListAdapter = listAdapter;
     }
 
     @Override
@@ -53,19 +52,17 @@
 
     @Override
     public void bindViewHolder(WidgetsListHeaderHolder viewHolder, WidgetsListHeaderEntry data,
-            int position) {
+            @ListPosition int position, List<Object> payloads) {
         WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
         widgetsListHeader.applyFromItemInfoWithIcon(data);
         widgetsListHeader.setExpanded(data.isWidgetListShown());
         widgetsListHeader.setListDrawableState(
                 WidgetsListDrawableState.obtain(
-                        /* isFirst= */ position == 0,
-                        /* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1,
+                        (position & POSITION_FIRST) != 0,
+                        (position & POSITION_LAST) != 0,
                         /* isExpanded= */ data.isWidgetListShown()));
         widgetsListHeader.setOnExpandChangeListener(isExpanded ->
-                mOnHeaderClickListener.onHeaderClicked(
-                        isExpanded,
-                        new PackageUserKey(data.mPkgItem.packageName, data.mPkgItem.user)
-                ));
+                mOnHeaderClickListener.onHeaderClicked(isExpanded,
+                        PackageUserKey.fromPackageItemInfo(data.mPkgItem)));
     }
 }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListLayoutManager.java b/src/com/android/launcher3/widget/picker/WidgetsListLayoutManager.java
deleted file mode 100644
index 2b7f544..0000000
--- a/src/com/android/launcher3/widget/picker/WidgetsListLayoutManager.java
+++ /dev/null
@@ -1,51 +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.widget.picker;
-
-import android.content.Context;
-
-import androidx.annotation.Nullable;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.launcher3.widget.picker.SearchAndRecommendationsScrollController.OnContentChangeListener;
-
-/**
- * A layout manager for the {@link WidgetsRecyclerView}.
- *
- * {@link #setOnContentChangeListener(OnContentChangeListener)} can be used to register a callback
- * for when the content of the layout manager has changed, following measurement and animation.
- */
-public final class WidgetsListLayoutManager extends LinearLayoutManager {
-    @Nullable
-    private OnContentChangeListener mOnContentChangeListener;
-
-    public WidgetsListLayoutManager(Context context) {
-        super(context);
-    }
-
-    @Override
-    public void onLayoutCompleted(RecyclerView.State state) {
-        super.onLayoutCompleted(state);
-        if (mOnContentChangeListener != null) {
-            mOnContentChangeListener.onContentChanged();
-        }
-    }
-
-    public void setOnContentChangeListener(@Nullable OnContentChangeListener listener) {
-        mOnContentChangeListener = listener;
-    }
-}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
index 31dd9ee..2b27fc2 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
@@ -24,6 +24,8 @@
 import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
 import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
 
+import java.util.List;
+
 /**
  * Binds data from {@link WidgetsListHeaderEntry} to UI elements in {@link WidgetsListHeaderHolder}.
  */
@@ -32,16 +34,13 @@
     private final LayoutInflater mLayoutInflater;
     private final OnHeaderClickListener mOnHeaderClickListener;
     private final WidgetsListDrawableFactory mListDrawableFactory;
-    private final WidgetsListAdapter mWidgetsListAdapter;
 
     public WidgetsListSearchHeaderViewHolderBinder(LayoutInflater layoutInflater,
             OnHeaderClickListener onHeaderClickListener,
-            WidgetsListDrawableFactory listDrawableFactory,
-            WidgetsListAdapter listAdapter) {
+            WidgetsListDrawableFactory listDrawableFactory) {
         mLayoutInflater = layoutInflater;
         mOnHeaderClickListener = onHeaderClickListener;
         mListDrawableFactory = listDrawableFactory;
-        mWidgetsListAdapter = listAdapter;
     }
 
     @Override
@@ -54,17 +53,17 @@
 
     @Override
     public void bindViewHolder(WidgetsListSearchHeaderHolder viewHolder,
-            WidgetsListSearchHeaderEntry data, int position) {
+            WidgetsListSearchHeaderEntry data, @ListPosition int position, List<Object> payloads) {
         WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
         widgetsListHeader.applyFromItemInfoWithIcon(data);
         widgetsListHeader.setExpanded(data.isWidgetListShown());
         widgetsListHeader.setListDrawableState(
                 WidgetsListDrawableState.obtain(
-                        /* isFirst= */ position == 0,
-                        /* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1,
+                        (position & POSITION_FIRST) != 0,
+                        (position & POSITION_LAST) != 0,
                         /* isExpanded= */ data.isWidgetListShown()));
         widgetsListHeader.setOnExpandChangeListener(isExpanded ->
                 mOnHeaderClickListener.onHeaderClicked(isExpanded,
-                        new PackageUserKey(data.mPkgItem.packageName, data.mPkgItem.user)));
+                        PackageUserKey.fromPackageItemInfo(data.mPkgItem)));
     }
 }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index 9c06558..05e26ad 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -20,7 +20,7 @@
 
 import android.graphics.Bitmap;
 import android.util.Log;
-import android.util.Size;
+import android.util.Pair;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -33,7 +33,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.recyclerview.ViewHolderBinder;
-import com.android.launcher3.widget.CachingWidgetPreviewLoader;
 import com.android.launcher3.widget.WidgetCell;
 import com.android.launcher3.widget.model.WidgetsListContentEntry;
 import com.android.launcher3.widget.util.WidgetsTableUtils;
@@ -53,32 +52,16 @@
     private final OnClickListener mIconClickListener;
     private final OnLongClickListener mIconLongClickListener;
     private final WidgetsListDrawableFactory mListDrawableFactory;
-    private final CachingWidgetPreviewLoader mWidgetPreviewLoader;
-    private final WidgetsListAdapter mWidgetsListAdapter;
-    private boolean mApplyBitmapDeferred = false;
 
     public WidgetsListTableViewHolderBinder(
             LayoutInflater layoutInflater,
             OnClickListener iconClickListener,
             OnLongClickListener iconLongClickListener,
-            CachingWidgetPreviewLoader widgetPreviewLoader,
-            WidgetsListDrawableFactory listDrawableFactory,
-            WidgetsListAdapter listAdapter) {
+            WidgetsListDrawableFactory listDrawableFactory) {
         mLayoutInflater = layoutInflater;
         mIconClickListener = iconClickListener;
         mIconLongClickListener = iconLongClickListener;
-        mWidgetPreviewLoader = widgetPreviewLoader;
         mListDrawableFactory = listDrawableFactory;
-        mWidgetsListAdapter = listAdapter;
-    }
-
-    /**
-     * Defers applying bitmap on all the {@link WidgetCell} at
-     * {@link #bindViewHolder(WidgetsRowViewHolder, WidgetsListContentEntry, int)} if
-     * {@code applyBitmapDeferred} is {@code true}.
-     */
-    public void setApplyBitmapDeferred(boolean applyBitmapDeferred) {
-        mApplyBitmapDeferred = applyBitmapDeferred;
     }
 
     @Override
@@ -90,27 +73,30 @@
         WidgetsRowViewHolder viewHolder =
                 new WidgetsRowViewHolder(mLayoutInflater.inflate(
                         R.layout.widgets_table_container, parent, false));
-        viewHolder.mTableContainer.setBackgroundDrawable(
+        viewHolder.tableContainer.setBackgroundDrawable(
                 mListDrawableFactory.createContentBackgroundDrawable());
         return viewHolder;
     }
 
     @Override
     public void bindViewHolder(WidgetsRowViewHolder holder, WidgetsListContentEntry entry,
-            int position) {
-        WidgetsListTableView table = holder.mTableContainer;
+            @ListPosition int position, List<Object> payloads) {
+        for (Object payload : payloads) {
+            Pair<WidgetItem, Bitmap> pair = (Pair) payload;
+            holder.previewCache.put(pair.first, pair.second);
+        }
+
+        WidgetsListTableView table = holder.tableContainer;
         if (DEBUG) {
             Log.d(TAG, String.format("onBindViewHolder [widget#=%d, table.getChildCount=%d]",
                     entry.mWidgets.size(), table.getChildCount()));
         }
-
-        table.setListDrawableState(
-                position == mWidgetsListAdapter.getItemCount() - 1 ? LAST : MIDDLE);
-
+        table.setListDrawableState(((position & POSITION_LAST) != 0) ? LAST : MIDDLE);
         List<ArrayList<WidgetItem>> widgetItemsTable =
-                WidgetsTableUtils.groupWidgetItemsIntoTable(
+                WidgetsTableUtils.groupWidgetItemsIntoTableWithReordering(
                         entry.mWidgets, entry.getMaxSpanSizeInCells());
         recycleTableBeforeBinding(table, widgetItemsTable);
+
         // Bind the widget items.
         for (int i = 0; i < widgetItemsTable.size(); i++) {
             List<WidgetItem> widgetItemsPerRow = widgetItemsTable.get(i);
@@ -120,16 +106,17 @@
                 WidgetCell widget = (WidgetCell) row.getChildAt(j);
                 widget.clear();
                 WidgetItem widgetItem = widgetItemsPerRow.get(j);
-                Size previewSize = widget.setPreviewSize(widgetItem);
-                widget.applyFromCellItem(widgetItem, mWidgetPreviewLoader);
-                widget.setApplyBitmapDeferred(mApplyBitmapDeferred);
-                Bitmap preview = mWidgetPreviewLoader.getPreview(widgetItem, previewSize);
-                if (preview == null) {
-                    widget.ensurePreview();
-                } else {
-                    widget.applyPreview(preview);
-                }
                 widget.setVisibility(View.VISIBLE);
+
+                // When preview loads, notify adapter to rebind the item and possibly animate
+                widget.applyFromCellItem(widgetItem, 1f,
+                        bitmap -> {
+                        if (holder.getBindingAdapter() != null) {
+                            holder.getBindingAdapter().notifyItemChanged(
+                                    holder.getBindingAdapterPosition(),
+                                    Pair.create(widgetItem, bitmap));
+                            }
+                        }, holder.previewCache.get(widgetItem));
             }
         }
     }
@@ -170,6 +157,7 @@
                     View preview = widget.findViewById(R.id.widget_preview_container);
                     preview.setOnClickListener(mIconClickListener);
                     preview.setOnLongClickListener(mIconLongClickListener);
+                    widget.setAnimatePreview(false);
                     tableRow.addView(widget);
                 }
             }
@@ -178,9 +166,10 @@
 
     @Override
     public void unbindViewHolder(WidgetsRowViewHolder holder) {
-        int numOfRows = holder.mTableContainer.getChildCount();
+        int numOfRows = holder.tableContainer.getChildCount();
+        holder.previewCache.clear();
         for (int i = 0; i < numOfRows; i++) {
-            TableRow tableRow = (TableRow) holder.mTableContainer.getChildAt(i);
+            TableRow tableRow = (TableRow) holder.tableContainer.getChildAt(i);
             int numOfCols = tableRow.getChildCount();
             for (int j = 0; j < numOfCols; j++) {
                 WidgetCell widget = (WidgetCell) tableRow.getChildAt(j);
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
index 0b8ca34..c986007 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
@@ -32,7 +32,6 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.widget.WidgetCell;
@@ -53,7 +52,6 @@
     private float mRecommendationTableMaxHeight = Float.MAX_VALUE;
     @Nullable private OnLongClickListener mWidgetCellOnLongClickListener;
     @Nullable private OnClickListener mWidgetCellOnClickListener;
-    @Nullable private OnTouchListener mWidgetCellOnTouchListener;
 
     public WidgetsRecommendationTableLayout(Context context) {
         this(context, /* attrs= */ null);
@@ -79,11 +77,6 @@
         mWidgetCellOnClickListener = widgetCellOnClickListener;
     }
 
-    /** Sets a {@link android.view.View.OnTouchListener} for all widget cells in this table. */
-    public void setWidgetCellOnTouchListener(OnTouchListener widgetCellOnTouchListener) {
-        mWidgetCellOnTouchListener = widgetCellOnTouchListener;
-    }
-
     /**
      * Sets a list of recommended widgets that would like to be displayed in this table within the
      * desired {@code recommendationTableMaxHeight}.
@@ -115,10 +108,7 @@
 
             for (WidgetItem widgetItem : widgetItems) {
                 WidgetCell widgetCell = addItemCell(tableRow);
-                widgetCell.setPreviewSize(widgetItem, data.mPreviewScale);
-                widgetCell.applyFromCellItem(widgetItem,
-                        LauncherAppState.getInstance(getContext()).getWidgetCache());
-                widgetCell.ensurePreview();
+                widgetCell.applyFromCellItem(widgetItem, data.mPreviewScale);
             }
             addView(tableRow);
         }
@@ -129,7 +119,6 @@
         WidgetCell widget = (WidgetCell) LayoutInflater.from(
                 getContext()).inflate(R.layout.widget_cell, parent, false);
 
-        widget.setOnTouchListener(mWidgetCellOnTouchListener);
         View previewContainer = widget.findViewById(R.id.widget_preview_container);
         previewContainer.setOnClickListener(mWidgetCellOnClickListener);
         previewContainer.setOnLongClickListener(mWidgetCellOnLongClickListener);
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
index 7671841..f780f03 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
@@ -23,7 +23,6 @@
 import android.view.View;
 import android.widget.TableLayout;
 
-import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener;
@@ -32,11 +31,12 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.widget.model.WidgetListSpaceEntry;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 import com.android.launcher3.widget.model.WidgetsListContentEntry;
 import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
 import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
-import com.android.launcher3.widget.picker.SearchAndRecommendationsScrollController.OnContentChangeListener;
+import com.android.launcher3.widget.picker.WidgetsSpaceViewHolderBinder.EmptySpaceView;
 
 /**
  * The widgets recycler view.
@@ -50,10 +50,13 @@
     private final Point mFastScrollerOffset = new Point();
     private boolean mTouchDownOnScroller;
     private HeaderViewDimensionsProvider mHeaderViewDimensionsProvider;
+
+    // Cached sizes
     private int mLastVisibleWidgetContentTableHeight = 0;
     private int mWidgetHeaderHeight = 0;
+    private int mWidgetEmptySpaceHeight = 0;
+
     private final int mSpacingBetweenEntries;
-    @Nullable private OnContentChangeListener mOnContentChangeListener;
 
     public WidgetsRecyclerView(Context context) {
         this(context, null);
@@ -82,9 +85,7 @@
         super.onFinishInflate();
         // create a layout manager with Launcher's context so that scroll position
         // can be preserved during screen rotation.
-        WidgetsListLayoutManager layoutManager = new WidgetsListLayoutManager(getContext());
-        layoutManager.setOnContentChangeListener(mOnContentChangeListener);
-        setLayoutManager(layoutManager);
+        setLayoutManager(new LinearLayoutManager(getContext()));
     }
 
     @Override
@@ -169,10 +170,12 @@
                 // This assumes there is ever only one content shown in this recycler view.
                 mLastVisibleWidgetContentTableHeight = view.getMeasuredHeight();
             } else if (view instanceof WidgetsListHeader
-                    && mLastVisibleWidgetContentTableHeight == 0
+                    && mWidgetHeaderHeight == 0
                     && view.getMeasuredHeight() > 0) {
                 // This assumes all header views are of the same height.
                 mWidgetHeaderHeight = view.getMeasuredHeight();
+            } else if (view instanceof EmptySpaceView && view.getMeasuredHeight() > 0) {
+                mWidgetEmptySpaceHeight = view.getMeasuredHeight();
             }
         }
 
@@ -251,14 +254,6 @@
         scrollToPosition(0);
     }
 
-    public void setOnContentChangeListener(@Nullable OnContentChangeListener listener) {
-        mOnContentChangeListener = listener;
-        WidgetsListLayoutManager layoutManager = (WidgetsListLayoutManager) getLayoutManager();
-        if (layoutManager != null) {
-            layoutManager.setOnContentChangeListener(listener);
-        }
-    }
-
     /**
      * Returns the sum of the height, in pixels, of this list adapter's items from index 0 until
      * {@code untilIndex}.
@@ -283,6 +278,8 @@
                 }
             } else if (entry instanceof WidgetsListContentEntry) {
                 totalItemsHeight += mLastVisibleWidgetContentTableHeight;
+            } else if (entry instanceof WidgetListSpaceEntry) {
+                totalItemsHeight += mWidgetEmptySpaceHeight;
             } else {
                 throw new UnsupportedOperationException("Can't estimate height for " + entry);
             }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java b/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
index 618e2cb..fe2d84b 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
@@ -15,20 +15,26 @@
  */
 package com.android.launcher3.widget.picker;
 
+import android.graphics.Bitmap;
 import android.view.View;
 
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
 import com.android.launcher3.R;
+import com.android.launcher3.model.WidgetItem;
+
+import java.util.HashMap;
+import java.util.Map;
 
 /** A {@link ViewHolder} for showing widgets of an app in the full widget picker. */
 public final class WidgetsRowViewHolder extends ViewHolder {
 
-    public final WidgetsListTableView mTableContainer;
+    public final WidgetsListTableView tableContainer;
+    public final Map<WidgetItem, Bitmap> previewCache = new HashMap<>();
 
     public WidgetsRowViewHolder(View v) {
         super(v);
 
-        mTableContainer = v.findViewById(R.id.widgets_table);
+        tableContainer = v.findViewById(R.id.widgets_table);
     }
 }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsSpaceViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsSpaceViewHolderBinder.java
new file mode 100644
index 0000000..1aa5753
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsSpaceViewHolderBinder.java
@@ -0,0 +1,115 @@
+/*
+ * 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.widget.picker;
+
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+import com.android.launcher3.recyclerview.ViewHolderBinder;
+import com.android.launcher3.widget.model.WidgetListSpaceEntry;
+
+import java.util.List;
+import java.util.function.IntSupplier;
+
+/**
+ * {@link ViewHolderBinder} for binding the top empty space
+ */
+public class WidgetsSpaceViewHolderBinder
+        implements ViewHolderBinder<WidgetListSpaceEntry, ViewHolder> {
+
+    private final IntSupplier mEmptySpaceHeightProvider;
+
+    public WidgetsSpaceViewHolderBinder(IntSupplier emptySpaceHeightProvider) {
+        mEmptySpaceHeightProvider = emptySpaceHeightProvider;
+    }
+
+    @Override
+    public ViewHolder newViewHolder(ViewGroup parent) {
+        return new ViewHolder(new EmptySpaceView(parent.getContext())) { };
+    }
+
+    @Override
+    public void bindViewHolder(ViewHolder holder, WidgetListSpaceEntry data,
+            @ListPosition int position, List<Object> payloads) {
+        ((EmptySpaceView) holder.itemView).setFixedHeight(mEmptySpaceHeightProvider.getAsInt());
+    }
+
+    /**
+     * Empty view which allows listening for 'Y' changes
+     */
+    public static class EmptySpaceView extends View {
+
+        private Runnable mOnYChangeCallback;
+        private int mHeight = 0;
+
+        private EmptySpaceView(Context context) {
+            super(context);
+            animate().setUpdateListener(v -> notifyYChanged());
+        }
+
+        /**
+         * Sets the height for the empty view
+         * @return true if the height changed, false otherwise
+         */
+        public boolean setFixedHeight(int height) {
+            if (mHeight != height) {
+                mHeight = height;
+                requestLayout();
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            super.onMeasure(widthMeasureSpec, makeMeasureSpec(mHeight, EXACTLY));
+        }
+
+        public void setOnYChangeCallback(Runnable callback) {
+            mOnYChangeCallback = callback;
+        }
+
+        @Override
+        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+            super.onLayout(changed, left, top, right, bottom);
+            notifyYChanged();
+        }
+
+        @Override
+        public void offsetTopAndBottom(int offset) {
+            super.offsetTopAndBottom(offset);
+            notifyYChanged();
+        }
+
+        @Override
+        public void setTranslationY(float translationY) {
+            super.setTranslationY(translationY);
+            notifyYChanged();
+        }
+
+        private void notifyYChanged() {
+            if (mOnYChangeCallback != null) {
+                mOnYChangeCallback.run();
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/widget/util/WidgetSizes.java b/src/com/android/launcher3/widget/util/WidgetSizes.java
index 451ed6e..fb2d63b 100644
--- a/src/com/android/launcher3/widget/util/WidgetSizes.java
+++ b/src/com/android/launcher3/widget/util/WidgetSizes.java
@@ -17,7 +17,6 @@
 
 import static android.appwidget.AppWidgetHostView.getDefaultPaddingForWidget;
 
-
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
 import android.content.ComponentName;
@@ -87,7 +86,13 @@
     }
 
     /**
-     * Returns the size of a WidgetItem.
+     * Returns the size of a {@link WidgetItem}.
+     *
+     * <p>This size is used by the widget picker. It should NEVER be shared with app widgets.
+     *
+     * <p>For sizes shared with app widgets, please refer to
+     * {@link #getWidgetPaddedSizes(Context, ComponentName, int, int)} &
+     * {@link #getWidgetPaddedSizePx(Context, ComponentName, DeviceProfile, int, int)}.
      */
     public static Size getWidgetItemSizePx(Context context, DeviceProfile profile,
             WidgetItem widgetItem) {
@@ -96,14 +101,21 @@
                     .getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
             return new Size(dimension, dimension);
         }
-        return getWidgetPaddedSizePx(context, widgetItem.componentName, profile, widgetItem.spanX,
-                widgetItem.spanY);
+        Size widgetItemSize = getWidgetSizePx(profile, widgetItem.spanX,
+                widgetItem.spanY, /* recycledCellSize= */ null);
+        if (profile.shouldInsetWidgets()) {
+            Rect inset = new Rect();
+            AppWidgetHostView.getDefaultPaddingForWidget(context, widgetItem.componentName, inset);
+            return new Size(widgetItemSize.getWidth() + inset.left + inset.right,
+                    widgetItemSize.getHeight() + inset.top + inset.bottom);
+        }
+        return widgetItemSize;
     }
 
     private static Size getWidgetSizePx(DeviceProfile profile, int spanX, int spanY,
             @Nullable Point recycledCellSize) {
-        final int hBorderSpacing = (spanX - 1) * profile.cellLayoutBorderSpacingPx;
-        final int vBorderSpacing = (spanY - 1) * profile.cellLayoutBorderSpacingPx;
+        final int hBorderSpacing = (spanX - 1) * profile.cellLayoutBorderSpacePx.x;
+        final int vBorderSpacing = (spanY - 1) * profile.cellLayoutBorderSpacePx.y;
         if (recycledCellSize == null) {
             recycledCellSize = new Point();
         }
diff --git a/src/com/android/launcher3/widget/util/WidgetsTableUtils.java b/src/com/android/launcher3/widget/util/WidgetsTableUtils.java
index 54aaf93..72e27bf 100644
--- a/src/com/android/launcher3/widget/util/WidgetsTableUtils.java
+++ b/src/com/android/launcher3/widget/util/WidgetsTableUtils.java
@@ -45,9 +45,20 @@
         return item.spanX > otherItem.spanX ? 1 : -1;
     };
 
+    /**
+     * Groups {@code widgetItems} items into a 2D array which matches their appearance in a UI
+     * table. This takes liberty to rearrange widgets to make the table visually appealing.
+     */
+    public static List<ArrayList<WidgetItem>> groupWidgetItemsIntoTableWithReordering(
+            List<WidgetItem> widgetItems, final int maxSpansPerRow) {
+        List<WidgetItem> sortedWidgetItems = widgetItems.stream().sorted(WIDGET_SHORTCUT_COMPARATOR)
+                .collect(Collectors.toList());
+        return groupWidgetItemsIntoTableWithoutReordering(sortedWidgetItems, maxSpansPerRow);
+    }
 
     /**
-     * Groups widgets items into a 2D array which matches their appearance in a UI table.
+     * Groups {@code widgetItems} into a 2D array which matches their appearance in a UI table while
+     * maintaining their order.
      *
      * <p>Grouping:
      * 1. Widgets and shortcuts never group together in the same row.
@@ -64,13 +75,12 @@
      * should be moved to a new row.
      * Example 3: Row 1: 6x4. This is okay because this is the only item in the row.
      */
-    public static List<ArrayList<WidgetItem>> groupWidgetItemsIntoTable(
+    public static List<ArrayList<WidgetItem>> groupWidgetItemsIntoTableWithoutReordering(
             List<WidgetItem> widgetItems, final int maxSpansPerRow) {
-        List<WidgetItem> sortedWidgetItems = widgetItems.stream().sorted(WIDGET_SHORTCUT_COMPARATOR)
-                .collect(Collectors.toList());
+
         List<ArrayList<WidgetItem>> widgetItemsTable = new ArrayList<>();
         ArrayList<WidgetItem> widgetItemsAtRow = null;
-        for (WidgetItem widgetItem : sortedWidgetItems) {
+        for (WidgetItem widgetItem : widgetItems) {
             if (widgetItemsAtRow == null) {
                 widgetItemsAtRow = new ArrayList<>();
                 widgetItemsTable.add(widgetItemsAtRow);
diff --git a/src_plugins/com/android/systemui/plugins/OneSearch.java b/src_plugins/com/android/systemui/plugins/OneSearch.java
new file mode 100644
index 0000000..8bd0b75
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/OneSearch.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.plugins;
+
+import android.os.Parcelable;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+import java.util.ArrayList;
+
+/**
+ * Implement this interface to get suggest for one search.
+ */
+@ProvidesInterface(action = OneSearch.ACTION, version = OneSearch.VERSION)
+public interface OneSearch extends Plugin {
+    String ACTION = "com.android.systemui.action.PLUGIN_ONE_SEARCH";
+    int VERSION = 3;
+
+    /**
+     * Get the content provider warmed up.
+     */
+    void warmUp();
+
+    /**
+     * Get the suggest search target list for the query.
+     * @param query The query to get the search suggests for.
+     */
+    ArrayList<Parcelable> getSuggests(Parcelable query);
+}
diff --git a/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java b/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java
deleted file mode 100644
index a434d07..0000000
--- a/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.plugins;
-
-import android.view.MotionEvent;
-
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-
-/**
- * Implement this interface to receive a callback when the user swipes right
- * to left on the gesture area. It won't fire if the user has quick switched to a previous app
- * (swiped right) and the current app isn't yet the active one (i.e., if swiping left would take
- * the user to a more recent app).
- */
-@ProvidesInterface(action = com.android.systemui.plugins.OverscrollPlugin.ACTION,
-        version = com.android.systemui.plugins.OverscrollPlugin.VERSION)
-public interface OverscrollPlugin extends Plugin {
-
-    String ACTION = "com.android.systemui.action.PLUGIN_LAUNCHER_OVERSCROLL";
-    int VERSION = 4;
-
-    String DEVICE_STATE_LOCKED = "Locked";
-    String DEVICE_STATE_LAUNCHER = "Launcher";
-    String DEVICE_STATE_APP = "App";
-    String DEVICE_STATE_UNKNOWN = "Unknown";
-
-    /**
-     * @return true if the plugin is active and will accept overscroll gestures
-     */
-    boolean isActive();
-
-    /**
-     * Called when a touch has been recognized as an overscroll gesture.
-     * @param horizontalDistancePx Horizontal distance from the last finger location to the finger
-     *                               location when it first touched the screen.
-     * @param verticalDistancePx Horizontal distance from the last finger location to the finger
-     *                             location when it first touched the screen.
-     * @param thresholdPx Minimum distance for gesture.
-     * @param flingDistanceThresholdPx Minimum distance for gesture by fling.
-     * @param flingVelocityThresholdPx Minimum velocity for gesture by fling.
-     * @param deviceState String representing the current device state
-     * @param underlyingActivity String representing the currently active Activity
-     */
-    void onTouchEvent(MotionEvent event,
-                      int horizontalDistancePx,
-                      int verticalDistancePx,
-                      int thresholdPx,
-                      int flingDistanceThresholdPx,
-                      int flingVelocityThresholdPx,
-                      String deviceState,
-                      String underlyingActivity);
-
-    /**
-     * @return `true` if overscroll gesture handling should override all other gestures.
-     */
-    boolean blockOtherGestures();
-
-    /**
-     * @return `true` if the overscroll gesture can pan the underlying app.
-     */
-    boolean allowsUnderlyingActivityOverscroll();
-}
diff --git a/src_plugins/com/android/systemui/plugins/OverviewScreenshotActions.java b/src_plugins/com/android/systemui/plugins/OverviewScreenshotActions.java
deleted file mode 100644
index 8d9c0f4..0000000
--- a/src_plugins/com/android/systemui/plugins/OverviewScreenshotActions.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.plugins;
-
-import android.app.Activity;
-import android.graphics.Bitmap;
-import android.view.ViewGroup;
-
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-
-/**
- * Implement this interface to add action buttons for overview screenshots, e.g. share, edit etc.
- */
-@ProvidesInterface(
-        action = OverviewScreenshotActions.ACTION, version = OverviewScreenshotActions.VERSION)
-public interface OverviewScreenshotActions extends Plugin {
-    String ACTION = "com.android.systemui.action.PLUGIN_OVERVIEW_SCREENSHOT_ACTIONS";
-    int VERSION = 1;
-
-    /**
-     * Setup the actions for the screenshot, including edit, save, etc.
-     * @param parent The parent view to add buttons on.
-     * @param screenshot The screenshot we will do actions on.
-     * @param activity THe host activity.
-     */
-    void setupActions(ViewGroup parent, Bitmap screenshot, Activity activity);
-}
diff --git a/src_plugins/com/android/systemui/plugins/RecentsExtraCard.java b/src_plugins/com/android/systemui/plugins/RecentsExtraCard.java
deleted file mode 100644
index cd9f33d..0000000
--- a/src_plugins/com/android/systemui/plugins/RecentsExtraCard.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.plugins;
-
-import android.app.Activity;
-import android.content.Context;
-import android.widget.FrameLayout;
-
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-
-/**
- * Implement this interface to allow extra card on recents overview.
- */
-@ProvidesInterface(action = RecentsExtraCard.ACTION, version = RecentsExtraCard.VERSION)
-public interface RecentsExtraCard extends Plugin {
-
-    String ACTION = "com.android.systemui.action.PLUGIN_RECENTS_EXTRA_CARD";
-    int VERSION = 1;
-
-    /**
-     * Sets up the recents overview extra card and fills in data.
-     *
-     * @param context     Plugin context
-     * @param frameLayout PlaceholderView
-     * @param activity    Recents activity to hold extra view
-     */
-    void setupView(Context context, FrameLayout frameLayout, Activity activity);
-}
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 631067b..702f343 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -4,7 +4,10 @@
 import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_HIDE_FROM_PICKER;
 
 import static com.android.launcher3.pm.ShortcutConfigActivityInfo.queryList;
+import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
 
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.mapping;
 import static java.util.stream.Collectors.toList;
 
 import android.appwidget.AppWidgetProviderInfo;
@@ -13,6 +16,7 @@
 import android.content.pm.PackageManager;
 import android.os.UserHandle;
 import android.util.Log;
+import android.util.Pair;
 
 import androidx.annotation.Nullable;
 import androidx.collection.ArrayMap;
@@ -27,10 +31,12 @@
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.pm.ShortcutConfigActivityInfo;
+import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.WidgetManagerHelper;
+import com.android.launcher3.widget.WidgetSections;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 import com.android.launcher3.widget.model.WidgetsListContentEntry;
 import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
@@ -40,12 +46,12 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.function.Predicate;
-import java.util.stream.Collectors;
 
 /**
  * Widgets data model that is used by the adapters of the widget views and controllers.
@@ -61,9 +67,6 @@
     private static final String TAG = "WidgetsModel";
     private static final boolean DEBUG = false;
 
-    private static final ComponentName CONVERSATION_WIDGET = ComponentName.createRelative(
-            "com.android.systemui", ".people.widget.PeopleSpaceWidgetProvider");
-
     /* Map of widgets and shortcuts that are tracked per package. */
     private final Map<PackageItemInfo, List<WidgetItem>> mWidgetsList = new HashMap<>();
 
@@ -150,7 +153,6 @@
             }
         }
 
-        app.getWidgetCache().removeObsoletePreviews(widgetsAndShortcuts, packageUser);
         return updatedItems;
     }
 
@@ -169,16 +171,15 @@
             mWidgetsList.clear();
         } else {
             // Otherwise, only clear the widgets and shortcuts for the changed package.
-            mWidgetsList.remove(
-                    packageItemInfoCache.getOrCreate(new WidgetPackageOrCategoryKey(packageUser)));
+            mWidgetsList.remove(packageItemInfoCache.getOrCreate(packageUser));
         }
 
         // add and update.
         mWidgetsList.putAll(rawWidgetsShortcuts.stream()
                 .filter(new WidgetValidityCheck(app))
-                .collect(Collectors.groupingBy(item ->
-                        packageItemInfoCache.getOrCreate(getWidgetPackageOrCategoryKey(item))
-                )));
+                .flatMap(widgetItem -> getPackageUserKeys(app.getContext(), widgetItem).stream()
+                        .map(key -> new Pair<>(packageItemInfoCache.getOrCreate(key), widgetItem)))
+                .collect(groupingBy(pair -> pair.first, mapping(pair -> pair.second, toList()))));
 
         // Update each package entry
         IconCache iconCache = app.getIconCache();
@@ -210,9 +211,9 @@
     }
 
     public WidgetItem getWidgetProviderInfoByProviderName(
-            ComponentName providerName) {
+            ComponentName providerName, UserHandle user) {
         List<WidgetItem> widgetsList = mWidgetsList.get(
-                new PackageItemInfo(providerName.getPackageName()));
+                new PackageItemInfo(providerName.getPackageName(), user));
         if (widgetsList == null) {
             return null;
         }
@@ -226,18 +227,40 @@
     }
 
     /** Returns {@link PackageItemInfo} of a pending widget. */
-    public static PackageItemInfo newPendingItemInfo(ComponentName provider) {
-        if (CONVERSATION_WIDGET.equals(provider)) {
-            return new PackageItemInfo(provider.getPackageName(), PackageItemInfo.CONVERSATIONS);
+    public static PackageItemInfo newPendingItemInfo(Context context, ComponentName provider,
+            UserHandle user) {
+        Map<ComponentName, IntSet> widgetsToCategories =
+                WidgetSections.getWidgetsToCategory(context);
+        if (widgetsToCategories.containsKey(provider)) {
+            Iterator<Integer> categoriesIterator = widgetsToCategories.get(provider).iterator();
+            int firstCategory = NO_CATEGORY;
+            while (categoriesIterator.hasNext() && firstCategory == NO_CATEGORY) {
+                firstCategory = categoriesIterator.next();
+            }
+            return new PackageItemInfo(provider.getPackageName(), firstCategory, user);
         }
-        return new PackageItemInfo(provider.getPackageName());
+        return new PackageItemInfo(provider.getPackageName(), user);
     }
 
-    private WidgetPackageOrCategoryKey getWidgetPackageOrCategoryKey(WidgetItem item) {
-        if (CONVERSATION_WIDGET.equals(item.componentName)) {
-            return new WidgetPackageOrCategoryKey(PackageItemInfo.CONVERSATIONS, item.user);
+    private List<PackageUserKey> getPackageUserKeys(Context context, WidgetItem item) {
+        Map<ComponentName, IntSet> widgetsToCategories =
+                WidgetSections.getWidgetsToCategory(context);
+        IntSet categories = widgetsToCategories.get(item.componentName);
+        if (categories == null || categories.isEmpty()) {
+            return Arrays.asList(
+                    new PackageUserKey(item.componentName.getPackageName(), item.user));
         }
-        return new WidgetPackageOrCategoryKey(item.componentName.getPackageName(), item.user);
+        List<PackageUserKey> packageUserKeys = new ArrayList<>();
+        categories.forEach(category -> {
+            if (category == NO_CATEGORY) {
+                packageUserKeys.add(
+                        new PackageUserKey(item.componentName.getPackageName(),
+                                item.user));
+            } else {
+                packageUserKeys.add(new PackageUserKey(category, item.user));
+            }
+        });
+        return packageUserKeys;
     }
 
     private static class WidgetValidityCheck implements Predicate<WidgetItem> {
@@ -280,53 +303,13 @@
         }
     }
 
-    /** A hash key for grouping widgets by package name or category. */
-    private static class WidgetPackageOrCategoryKey {
-        /**
-         * The package name of the widget provider.
-         *
-         * <p>This shouldn't be empty if {@link #mCategory} has a value,
-         * {@link PackageItemInfo#NO_CATEGORY}.
-         */
-        public final String mPackage;
-        /** A widget category. */
-        @PackageItemInfo.Category public final int mCategory;
-        public final UserHandle mUser;
-        private final int mHashCode;
-
-        WidgetPackageOrCategoryKey(PackageUserKey key) {
-            this(key.mPackageName, key.mUser);
-        }
-
-        WidgetPackageOrCategoryKey(String packageName, UserHandle user) {
-            this(packageName,  PackageItemInfo.NO_CATEGORY, user);
-        }
-
-        WidgetPackageOrCategoryKey(@PackageItemInfo.Category int category, UserHandle user) {
-            this("", category, user);
-        }
-
-        private WidgetPackageOrCategoryKey(String packageName,
-                @PackageItemInfo.Category int category, UserHandle user) {
-            mPackage = packageName;
-            mCategory = category;
-            mUser = user;
-            mHashCode = Arrays.hashCode(new Object[]{mPackage, mCategory, mUser});
-        }
-
-        @Override
-        public int hashCode() {
-            return mHashCode;
-        }
-    }
-
     private static final class PackageItemInfoCache {
-        private final Map<WidgetPackageOrCategoryKey, PackageItemInfo> mMap = new ArrayMap<>();
+        private final Map<PackageUserKey, PackageItemInfo> mMap = new ArrayMap<>();
 
-        PackageItemInfo getOrCreate(WidgetPackageOrCategoryKey key) {
+        PackageItemInfo getOrCreate(PackageUserKey key) {
             PackageItemInfo pInfo = mMap.get(key);
             if (pInfo == null) {
-                pInfo = new PackageItemInfo(key.mPackage, key.mCategory);
+                pInfo = new PackageItemInfo(key.mPackageName, key.mWidgetCategory, key.mUser);
                 pInfo.user = key.mUser;
                 mMap.put(key,  pInfo);
             }
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
index 4407fe1..81e3f98 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.uioverrides;
 
 import android.app.Person;
+import android.content.Context;
 import android.content.pm.ShortcutInfo;
 import android.view.Display;
 
@@ -36,4 +37,18 @@
     public static boolean isInternalDisplay(Display display) {
         return display.getDisplayId() == Display.DEFAULT_DISPLAY;
     }
+
+    /**
+     * Returns a unique ID representing the display
+     */
+    public static String getUniqueId(Display display) {
+        return Integer.toString(display.getDisplayId());
+    }
+
+    /**
+     * Returns the minimum space that should be left empty at the end of hotseat
+     */
+    public static int getHotseatEndOffset(Context context) {
+        return 0;
+    }
 }
diff --git a/tests/Android.bp b/tests/Android.bp
index da55c28..3670c37 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -20,7 +20,77 @@
     default_applicable_licenses: ["packages_apps_Launcher3_license"],
 }
 
+// Source code used for test
 filegroup {
-    name: "launcher3-test-src-common",
-    srcs: ["src_common/**/*.java"],
+    name: "launcher-tests-src",
+    srcs: ["src/**/*.java"],
+}
+
+// Source code used for oop test helpers
+filegroup {
+    name: "launcher-oop-tests-src",
+    srcs: [
+      "src/com/android/launcher3/ui/AbstractLauncherUiTest.java",
+      "src/com/android/launcher3/ui/PortraitLandscapeRunner.java",
+      "src/com/android/launcher3/util/Wait.java",
+      "src/com/android/launcher3/util/WidgetUtils.java",
+      "src/com/android/launcher3/util/rule/FailureWatcher.java",
+      "src/com/android/launcher3/util/rule/LauncherActivityRule.java",
+      "src/com/android/launcher3/util/rule/ScreenRecordRule.java",
+      "src/com/android/launcher3/util/rule/ShellCommandRule.java",
+      "src/com/android/launcher3/util/rule/SimpleActivityRule.java",
+      "src/com/android/launcher3/util/rule/TestStabilityRule.java",
+      "src/com/android/launcher3/ui/TaplTestsLauncher3.java",
+      "src/com/android/launcher3/testcomponent/BaseTestingActivity.java",
+      "src/com/android/launcher3/testcomponent/CustomShortcutConfigActivity.java",
+      "src/com/android/launcher3/testcomponent/TestCommandReceiver.java",
+      "src/com/android/launcher3/testcomponent/TestLauncherActivity.java",
+    ],
+}
+
+// Library with all the dependencies for building quickstep
+android_library {
+    name: "Launcher3TestLib",
+    srcs: [ ],
+    resource_dirs: ["res"],
+    static_libs: [
+        "launcher-aosp-tapl",
+        "androidx.test.core",
+        "androidx.test.runner",
+        "androidx.test.rules",
+        "androidx.test.ext.junit",
+        "androidx.test.espresso.core",
+        "androidx.test.espresso.contrib",
+        "androidx.test.espresso.intents",
+        "androidx.test.uiautomator_uiautomator",
+        "mockito-target-inline-minus-junit4",
+        "launcher_log_protos_lite",
+        "truth-prebuilt"
+    ],
+    manifest: "AndroidManifest-common.xml",
+    platform_apis: true,
+}
+
+android_test {
+    name: "Launcher3Tests",
+    srcs: [
+        ":launcher-tests-src",
+    ],
+    static_libs: ["Launcher3TestLib"],
+    libs: [
+        "android.test.base",
+        "android.test.runner",
+        "android.test.mock",
+    ],
+    jni_libs: [
+        "libdexmakerjvmtiagent",
+        "libstaticjvmtiagent",
+    ],
+    use_embedded_native_libs: false,
+    compile_multilib: "both",
+    instrumentation_for: "Launcher3",
+    manifest: "AndroidManifest.xml",
+    platform_apis: true,
+    test_config: "Launcher3Tests.xml",
+    data: [":Launcher3"]
 }
diff --git a/tests/Android.mk b/tests/Android.mk
deleted file mode 100644
index 6adc685..0000000
--- a/tests/Android.mk
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright (C) 2015 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-#
-# Build rule for Launcher3Tests
-#
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    androidx.test.runner \
-    androidx.test.rules \
-    androidx.test.uiautomator_uiautomator \
-    mockito-target-minus-junit4 \
-    launcher_log_protos_lite
-
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_STATIC_JAVA_LIBRARIES += launcher-aosp-tapl
-
-LOCAL_SRC_FILES := \
-	$(call all-java-files-under, src) \
-	$(call all-java-files-under, src_common)
-
-
-LOCAL_FULL_LIBS_MANIFEST_FILES := $(LOCAL_PATH)/AndroidManifest-common.xml
-
-LOCAL_PACKAGE_NAME := Launcher3Tests
-
-LOCAL_INSTRUMENTATION_FOR := Launcher3
-
-LOCAL_TEST_CONFIG := Launcher3Tests.xml
-
-LOCAL_COMPATIBILITY_SUPPORT_FILES := $(call intermediates-dir-for,APPS,Launcher3)/package.apk:Launcher3.apk
-
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 918ec4a..aae8fb5 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -24,7 +24,7 @@
     <uses-permission android:name="android.permission.READ_LOGS"/>
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
 
-    <application android:debuggable="true">
+    <application android:debuggable="true" android:extractNativeLibs="true">
         <uses-library android:name="android.test.runner"/>
 
         <receiver
@@ -145,6 +145,16 @@
             <meta-data android:name="android.app.shortcuts"
                        android:resource="@xml/shortcuts"/>
         </activity>
+        <activity
+            android:name="com.android.launcher3.testcomponent.OtherBaseTestingActivity"
+            android:label="OtherLauncherTestApp"
+            android:exported="true"
+            android:taskAffinity="com.android.launcher3.testcomponent.Affinity2">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
         <activity-alias android:name="Activity2"
                         android:label="TestActivity2"
                         android:exported="true"
@@ -208,31 +218,36 @@
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity-alias>
-        <activity-alias android:name="Activity9"
-                        android:label="TestActivity9"
-                        android:exported="true"
-                        android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
+        <activity-alias android:name="Activity9" android:exported="true"
+            android:label="TestActivity9"
+            android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
             <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.LAUNCHER"/>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity-alias>
-        <activity-alias android:name="Activity10"
-                        android:label="TestActivity10"
-                        android:exported="true"
-                        android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
+        <activity-alias android:name="Activity10" android:exported="true"
+            android:label="TestActivity10"
+            android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
             <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.LAUNCHER"/>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity-alias>
-        <activity-alias android:name="Activity11"
-                        android:label="TestActivity11"
-                        android:exported="true"
-                        android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
+        <activity-alias android:name="Activity11" android:exported="true"
+            android:label="TestActivity11"
+            android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
             <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.LAUNCHER"/>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity-alias>
+        <activity-alias android:name="Activity12" android:exported="true"
+            android:label="TestActivity12"
+            android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity-alias>
     </application>
diff --git a/robolectric_tests/resources/cache_data_updated_task_data.txt b/tests/res/raw/cache_data_updated_task_data.txt
similarity index 100%
rename from robolectric_tests/resources/cache_data_updated_task_data.txt
rename to tests/res/raw/cache_data_updated_task_data.txt
diff --git a/robolectric_tests/resources/db_schema_v10.json b/tests/res/raw/db_schema_v10.json
similarity index 100%
rename from robolectric_tests/resources/db_schema_v10.json
rename to tests/res/raw/db_schema_v10.json
diff --git a/robolectric_tests/resources/package_install_state_change_task_data.txt b/tests/res/raw/package_install_state_change_task_data.txt
similarity index 100%
rename from robolectric_tests/resources/package_install_state_change_task_data.txt
rename to tests/res/raw/package_install_state_change_task_data.txt
diff --git a/robolectric_tests/resources/widgets_predication_update_task_data.txt b/tests/res/raw/widgets_predication_update_task_data.txt
similarity index 100%
rename from robolectric_tests/resources/widgets_predication_update_task_data.txt
rename to tests/res/raw/widgets_predication_update_task_data.txt
diff --git a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
index 33066e4..032a7b4 100644
--- a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
+++ b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
@@ -25,8 +25,8 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.Workspace;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
 
 import org.junit.After;
 import org.junit.Test;
@@ -75,7 +75,7 @@
     @Test
     public void testPromiseIcon_addedFromEligibleSession() throws Throwable {
         final String appLabel = "Test Promise App " + UUID.randomUUID().toString();
-        final Workspace.ItemOperator findPromiseApp = (info, view) ->
+        final ItemOperator findPromiseApp = (info, view) ->
                 info != null && TextUtils.equals(info.title, appLabel);
 
         // Create and add test session
@@ -97,7 +97,7 @@
     @Test
     public void testPromiseIcon_notAddedFromIneligibleSession() throws Throwable {
         final String appLabel = "Test Promise App " + UUID.randomUUID().toString();
-        final Workspace.ItemOperator findPromiseApp = (info, view) ->
+        final ItemOperator findPromiseApp = (info, view) ->
                 info != null && TextUtils.equals(info.title, appLabel);
 
         // Create and add test session without icon or label
diff --git a/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java b/tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
similarity index 90%
rename from robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
rename to tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
index 2a94d9b..23e6235 100644
--- a/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
+++ b/tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.folder;
 
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
@@ -23,6 +25,9 @@
 import android.content.Intent;
 import android.os.UserHandle;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.Executors;
@@ -30,15 +35,11 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.LooperMode;
-import org.robolectric.annotation.LooperMode.Mode;
 
 import java.util.ArrayList;
 
-@RunWith(RobolectricTestRunner.class)
-@LooperMode(Mode.PAUSED)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public final class FolderNameProviderTest {
     private Context mContext;
     private WorkspaceItemInfo mItem1;
@@ -46,7 +47,7 @@
 
     @Before
     public void setUp() {
-        mContext = RuntimeEnvironment.application;
+        mContext = getApplicationContext();
         mItem1 = new WorkspaceItemInfo(new AppInfo(
                 new ComponentName("a.b.c", "a.b.c/a.b.c.d"),
                 "title1",
diff --git a/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java b/tests/src/com/android/launcher3/logging/FileLogTest.java
similarity index 89%
rename from robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
rename to tests/src/com/android/launcher3/logging/FileLogTest.java
index 01b23ba..e5f8cec 100644
--- a/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
+++ b/tests/src/com/android/launcher3/logging/FileLogTest.java
@@ -1,16 +1,17 @@
 package com.android.launcher3.logging;
 
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.LooperMode;
-import org.robolectric.annotation.LooperMode.Mode;
 
 import java.io.File;
 import java.io.PrintWriter;
@@ -20,8 +21,8 @@
 /**
  * Tests for {@link FileLog}
  */
-@RunWith(RobolectricTestRunner.class)
-@LooperMode(Mode.PAUSED)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class FileLogTest {
 
     private File mTempDir;
@@ -29,7 +30,7 @@
     public void setUp() {
         int count = 0;
         do {
-            mTempDir = new File(RuntimeEnvironment.application.getCacheDir(),
+            mTempDir = new File(getApplicationContext().getCacheDir(),
                     "log-test-" + (count++));
         } while (!mTempDir.mkdir());
 
diff --git a/robolectric_tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
similarity index 84%
rename from robolectric_tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
rename to tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
index 8aa6f37..16f024e 100644
--- a/robolectric_tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
+++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
@@ -13,6 +13,9 @@
 import android.graphics.Rect;
 import android.util.Pair;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
@@ -21,19 +24,17 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSparseArrayMap;
 import com.android.launcher3.util.LauncherModelHelper;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.LooperMode;
-import org.robolectric.annotation.LooperMode.Mode;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -41,8 +42,8 @@
 /**
  * Tests for {@link AddWorkspaceItemsTask}
  */
-@RunWith(RobolectricTestRunner.class)
-@LooperMode(Mode.PAUSED)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class AddWorkspaceItemsTaskTest {
 
     private final ComponentName mComponent1 = new ComponentName("a", "b");
@@ -60,7 +61,7 @@
     @Before
     public void setup() {
         mModelHelper = new LauncherModelHelper();
-        mTargetContext = RuntimeEnvironment.application;
+        mTargetContext = mModelHelper.sandboxContext;
         mIdp = InvariantDeviceProfile.INSTANCE.get(mTargetContext);
         mIdp.numColumns = mIdp.numRows = 5;
         mAppState = LauncherAppState.getInstance(mTargetContext);
@@ -70,6 +71,11 @@
         mNewScreens = new IntArray();
     }
 
+    @After
+    public void tearDown() {
+        mModelHelper.destroy();
+    }
+
     private AddWorkspaceItemsTask newTask(ItemInfo... items) {
         List<Pair<ItemInfo, Object>> list = new ArrayList<>();
         for (ItemInfo item : items) {
@@ -80,6 +86,8 @@
 
     @Test
     public void testFindSpaceForItem_prefers_second() throws Exception {
+        mIdp.isSplitDisplay = false;
+
         // First screen has only one hole of size 1
         int nextId = setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
 
@@ -88,7 +96,7 @@
 
         int[] spaceFound = newTask().findSpaceForItem(
                 mAppState, mModelHelper.getBgDataModel(), mExistingScreens, mNewScreens, 1, 1);
-        assertEquals(2, spaceFound[0]);
+        assertEquals(1, spaceFound[0]);
         assertTrue(mScreenOccupancy.get(spaceFound[0])
                 .isRegionVacant(spaceFound[1], spaceFound[2], 1, 1));
 
@@ -101,6 +109,24 @@
     }
 
     @Test
+    public void testFindSpaceForItem_prefers_third_on_split_display() throws Exception {
+        mIdp.isSplitDisplay = true;
+        // First screen has only one hole of size 1
+        int nextId = setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
+
+        // Second screen has 2 holes of sizes 3x2 and 2x3
+        setupWorkspaceWithHoles(nextId, 2, new Rect(2, 0, 5, 2), new Rect(0, 2, 2, 5));
+
+        int[] spaceFound = newTask().findSpaceForItem(
+                mAppState, mModelHelper.getBgDataModel(), mExistingScreens, mNewScreens, 1, 1);
+        // For split display, it picks the next screen, even if there is enough space
+        // on previous screen
+        assertEquals(2, spaceFound[0]);
+        assertTrue(mScreenOccupancy.get(spaceFound[0])
+                .isRegionVacant(spaceFound[1], spaceFound[2], 1, 1));
+    }
+
+    @Test
     public void testFindSpaceForItem_adds_new_screen() throws Exception {
         // First screen has 2 holes of sizes 3x2 and 2x3
         setupWorkspaceWithHoles(1, 1, new Rect(2, 0, 5, 2), new Rect(0, 2, 2, 5));
@@ -127,7 +153,7 @@
     @Test
     public void testAddItem_some_items_added() throws Exception {
         Callbacks callbacks = mock(Callbacks.class);
-        mModelHelper.getModel().addCallbacks(callbacks);
+        Executors.MAIN_EXECUTOR.submit(() -> mModelHelper.getModel().addCallbacks(callbacks)).get();
 
         WorkspaceItemInfo info = new WorkspaceItemInfo();
         info.intent = new Intent().setComponent(mComponent1);
diff --git a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java b/tests/src/com/android/launcher3/model/BackupRestoreTest.java
similarity index 62%
rename from robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
rename to tests/src/com/android/launcher3/model/BackupRestoreTest.java
index 34a8025..41914de 100644
--- a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
+++ b/tests/src/com/android/launcher3/model/BackupRestoreTest.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.model;
 
 import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
+import static android.os.Process.myUserHandle;
 
 import static com.android.launcher3.LauncherSettings.Favorites.BACKUP_TABLE_NAME;
 import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
@@ -26,74 +27,110 @@
 import static com.android.launcher3.util.LauncherModelHelper.APP_ICON;
 import static com.android.launcher3.util.LauncherModelHelper.NO__ICON;
 import static com.android.launcher3.util.LauncherModelHelper.SHORTCUT;
+import static com.android.launcher3.util.ReflectionHelpers.getField;
+import static com.android.launcher3.util.ReflectionHelpers.setField;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-import static org.robolectric.util.ReflectionHelpers.setField;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
 
 import android.app.backup.BackupManager;
 import android.content.pm.PackageInstaller;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
-import android.os.Process;
 import android.os.UserHandle;
-import android.os.UserManager;
+import android.util.ArrayMap;
+import android.util.LongSparseArray;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
 
 import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.provider.RestoreDbTask;
-import com.android.launcher3.shadows.LShadowBackupManager;
 import com.android.launcher3.util.LauncherModelHelper;
+import com.android.launcher3.util.SafeCloseable;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.LooperMode;
-import org.robolectric.shadow.api.Shadow;
-import org.robolectric.shadows.ShadowUserManager;
 
 /**
  * Tests to verify backup and restore flow.
  */
-@RunWith(RobolectricTestRunner.class)
-@LooperMode(LooperMode.Mode.PAUSED)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class BackupRestoreTest {
 
-    private static final long MY_OLD_PROFILE_ID = 1;
-    private static final long MY_PROFILE_ID = 0;
-    private static final long OLD_WORK_PROFILE_ID = 11;
-    private static final int WORK_PROFILE_ID = 10;
+    private static final int PER_USER_RANGE = 200000;
 
-    private ShadowUserManager mUserManager;
+
+    private long mCurrentMyProfileId;
+    private long mOldMyProfileId;
+
+    private long mCurrentWorkProfileId;
+    private long mOldWorkProfileId;
+
     private BackupManager mBackupManager;
     private LauncherModelHelper mModelHelper;
     private SQLiteDatabase mDb;
     private InvariantDeviceProfile mIdp;
 
+    private UserHandle mWorkUserHandle;
+
+    private SafeCloseable mUserChangeListener;
+
     @Before
     public void setUp() {
+        mModelHelper = new LauncherModelHelper();
+
+        mCurrentMyProfileId = mModelHelper.defaultProfileId;
+        mOldMyProfileId = mCurrentMyProfileId + 1;
+        mCurrentWorkProfileId = mOldMyProfileId + 1;
+        mOldWorkProfileId = mCurrentWorkProfileId + 1;
+
+        mWorkUserHandle = UserHandle.getUserHandleForUid(PER_USER_RANGE);
+        mUserChangeListener = UserCache.INSTANCE.get(mModelHelper.sandboxContext)
+                .addUserChangeListener(() -> { });
+
         setupUserManager();
         setupBackupManager();
-        mModelHelper = new LauncherModelHelper();
-        RestoreDbTask.setPending(RuntimeEnvironment.application, true);
+        RestoreDbTask.setPending(mModelHelper.sandboxContext);
         mDb = mModelHelper.provider.getDb();
-        mIdp = InvariantDeviceProfile.INSTANCE.get(RuntimeEnvironment.application);
+        mIdp = InvariantDeviceProfile.INSTANCE.get(mModelHelper.sandboxContext);
+
+    }
+
+    @After
+    public void tearDown() {
+        mUserChangeListener.close();
+        mModelHelper.destroy();
     }
 
     private void setupUserManager() {
-        final UserManager userManager = RuntimeEnvironment.application.getSystemService(
-                UserManager.class);
-        mUserManager = Shadow.extract(userManager);
-        // sign in to work profile
-        mUserManager.addUser(WORK_PROFILE_ID, "work", ShadowUserManager.FLAG_MANAGED_PROFILE);
+        UserCache cache = UserCache.INSTANCE.get(mModelHelper.sandboxContext);
+        synchronized (cache) {
+            LongSparseArray<UserHandle> users = getField(cache, "mUsers");
+            users.clear();
+            users.put(mCurrentMyProfileId, myUserHandle());
+            users.put(mCurrentWorkProfileId, mWorkUserHandle);
+
+            ArrayMap<UserHandle, Long> userMap = getField(cache, "mUserToSerialMap");
+            userMap.clear();
+            userMap.put(myUserHandle(), mCurrentMyProfileId);
+            userMap.put(mWorkUserHandle, mCurrentWorkProfileId);
+        }
     }
 
     private void setupBackupManager() {
-        mBackupManager = new BackupManager(RuntimeEnvironment.application);
-        final LShadowBackupManager bm = Shadow.extract(mBackupManager);
-        bm.addProfile(MY_OLD_PROFILE_ID, Process.myUserHandle());
-        bm.addProfile(OLD_WORK_PROFILE_ID, UserHandle.of(WORK_PROFILE_ID));
+        mBackupManager = spy(new BackupManager(mModelHelper.sandboxContext));
+        doReturn(myUserHandle()).when(mBackupManager)
+                .getUserForAncestralSerialNumber(eq(mOldMyProfileId));
+        doReturn(mWorkUserHandle).when(mBackupManager)
+                .getUserForAncestralSerialNumber(eq(mOldWorkProfileId));
     }
 
     @Test
@@ -118,18 +155,18 @@
                 { SHORTCUT, SHORTCUT, NO__ICON, NO__ICON},
                 { NO__ICON, NO__ICON, SHORTCUT, SHORTCUT},
                 { APP_ICON, SHORTCUT, SHORTCUT, APP_ICON},
-            }}, 1, MY_OLD_PROFILE_ID);
+            }}, 1, mOldMyProfileId);
         // setup grid for work profile on second screen
         mModelHelper.createGrid(new int[][][]{{
                 { NO__ICON, APP_ICON, SHORTCUT, SHORTCUT},
                 { SHORTCUT, SHORTCUT, NO__ICON, NO__ICON},
                 { NO__ICON, NO__ICON, SHORTCUT, SHORTCUT},
                 { APP_ICON, SHORTCUT, SHORTCUT, NO__ICON},
-            }}, 2, OLD_WORK_PROFILE_ID);
+            }}, 2, mOldWorkProfileId);
         // simulates the creation of backup upon restore
-        new GridBackupTable(RuntimeEnvironment.application, mDb, mIdp.numDatabaseHotseatIcons,
+        new GridBackupTable(mModelHelper.sandboxContext, mDb, mIdp.numDatabaseHotseatIcons,
                 mIdp.numColumns, mIdp.numRows).doBackup(
-                        MY_OLD_PROFILE_ID, GridBackupTable.OPTION_REQUIRES_SANITIZATION);
+                mOldMyProfileId, GridBackupTable.OPTION_REQUIRES_SANITIZATION);
         // reset favorites table
         createTableUsingOldProfileId();
     }
@@ -141,28 +178,28 @@
     private void verifyTableIsFilled(String tableName, boolean sanitized) {
         assertEquals(sanitized ? 12 : 13, getCount(mDb,
                 "SELECT * FROM " + tableName + " WHERE profileId = "
-                        + (sanitized ? MY_PROFILE_ID : MY_OLD_PROFILE_ID)));
+                        + (sanitized ? mCurrentMyProfileId : mOldMyProfileId)));
         assertEquals(10, getCount(mDb, "SELECT * FROM " + tableName + " WHERE profileId = "
-                + (sanitized ? WORK_PROFILE_ID : OLD_WORK_PROFILE_ID)));
+                + (sanitized ? mCurrentWorkProfileId : mOldWorkProfileId)));
     }
 
     private void createTableUsingOldProfileId() {
         // simulates the creation of favorites table on old device
         dropTable(mDb, TABLE_NAME);
-        addTableToDb(mDb, MY_OLD_PROFILE_ID, false);
+        addTableToDb(mDb, mOldMyProfileId, false);
     }
 
     private void createRestoreSession() throws Exception {
         final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                 PackageInstaller.SessionParams.MODE_FULL_INSTALL);
-        final PackageInstaller installer = RuntimeEnvironment.application.getPackageManager()
+        final PackageInstaller installer = mModelHelper.sandboxContext.getPackageManager()
                 .getPackageInstaller();
         final int sessionId = installer.createSession(params);
         final PackageInstaller.SessionInfo info = installer.getSessionInfo(sessionId);
         setField(info, "installReason", INSTALL_REASON_DEVICE_RESTORE);
         // TODO: (b/148410677) we should verify the following call instead
         //  InstallSessionHelper.INSTANCE.get(getContext()).restoreDbIfApplicable(info);
-        RestoreDbTask.restoreIfPossible(RuntimeEnvironment.application,
+        RestoreDbTask.restoreIfPossible(mModelHelper.sandboxContext,
                 mModelHelper.provider.getHelper(), mBackupManager);
     }
 
diff --git a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
similarity index 92%
rename from robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
rename to tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index 9ac3fe7..dba0a40 100644
--- a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -16,6 +16,8 @@
 import android.os.UserManager;
 
 import androidx.annotation.NonNull;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
 
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.icons.BitmapInfo;
@@ -26,13 +28,10 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.LauncherModelHelper;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.LooperMode;
-import org.robolectric.annotation.LooperMode.Mode;
 
 import java.util.Arrays;
 import java.util.HashSet;
@@ -40,8 +39,8 @@
 /**
  * Tests for {@link CacheDataUpdatedTask}
  */
-@RunWith(RobolectricTestRunner.class)
-@LooperMode(Mode.PAUSED)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class CacheDataUpdatedTaskTest {
 
     private static final String NEW_LABEL_PREFIX = "new-label-";
@@ -51,10 +50,10 @@
     @Before
     public void setup() throws Exception {
         mModelHelper = new LauncherModelHelper();
-        mModelHelper.initializeData("/cache_data_updated_task_data.txt");
+        mModelHelper.initializeData("cache_data_updated_task_data");
 
         // Add placeholder entries in the cache to simulate update
-        Context context = RuntimeEnvironment.application;
+        Context context = mModelHelper.sandboxContext;
         IconCache iconCache = LauncherAppState.getInstance(context).getIconCache();
         CachingLogic<ItemInfo> placeholderLogic = new CachingLogic<ItemInfo>() {
             @Override
@@ -86,6 +85,11 @@
         }
     }
 
+    @After
+    public void tearDown() {
+        mModelHelper.destroy();
+    }
+
     private CacheDataUpdatedTask newTask(int op, String... pkg) {
         return new CacheDataUpdatedTask(op, Process.myUserHandle(),
                 new HashSet<>(Arrays.asList(pkg)));
diff --git a/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java b/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
similarity index 91%
rename from robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
rename to tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
index be03c7d..d849c8f 100644
--- a/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
+++ b/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
@@ -15,12 +15,13 @@
  */
 package com.android.launcher3.model;
 
+import static androidx.test.InstrumentationRegistry.getContext;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotSame;
 import static junit.framework.Assert.assertTrue;
 
-import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
@@ -32,6 +33,10 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
 import com.android.launcher3.LauncherProvider;
 import com.android.launcher3.LauncherProvider.DatabaseHelper;
 import com.android.launcher3.LauncherSettings.Favorites;
@@ -40,15 +45,14 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
 
 import java.io.File;
 
 /**
  * Tests for {@link DbDowngradeHelper}
  */
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class DbDowngradeHelperTest {
 
     private static final String SCHEMA_FILE = "test_schema.json";
@@ -60,7 +64,7 @@
 
     @Before
     public void setup() {
-        mContext = RuntimeEnvironment.application;
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
         mSchemaFile = mContext.getFileStreamPath(SCHEMA_FILE);
         mDbFile = mContext.getDatabasePath(DB_FILE);
     }
@@ -77,8 +81,10 @@
     public void testUpdateSchemaFile() throws Exception {
         // Setup mock resources
         Resources res = spy(mContext.getResources());
-        doAnswer(i ->this.getClass().getResourceAsStream("/db_schema_v10.json"))
-                .when(res).openRawResource(eq(R.raw.downgrade_schema));
+        Resources myRes = getContext().getResources();
+        doAnswer(i -> myRes.openRawResource(
+                myRes.getIdentifier("db_schema_v10", "raw", getContext().getPackageName())))
+                .when(res).openRawResource(R.raw.downgrade_schema);
         Context context = spy(mContext);
         when(context.getResources()).thenReturn(res);
 
diff --git a/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java b/tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
similarity index 77%
rename from robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
rename to tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
index 655237d..004ed06 100644
--- a/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
+++ b/tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
@@ -16,18 +16,18 @@
 
 package com.android.launcher3.model;
 
+import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY;
 import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
 
 import static org.junit.Assert.assertEquals;
-import static org.robolectric.Shadows.shadowOf;
-import static org.robolectric.util.ReflectionHelpers.setField;
 
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageInstaller.SessionParams;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.model.data.FolderInfo;
@@ -35,19 +35,16 @@
 import com.android.launcher3.util.LauncherLayoutBuilder;
 import com.android.launcher3.util.LauncherModelHelper;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.LooperMode;
-import org.robolectric.annotation.LooperMode.Mode;
 
 /**
  * Tests for layout parser for remote layout
  */
-@RunWith(RobolectricTestRunner.class)
-@LooperMode(Mode.PAUSED)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class DefaultLayoutProviderTest {
 
     private LauncherModelHelper mModelHelper;
@@ -56,16 +53,18 @@
     @Before
     public void setUp() {
         mModelHelper = new LauncherModelHelper();
-        mTargetContext = RuntimeEnvironment.application;
+        mTargetContext = mModelHelper.sandboxContext;
+    }
 
-        shadowOf(mTargetContext.getPackageManager())
-                .addActivityIfNotPresent(new ComponentName(TEST_PACKAGE, TEST_PACKAGE));
+    @After
+    public void tearDown() {
+        mModelHelper.destroy();
     }
 
     @Test
     public void testCustomProfileLoaded_with_icon_on_hotseat() throws Exception {
         writeLayoutAndLoad(new LauncherLayoutBuilder().atHotseat(0)
-                .putApp(TEST_PACKAGE, TEST_PACKAGE));
+                .putApp(TEST_PACKAGE, TEST_ACTIVITY));
 
         // Verify one item in hotseat
         assertEquals(1, mModelHelper.getBgDataModel().workspaceItems.size());
@@ -77,9 +76,9 @@
     @Test
     public void testCustomProfileLoaded_with_folder() throws Exception {
         writeLayoutAndLoad(new LauncherLayoutBuilder().atHotseat(0).putFolder(android.R.string.copy)
-                .addApp(TEST_PACKAGE, TEST_PACKAGE)
-                .addApp(TEST_PACKAGE, TEST_PACKAGE)
-                .addApp(TEST_PACKAGE, TEST_PACKAGE)
+                .addApp(TEST_PACKAGE, TEST_ACTIVITY)
+                .addApp(TEST_PACKAGE, TEST_ACTIVITY)
+                .addApp(TEST_PACKAGE, TEST_ACTIVITY)
                 .build());
 
         // Verify folder
@@ -92,9 +91,9 @@
     @Test
     public void testCustomProfileLoaded_with_folder_custom_title() throws Exception {
         writeLayoutAndLoad(new LauncherLayoutBuilder().atHotseat(0).putFolder("CustomFolder")
-                .addApp(TEST_PACKAGE, TEST_PACKAGE)
-                .addApp(TEST_PACKAGE, TEST_PACKAGE)
-                .addApp(TEST_PACKAGE, TEST_PACKAGE)
+                .addApp(TEST_PACKAGE, TEST_ACTIVITY)
+                .addApp(TEST_PACKAGE, TEST_ACTIVITY)
+                .addApp(TEST_PACKAGE, TEST_ACTIVITY)
                 .build());
 
         // Verify folder
@@ -112,12 +111,10 @@
         // Add a placeholder session info so that the widget exists
         SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
         params.setAppPackageName(pendingAppPkg);
+        params.setAppIcon(BitmapInfo.LOW_RES_ICON);
 
         PackageInstaller installer = mTargetContext.getPackageManager().getPackageInstaller();
-        int sessionId = installer.createSession(params);
-        SessionInfo sessionInfo = installer.getSessionInfo(sessionId);
-        setField(sessionInfo, "installerPackageName", "com.test");
-        setField(sessionInfo, "appIcon", BitmapInfo.LOW_RES_ICON);
+        installer.createSession(params);
 
         writeLayoutAndLoad(new LauncherLayoutBuilder().atWorkspace(0, 1, 0)
                 .putWidget(pendingAppPkg, "PlaceholderWidget", 2, 2));
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
similarity index 85%
rename from robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
rename to tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
index 8e49fae..005389e 100644
--- a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
@@ -30,26 +30,31 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
+import android.content.Intent;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Point;
 import android.os.Process;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.util.LauncherModelHelper;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
 
+import java.util.HashMap;
 import java.util.HashSet;
 
 /** Unit tests for {@link GridSizeMigrationTaskV2} */
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class GridSizeMigrationTaskV2Test {
 
     private LauncherModelHelper mModelHelper;
@@ -73,7 +78,7 @@
     @Before
     public void setUp() {
         mModelHelper = new LauncherModelHelper();
-        mContext = RuntimeEnvironment.application;
+        mContext = mModelHelper.sandboxContext;
         mDb = mModelHelper.provider.getDb();
 
         mValidPackages = new HashSet<>();
@@ -98,8 +103,13 @@
                 LauncherSettings.Favorites.TMP_TABLE);
     }
 
+    @After
+    public void tearDown() {
+        mModelHelper.destroy();
+    }
+
     @Test
-    public void testMigration() {
+    public void testMigration() throws Exception {
         int[] srcHotseatItems = {
                 mModelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI),
                 mModelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI),
@@ -124,29 +134,27 @@
         mIdp.numColumns = 4;
         mIdp.numRows = 4;
         GridSizeMigrationTaskV2.DbReader srcReader = new GridSizeMigrationTaskV2.DbReader(mDb,
-                LauncherSettings.Favorites.TMP_TABLE, mContext, mValidPackages,
-                srcHotseatItems.length);
+                LauncherSettings.Favorites.TMP_TABLE, mContext, mValidPackages);
         GridSizeMigrationTaskV2.DbReader destReader = new GridSizeMigrationTaskV2.DbReader(mDb,
-                LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages,
-                mIdp.numDatabaseHotseatIcons);
+                LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages);
         GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
                 destReader, mIdp.numDatabaseHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
-        task.migrate();
+        task.migrate(mIdp);
 
         // Check hotseat items
         Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
                 new String[]{LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.INTENT},
-                "container=" + CONTAINER_HOTSEAT, null, null, null);
+                "container=" + CONTAINER_HOTSEAT, null, LauncherSettings.Favorites.SCREEN, null);
         assertEquals(c.getCount(), mIdp.numDatabaseHotseatIcons);
         int screenIndex = c.getColumnIndex(LauncherSettings.Favorites.SCREEN);
         int intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
         c.moveToNext();
-        assertEquals(c.getInt(screenIndex), 1);
-        assertTrue(c.getString(intentIndex).contains(testPackage2));
-        c.moveToNext();
         assertEquals(c.getInt(screenIndex), 0);
         assertTrue(c.getString(intentIndex).contains(testPackage1));
         c.moveToNext();
+        assertEquals(c.getInt(screenIndex), 1);
+        assertTrue(c.getString(intentIndex).contains(testPackage2));
+        c.moveToNext();
         assertEquals(c.getInt(screenIndex), 2);
         assertTrue(c.getString(intentIndex).contains(testPackage3));
         c.moveToNext();
@@ -159,35 +167,24 @@
                 new String[]{LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
                         LauncherSettings.Favorites.INTENT},
                 "container=" + CONTAINER_DESKTOP, null, null, null);
-        assertEquals(c.getCount(), 6);
         intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
         int cellXIndex = c.getColumnIndex(LauncherSettings.Favorites.CELLX);
         int cellYIndex = c.getColumnIndex(LauncherSettings.Favorites.CELLY);
 
-        c.moveToNext();
-        assertTrue(c.getString(intentIndex).contains(testPackage7));
-        c.moveToNext();
-        assertTrue(c.getString(intentIndex).contains(testPackage6));
-        assertEquals(c.getInt(cellXIndex), 0);
-        assertEquals(c.getInt(cellYIndex), 3);
-        c.moveToNext();
-        assertTrue(c.getString(intentIndex).contains(testPackage10));
-        assertEquals(c.getInt(cellXIndex), 1);
-        assertEquals(c.getInt(cellYIndex), 3);
-        c.moveToNext();
-        assertTrue(c.getString(intentIndex).contains(testPackage5));
-        assertEquals(c.getInt(cellXIndex), 2);
-        assertEquals(c.getInt(cellYIndex), 3);
-        c.moveToNext();
-        assertTrue(c.getString(intentIndex).contains(testPackage9));
-        assertEquals(c.getInt(cellXIndex), 3);
-        assertEquals(c.getInt(cellYIndex), 3);
-        c.moveToNext();
-        assertTrue(c.getString(intentIndex).contains(testPackage8));
-        assertEquals(c.getInt(cellXIndex), 0);
-        assertEquals(c.getInt(cellYIndex), 2);
-
+        HashMap<String, Point> locMap = new HashMap<>();
+        while (c.moveToNext()) {
+            locMap.put(
+                    Intent.parseUri(c.getString(intentIndex), 0).getPackage(),
+                    new Point(c.getInt(cellXIndex), c.getInt(cellYIndex)));
+        }
         c.close();
+
+        assertEquals(locMap.size(), 6);
+        assertEquals(new Point(0, 2), locMap.get(testPackage8));
+        assertEquals(new Point(0, 3), locMap.get(testPackage6));
+        assertEquals(new Point(1, 3), locMap.get(testPackage10));
+        assertEquals(new Point(2, 3), locMap.get(testPackage5));
+        assertEquals(new Point(3, 3), locMap.get(testPackage9));
     }
 
     @Test
@@ -204,19 +201,17 @@
         mIdp.numColumns = 4;
         mIdp.numRows = 4;
         GridSizeMigrationTaskV2.DbReader srcReader = new GridSizeMigrationTaskV2.DbReader(mDb,
-                LauncherSettings.Favorites.TMP_TABLE, mContext, mValidPackages,
-                numSrcDatabaseHotseatIcons);
+                LauncherSettings.Favorites.TMP_TABLE, mContext, mValidPackages);
         GridSizeMigrationTaskV2.DbReader destReader = new GridSizeMigrationTaskV2.DbReader(mDb,
-                LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages,
-                mIdp.numDatabaseHotseatIcons);
+                LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages);
         GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
                 destReader, mIdp.numDatabaseHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
-        task.migrate();
+        task.migrate(mIdp);
 
         // Check hotseat items
         Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
                 new String[]{LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.INTENT},
-                "container=" + CONTAINER_HOTSEAT, null, null, null);
+                "container=" + CONTAINER_HOTSEAT, null, LauncherSettings.Favorites.SCREEN, null);
         assertEquals(c.getCount(), numSrcDatabaseHotseatIcons);
         int screenIndex = c.getColumnIndex(LauncherSettings.Favorites.SCREEN);
         int intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
@@ -247,24 +242,21 @@
                 mModelHelper.addItem(APP_ICON, 5, HOTSEAT, 0, 0, testPackage5, 5, TMP_CONTENT_URI),
         };
 
-        int numSrcDatabaseHotseatIcons = srcHotseatItems.length;
         mIdp.numDatabaseHotseatIcons = 4;
         mIdp.numColumns = 4;
         mIdp.numRows = 4;
         GridSizeMigrationTaskV2.DbReader srcReader = new GridSizeMigrationTaskV2.DbReader(mDb,
-                LauncherSettings.Favorites.TMP_TABLE, mContext, mValidPackages,
-                numSrcDatabaseHotseatIcons);
+                LauncherSettings.Favorites.TMP_TABLE, mContext, mValidPackages);
         GridSizeMigrationTaskV2.DbReader destReader = new GridSizeMigrationTaskV2.DbReader(mDb,
-                LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages,
-                mIdp.numDatabaseHotseatIcons);
+                LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages);
         GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
                 destReader, mIdp.numDatabaseHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
-        task.migrate();
+        task.migrate(mIdp);
 
         // Check hotseat items
         Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
                 new String[]{LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.INTENT},
-                "container=" + CONTAINER_HOTSEAT, null, null, null);
+                "container=" + CONTAINER_HOTSEAT, null, LauncherSettings.Favorites.SCREEN, null);
         assertEquals(c.getCount(), mIdp.numDatabaseHotseatIcons);
         int screenIndex = c.getColumnIndex(LauncherSettings.Favorites.SCREEN);
         int intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
diff --git a/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
similarity index 93%
rename from robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
rename to tests/src/com/android/launcher3/model/LoaderCursorTest.java
index 800311a..6444ef6 100644
--- a/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.model;
 
+import static androidx.test.InstrumentationRegistry.getContext;
+
 import static com.android.launcher3.LauncherSettings.Favorites.CELLX;
 import static com.android.launcher3.LauncherSettings.Favorites.CELLY;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER;
@@ -33,7 +35,7 @@
 import static com.android.launcher3.LauncherSettings.Favorites.SCREEN;
 import static com.android.launcher3.LauncherSettings.Favorites.TITLE;
 import static com.android.launcher3.LauncherSettings.Favorites._ID;
-import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -41,37 +43,37 @@
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
 
-import static org.robolectric.Shadows.shadowOf;
-
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.database.MatrixCursor;
 import android.os.Process;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.LauncherModelHelper;
 import com.android.launcher3.util.PackageManagerHelper;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.LooperMode;
-import org.robolectric.annotation.LooperMode.Mode;
 
 /**
  * Tests for {@link LoaderCursor}
  */
-@RunWith(RobolectricTestRunner.class)
-@LooperMode(Mode.PAUSED)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class LoaderCursorTest {
 
+    private LauncherModelHelper mModelHelper;
     private LauncherAppState mApp;
 
     private MatrixCursor mCursor;
@@ -82,7 +84,8 @@
 
     @Before
     public void setup() {
-        mContext = RuntimeEnvironment.application;
+        mModelHelper = new LauncherModelHelper();
+        mContext = mModelHelper.sandboxContext;
         mIDP = InvariantDeviceProfile.INSTANCE.get(mContext);
         mApp = LauncherAppState.getInstance(mContext);
 
@@ -97,6 +100,11 @@
         ums.allUsers.put(0, Process.myUserHandle());
     }
 
+    @After
+    public void tearDown() {
+        mModelHelper.destroy();
+    }
+
     private void initCursor(int itemType, String title) {
         mCursor.newRow()
                 .add(_ID, 1)
@@ -117,9 +125,7 @@
 
     @Test
     public void getAppShortcutInfo_dontAllowMissing_validComponent() throws Exception {
-        ComponentName cn = new ComponentName(TEST_PACKAGE, TEST_PACKAGE);
-        shadowOf(mContext.getPackageManager()).addActivityIfNotPresent(cn);
-
+        ComponentName cn = new ComponentName(getContext(), TEST_ACTIVITY);
         initCursor(ITEM_TYPE_APPLICATION, "");
         assertTrue(mLoaderCursor.moveToNext());
 
diff --git a/tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java b/tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
new file mode 100644
index 0000000..42c9f11
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2020 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.model;
+
+import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.spy;
+
+import android.os.Process;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.LauncherLayoutBuilder;
+import com.android.launcher3.util.LauncherModelHelper;
+import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.util.TestUtil;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Tests to verify multiple callbacks in Loader
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ModelMultiCallbacksTest {
+
+    private LauncherModelHelper mModelHelper;
+
+    @Before
+    public void setUp() {
+        mModelHelper = new LauncherModelHelper();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mModelHelper.destroy();
+        TestUtil.uninstallDummyApp();
+    }
+
+    @Test
+    public void testTwoCallbacks_loadedTogether() throws Exception {
+        setupWorkspacePages(3);
+
+        MyCallbacks cb1 = spy(MyCallbacks.class);
+        Executors.MAIN_EXECUTOR.execute(() -> mModelHelper.getModel().addCallbacksAndLoad(cb1));
+
+        waitForLoaderAndTempMainThread();
+        cb1.verifySynchronouslyBound(3);
+
+        // Add a new callback
+        cb1.reset();
+        MyCallbacks cb2 = spy(MyCallbacks.class);
+        cb2.mPageToBindSync = IntSet.wrap(2);
+        Executors.MAIN_EXECUTOR.execute(() -> mModelHelper.getModel().addCallbacksAndLoad(cb2));
+
+        waitForLoaderAndTempMainThread();
+        assertFalse(cb1.bindStarted);
+        cb2.verifySynchronouslyBound(3);
+
+        // Remove callbacks
+        cb1.reset();
+        cb2.reset();
+
+        // No effect on callbacks when removing an callback
+        Executors.MAIN_EXECUTOR.execute(() -> mModelHelper.getModel().removeCallbacks(cb2));
+        waitForLoaderAndTempMainThread();
+        assertNull(cb1.mPendingTasks);
+        assertNull(cb2.mPendingTasks);
+
+        // Reloading only loads registered callbacks
+        mModelHelper.getModel().startLoader();
+        waitForLoaderAndTempMainThread();
+        cb1.verifySynchronouslyBound(3);
+        assertNull(cb2.mPendingTasks);
+    }
+
+    @Test
+    public void testTwoCallbacks_receiveUpdates() throws Exception {
+        TestUtil.uninstallDummyApp();
+
+        setupWorkspacePages(1);
+
+        MyCallbacks cb1 = spy(MyCallbacks.class);
+        MyCallbacks cb2 = spy(MyCallbacks.class);
+        Executors.MAIN_EXECUTOR.execute(() -> mModelHelper.getModel().addCallbacksAndLoad(cb1));
+        Executors.MAIN_EXECUTOR.execute(() -> mModelHelper.getModel().addCallbacksAndLoad(cb2));
+        waitForLoaderAndTempMainThread();
+
+        assertTrue(cb1.allApps().contains(TEST_PACKAGE));
+        assertTrue(cb2.allApps().contains(TEST_PACKAGE));
+
+        // Install package 1
+        TestUtil.installDummyApp();
+        mModelHelper.getModel().onPackageAdded(TestUtil.DUMMY_PACKAGE, Process.myUserHandle());
+        waitForLoaderAndTempMainThread();
+        assertTrue(cb1.allApps().contains(TestUtil.DUMMY_PACKAGE));
+        assertTrue(cb2.allApps().contains(TestUtil.DUMMY_PACKAGE));
+
+        // Uninstall package 2
+        TestUtil.uninstallDummyApp();
+        mModelHelper.getModel().onPackageRemoved(TestUtil.DUMMY_PACKAGE, Process.myUserHandle());
+        waitForLoaderAndTempMainThread();
+        assertFalse(cb1.allApps().contains(TestUtil.DUMMY_PACKAGE));
+        assertFalse(cb2.allApps().contains(TestUtil.DUMMY_PACKAGE));
+
+        // Unregister a callback and verify updates no longer received
+        Executors.MAIN_EXECUTOR.execute(() -> mModelHelper.getModel().removeCallbacks(cb2));
+        TestUtil.installDummyApp();
+        mModelHelper.getModel().onPackageAdded(TestUtil.DUMMY_PACKAGE, Process.myUserHandle());
+        waitForLoaderAndTempMainThread();
+
+        // cb2 didn't get the update
+        assertTrue(cb1.allApps().contains(TestUtil.DUMMY_PACKAGE));
+        assertFalse(cb2.allApps().contains(TestUtil.DUMMY_PACKAGE));
+    }
+
+    private void waitForLoaderAndTempMainThread() throws Exception {
+        Executors.MAIN_EXECUTOR.submit(() -> { }).get();
+        Executors.MODEL_EXECUTOR.submit(() -> { }).get();
+        Executors.MAIN_EXECUTOR.submit(() -> { }).get();
+    }
+
+    private void setupWorkspacePages(int pageCount) throws Exception {
+        // Create a layout with 3 pages
+        LauncherLayoutBuilder builder = new LauncherLayoutBuilder();
+        for (int i = 0; i < pageCount; i++) {
+            builder.atWorkspace(1, 1, i).putApp(TEST_PACKAGE, TEST_PACKAGE);
+        }
+        mModelHelper.setupDefaultLayoutProvider(builder);
+    }
+
+    private abstract static class MyCallbacks implements Callbacks {
+
+        final List<ItemInfo> mItems = new ArrayList<>();
+        IntSet mPageToBindSync = IntSet.wrap(0);
+        IntSet mPageBoundSync = new IntSet();
+        RunnableList mPendingTasks;
+        AppInfo[] mAppInfos;
+        boolean bindStarted;
+
+        MyCallbacks() { }
+
+        @Override
+        public void startBinding() {
+            bindStarted = true;
+        }
+
+        @Override
+        public void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks) {
+            mPageBoundSync = boundPages;
+            mPendingTasks = pendingTasks;
+        }
+
+        @Override
+        public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons) {
+            mItems.addAll(shortcuts);
+        }
+
+        @Override
+        public void bindAllApplications(AppInfo[] apps, int flags) {
+            mAppInfos = apps;
+        }
+
+        @Override
+        public IntSet getPagesToBindSynchronously(IntArray orderedScreenIds) {
+            return mPageToBindSync;
+        }
+
+        public void reset() {
+            mItems.clear();
+            mPageBoundSync = new IntSet();
+            mPendingTasks = null;
+            mAppInfos = null;
+            bindStarted = false;
+        }
+
+        public void verifySynchronouslyBound(int totalItems) {
+            // Verify that the requested page is bound synchronously
+            assertTrue(bindStarted);
+            assertEquals(mPageToBindSync, mPageBoundSync);
+            assertEquals(mItems.size(), 1);
+            assertEquals(IntSet.wrap(mItems.get(0).screenId), mPageBoundSync);
+            assertNotNull(mPendingTasks);
+
+            // Verify that all other pages are bound properly
+            mPendingTasks.executeAllAndDestroy();
+            assertEquals(mItems.size(), totalItems);
+        }
+
+        public Set<String> allApps() {
+            return Arrays.stream(mAppInfos)
+                    .map(ai -> ai.getTargetComponent().getPackageName())
+                    .collect(Collectors.toSet());
+        }
+
+        public void verifyApps(String... apps) {
+            assertTrue(allApps().containsAll(Arrays.asList(apps)));
+        }
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
similarity index 87%
rename from robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
rename to tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
index 412ace0..519191e 100644
--- a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
+++ b/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
@@ -2,18 +2,19 @@
 
 import static org.junit.Assert.assertEquals;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
 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.pm.PackageInstallInfo;
 import com.android.launcher3.util.LauncherModelHelper;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.LooperMode;
-import org.robolectric.annotation.LooperMode.Mode;
 
 import java.util.Arrays;
 import java.util.HashSet;
@@ -21,8 +22,8 @@
 /**
  * Tests for {@link PackageInstallStateChangedTask}
  */
-@RunWith(RobolectricTestRunner.class)
-@LooperMode(Mode.PAUSED)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class PackageInstallStateChangedTaskTest {
 
     private LauncherModelHelper mModelHelper;
@@ -30,7 +31,12 @@
     @Before
     public void setup() throws Exception {
         mModelHelper = new LauncherModelHelper();
-        mModelHelper.initializeData("/package_install_state_change_task_data.txt");
+        mModelHelper.initializeData("package_install_state_change_task_data");
+    }
+
+    @After
+    public void tearDown() {
+        mModelHelper.destroy();
     }
 
     private PackageInstallStateChangedTask newTask(String pkg, int progress) {
@@ -66,7 +72,7 @@
         HashSet<Integer> updates = new HashSet<>(Arrays.asList(idsUpdated));
         for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) {
             if (info instanceof WorkspaceItemInfo) {
-                assertEquals(updates.contains(info.id) ? progress: 0,
+                assertEquals(updates.contains(info.id) ? progress: 100,
                         ((WorkspaceItemInfo) info).getProgressLevel());
             } else {
                 assertEquals(updates.contains(info.id) ? progress: -1,
diff --git a/robolectric_tests/src/com/android/launcher3/popup/PopupPopulatorTest.java b/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
similarity index 95%
rename from robolectric_tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
rename to tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
index 83bf7da..6764e09 100644
--- a/robolectric_tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
+++ b/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.popup;
 
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
 import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
 import static com.android.launcher3.popup.PopupPopulator.NUM_DYNAMIC;
 
@@ -27,10 +29,11 @@
 
 import android.content.pm.ShortcutInfo;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -39,7 +42,8 @@
 /**
  * Tests the sorting and filtering of shortcuts in {@link PopupPopulator}.
  */
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class PopupPopulatorTest {
 
     @Test
@@ -137,7 +141,7 @@
 
     private ShortcutInfo createInfo(boolean isStatic, int rank) {
         ShortcutInfo info = spy(new ShortcutInfo.Builder(
-                RuntimeEnvironment.application, generateId(isStatic, rank))
+                getApplicationContext(), generateId(isStatic, rank))
                 .setRank(rank)
                 .build());
         doReturn(isStatic).when(info).isDeclaredInManifest();
diff --git a/robolectric_tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
similarity index 92%
rename from robolectric_tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
rename to tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
index 4184d33..48305ee 100644
--- a/robolectric_tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
+++ b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -21,18 +21,21 @@
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.launcher3.LauncherProvider.DatabaseHelper;
 import com.android.launcher3.LauncherSettings.Favorites;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
 
 /**
  * Tests for {@link RestoreDbTask}
  */
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class RestoreDbTaskTest {
 
     @Test
@@ -95,7 +98,7 @@
         private final long mProfileId;
 
         MyDatabaseHelper(long profileId) {
-            super(RuntimeEnvironment.application, null, false);
+            super(InstrumentationRegistry.getInstrumentation().getTargetContext(), null, false);
             mProfileId = profileId;
         }
 
diff --git a/tests/src/com/android/launcher3/secondarydisplay/SDLauncherTest.java b/tests/src/com/android/launcher3/secondarydisplay/SDLauncherTest.java
new file mode 100644
index 0000000..fd86cf1
--- /dev/null
+++ b/tests/src/com/android/launcher3/secondarydisplay/SDLauncherTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.secondarydisplay;
+
+import static androidx.test.core.app.ActivityScenario.launch;
+
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.espresso.intent.Intents;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.MediumTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link SecondaryDisplayLauncher}
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class SDLauncherTest {
+
+    @Before
+    public void setUp() {
+        Intents.init();
+    }
+
+    @After
+    public void tearDown() {
+        Intents.release();
+    }
+
+    @Test
+    public void testAllAppsListOpens() {
+        ActivityScenario<SecondaryDisplayLauncher> launcher =
+                launch(SecondaryDisplayLauncher.class);
+        launcher.onActivity(l -> l.showAppDrawer(true));
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/settings/SettingsActivityTest.java b/tests/src/com/android/launcher3/settings/SettingsActivityTest.java
similarity index 96%
rename from robolectric_tests/src/com/android/launcher3/settings/SettingsActivityTest.java
rename to tests/src/com/android/launcher3/settings/SettingsActivityTest.java
index 3271812..1c205f0 100644
--- a/robolectric_tests/src/com/android/launcher3/settings/SettingsActivityTest.java
+++ b/tests/src/com/android/launcher3/settings/SettingsActivityTest.java
@@ -55,6 +55,7 @@
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -75,6 +76,7 @@
     }
 
     @Test
+    @Ignore  // b/199309785
     public void testSettings_aboutTap_launchesActivity() {
         ActivityScenario.launch(SettingsActivity.class);
         onView(withId(R.id.recycler_view)).perform(
@@ -88,6 +90,7 @@
     }
 
     @Test
+    @Ignore  // b/199309785
     public void testSettings_developerOptionsTap_launchesActivityWithFragment() {
         PluginPrefs.setHasPlugins(mApplicationContext);
         ActivityScenario.launch(SettingsActivity.class);
@@ -100,6 +103,7 @@
     }
 
     @Test
+    @Ignore  // b/199309785
     public void testSettings_aboutScreenIntent() {
         Bundle fragmentArgs = new Bundle();
         fragmentArgs.putString(ARG_PREFERENCE_ROOT, "about_screen");
@@ -114,6 +118,7 @@
     }
 
     @Test
+    @Ignore  // b/199309785
     public void testSettings_developerOptionsFragmentIntent() {
         Intent intent = new Intent(mApplicationContext, SettingsActivity.class)
                 .putExtra(EXTRA_FRAGMENT, DeveloperOptionsFragment.class.getName());
@@ -125,6 +130,7 @@
     }
 
     @Test
+    @Ignore  // b/199309785
     public void testSettings_intentWithUnknownFragment() {
         String fragmentClass = PreferenceFragmentCompat.class.getName();
         Intent intent = new Intent(mApplicationContext, SettingsActivity.class)
@@ -139,6 +145,7 @@
     }
 
     @Test
+    @Ignore  // b/199309785
     public void testSettings_backButtonFinishesActivity() {
         Bundle fragmentArgs = new Bundle();
         fragmentArgs.putString(ARG_PREFERENCE_ROOT, "about_screen");
diff --git a/tests/src/com/android/launcher3/testcomponent/OtherBaseTestingActivity.java b/tests/src/com/android/launcher3/testcomponent/OtherBaseTestingActivity.java
new file mode 100644
index 0000000..8bcab62
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/OtherBaseTestingActivity.java
@@ -0,0 +1,22 @@
+/*
+ * 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.testcomponent;
+
+/**
+ * Extension of BaseTestingActivity to help test many activities open at once.
+ */
+public class OtherBaseTestingActivity extends BaseTestingActivity {}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index dcb6dc1..0ffbeeb 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -36,7 +36,6 @@
 import android.os.Debug;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.StrictMode;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
@@ -52,7 +51,6 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.common.WidgetUtils;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.tapl.LauncherInstrumentation;
@@ -63,6 +61,7 @@
 import com.android.launcher3.util.LooperExecutor;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Wait;
+import com.android.launcher3.util.WidgetUtils;
 import com.android.launcher3.util.rule.FailureWatcher;
 import com.android.launcher3.util.rule.LauncherActivityRule;
 import com.android.launcher3.util.rule.ScreenRecordRule;
@@ -99,11 +98,9 @@
     public static final long DEFAULT_UI_TIMEOUT = 10000;
     private static final String TAG = "AbstractLauncherUiTest";
 
-    private static String sStrictmodeDetectedActivityLeak;
     private static boolean sDumpWasGenerated = false;
-    private static boolean sActivityLeakReported;
+    private static boolean sActivityLeakReported = false;
     private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
-    protected static final ActivityLeakTracker ACTIVITY_LEAK_TRACKER = new ActivityLeakTracker();
 
     protected LooperExecutor mMainThreadExecutor = MAIN_EXECUTOR;
     protected final UiDevice mDevice = UiDevice.getInstance(getInstrumentation());
@@ -112,45 +109,25 @@
     protected String mTargetPackage;
     private int mLauncherPid;
 
-    static {
-        if (TestHelpers.isInLauncherProcess()) {
-            StrictMode.VmPolicy.Builder builder =
-                    new StrictMode.VmPolicy.Builder()
-                            .penaltyLog()
-                            .penaltyListener(Runnable::run, violation -> {
-                                if (sStrictmodeDetectedActivityLeak == null) {
-                                    sStrictmodeDetectedActivityLeak = violation.toString() + ", "
-                                            + dumpHprofData() + ".";
-                                }
-                            });
-            StrictMode.setVmPolicy(builder.build());
-        }
-    }
-
     public static void checkDetectedLeaks(LauncherInstrumentation launcher) {
         if (sActivityLeakReported) return;
 
-        if (sStrictmodeDetectedActivityLeak != null) {
-            // Report from the test thread strictmode violations detected in the main thread.
-            sActivityLeakReported = true;
-            Assert.fail(sStrictmodeDetectedActivityLeak);
-        }
-
         // Check whether activity leak detector has found leaked activities.
-        Wait.atMost(AbstractLauncherUiTest::getActivityLeakErrorMessage,
+        Wait.atMost(() -> getActivityLeakErrorMessage(launcher),
                 () -> {
                     launcher.forceGc();
                     return MAIN_EXECUTOR.submit(
-                            () -> ACTIVITY_LEAK_TRACKER.noLeakedActivities()).get();
+                            () -> launcher.noLeakedActivities()).get();
                 }, DEFAULT_UI_TIMEOUT, launcher);
     }
 
-    private static String getActivityLeakErrorMessage() {
+    private static String getActivityLeakErrorMessage(LauncherInstrumentation launcher) {
         sActivityLeakReported = true;
-        return "Activity leak detector has found leaked activities, " + dumpHprofData() + ".";
+        return "Activity leak detector has found leaked activities, "
+                + dumpHprofData(launcher) + ".";
     }
 
-    public static String dumpHprofData() {
+    public static String dumpHprofData(LauncherInstrumentation launcher) {
         String result;
         if (sDumpWasGenerated) {
             Log.d("b/195319692", "dump has already been generated by another test",
@@ -176,8 +153,7 @@
                 result = "failed to save memory dump";
             }
         }
-        return result
-                + ". Full list of activities: " + ACTIVITY_LEAK_TRACKER.getActivitiesList();
+        return result + ". Full list of activities: " + launcher.getRootedActivitiesList();
     }
 
     protected AbstractLauncherUiTest() {
@@ -254,26 +230,12 @@
         return mDevice;
     }
 
-    private boolean hasSystemUiObject(String resId) {
-        return mDevice.hasObject(By.res(SYSTEMUI_PACKAGE, resId));
-    }
-
     @Before
     public void setUp() throws Exception {
         mLauncher.onTestStart();
-        Log.d(TAG, "Before disabling battery defender");
-        mDevice.executeShellCommand("setprop vendor.battery.defender.disable 1");
-        Log.d(TAG, "Before enabling stay awake");
-        mDevice.executeShellCommand("settings put global stay_on_while_plugged_in 3");
-        for (int i = 0; i < 10 && hasSystemUiObject("keyguard_status_view"); ++i) {
-            Log.d(TAG, "Before unlocking the phone");
-            mDevice.executeShellCommand("input keyevent 82");
-            mDevice.waitForIdle();
-        }
-        Assert.assertTrue("Keyguard still visible",
+        Assert.assertTrue("Keyguard is visible, which is likely caused by a crash in SysUI",
                 TestHelpers.wait(
                         Until.gone(By.res(SYSTEMUI_PACKAGE, "keyguard_status_view")), 60000));
-        Log.d(TAG, "Keyguard is not visible");
 
         final String launcherPackageName = mDevice.getLauncherPackageName();
         try {
@@ -300,8 +262,6 @@
         if (userManager != null) {
             for (UserHandle userHandle : userManager.getUserProfiles()) {
                 if (!userHandle.isSystem()) {
-                    Log.d(TestProtocol.WORK_PROFILE_REMOVED,
-                            "removing user " + userHandle.getIdentifier());
                     mDevice.executeShellCommand("pm remove-user " + userHandle.getIdentifier());
                 }
             }
diff --git a/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java b/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java
deleted file mode 100644
index 2db7472..0000000
--- a/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2020 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.ui;
-
-import android.app.Activity;
-import android.app.Application;
-import android.os.Bundle;
-
-import androidx.test.InstrumentationRegistry;
-
-import com.android.launcher3.tapl.TestHelpers;
-
-import java.util.WeakHashMap;
-import java.util.stream.Collectors;
-
-public class ActivityLeakTracker implements Application.ActivityLifecycleCallbacks {
-    private final WeakHashMap<Activity, Boolean> mActivities = new WeakHashMap<>();
-
-    private int mActivitiesCreated;
-
-    ActivityLeakTracker() {
-        if (!TestHelpers.isInLauncherProcess()) return;
-        final Application app =
-                (Application) InstrumentationRegistry.getTargetContext().getApplicationContext();
-        app.registerActivityLifecycleCallbacks(this);
-    }
-
-    public int getActivitiesCreated() {
-        return mActivitiesCreated;
-    }
-
-    @Override
-    public void onActivityCreated(Activity activity, Bundle bundle) {
-        mActivities.put(activity, true);
-        ++mActivitiesCreated;
-    }
-
-    @Override
-    public void onActivityStarted(Activity activity) {
-    }
-
-    @Override
-    public void onActivityResumed(Activity activity) {
-    }
-
-    @Override
-    public void onActivityPaused(Activity activity) {
-    }
-
-    @Override
-    public void onActivityStopped(Activity activity) {
-    }
-
-    @Override
-    public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
-    }
-
-    @Override
-    public void onActivityDestroyed(Activity activity) {
-    }
-
-    public boolean noLeakedActivities() {
-        for (Activity activity : mActivities.keySet()) {
-            if (activity.isDestroyed()) {
-                return false;
-            }
-        }
-
-        return mActivities.size() <= 2;
-    }
-
-    public String getActivitiesList() {
-        return mActivities.keySet().stream().map(a -> a.getClass().getSimpleName())
-                .collect(Collectors.joining(","));
-    }
-}
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 4dd44f4..881f50c 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -36,7 +36,6 @@
 import com.android.launcher3.tapl.AppIconMenuItem;
 import com.android.launcher3.tapl.Widgets;
 import com.android.launcher3.tapl.Workspace;
-import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.launcher3.views.OptionsPopupView;
 import com.android.launcher3.widget.picker.WidgetsFullSheet;
 import com.android.launcher3.widget.picker.WidgetsRecyclerView;
@@ -80,8 +79,12 @@
         assertTrue(message, failed);
     }
 
+    private int pagesPerScreen() {
+        return mLauncher.isTwoPanels() ? 2 : 1;
+    }
+
     private boolean isWorkspaceScrollable(Launcher launcher) {
-        return launcher.getWorkspace().getPageCount() > 1;
+        return launcher.getWorkspace().getPageCount() > pagesPerScreen();
     }
 
     private int getCurrentWorkspacePage(Launcher launcher) {
@@ -93,7 +96,6 @@
     }
 
     @Test
-    @ScreenRecord //b/187080582
     public void testDevicePressMenu() throws Exception {
         mDevice.pressMenu();
         mDevice.waitForIdle();
@@ -195,8 +197,9 @@
         workspace.ensureWorkspaceIsScrollable();
 
         executeOnLauncher(
-                launcher -> assertEquals("Ensuring workspace scrollable didn't switch to page #1",
-                        1, getCurrentWorkspacePage(launcher)));
+                launcher -> assertEquals(
+                        "Ensuring workspace scrollable didn't switch to next screen",
+                        pagesPerScreen(), getCurrentWorkspacePage(launcher)));
         executeOnLauncher(
                 launcher -> assertTrue("ensureScrollable didn't make workspace scrollable",
                         isWorkspaceScrollable(launcher)));
@@ -212,8 +215,8 @@
 
         workspace.flingForward();
         executeOnLauncher(
-                launcher -> assertEquals("Flinging forward didn't switch workspace to page #1",
-                        1, getCurrentWorkspacePage(launcher)));
+                launcher -> assertEquals("Flinging forward didn't switch workspace to next screen",
+                        pagesPerScreen(), getCurrentWorkspacePage(launcher)));
         assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL));
 
         // Test starting a workspace app.
diff --git a/tests/src/com/android/launcher3/ui/WorkProfileTest.java b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
new file mode 100644
index 0000000..fcb0b7f
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
@@ -0,0 +1,167 @@
+/*
+ * 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.ui;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.view.View;
+
+import com.android.launcher3.R;
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsPagedView;
+import com.android.launcher3.allapps.WorkAdapterProvider;
+import com.android.launcher3.allapps.WorkEduCard;
+import com.android.launcher3.allapps.WorkProfileManager;
+import com.android.launcher3.tapl.LauncherInstrumentation;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Objects;
+
+public class WorkProfileTest extends AbstractLauncherUiTest {
+
+    private static final int WORK_PAGE = AllAppsContainerView.AdapterHolder.WORK;
+
+    private int mProfileUserId;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        String output =
+                mDevice.executeShellCommand(
+                        "pm create-user --profileOf 0 --managed TestProfile");
+        assertTrue("Failed to create work profile", output.startsWith("Success"));
+
+        String[] tokens = output.split("\\s+");
+        mProfileUserId = Integer.parseInt(tokens[tokens.length - 1]);
+        mDevice.executeShellCommand("am start-user " + mProfileUserId);
+    }
+
+    @After
+    public void removeWorkProfile() throws Exception {
+        mDevice.executeShellCommand("pm remove-user " + mProfileUserId);
+    }
+
+    @After
+    public void resumeAppStoreUpdate() {
+        executeOnLauncher(launcher -> {
+            if (launcher == null || launcher.getAppsView() == null) {
+                return;
+            }
+            launcher.getAppsView().getAppsStore().disableDeferUpdates(DEFER_UPDATES_TEST);
+        });
+    }
+
+    private void waitForWorkTabSetup() {
+        waitForLauncherCondition("Work tab not setup", launcher -> {
+            if (launcher.getAppsView().getContentView() instanceof AllAppsPagedView) {
+                launcher.getAppsView().getAppsStore().enableDeferUpdates(DEFER_UPDATES_TEST);
+                return true;
+            }
+            return false;
+        }, LauncherInstrumentation.WAIT_TIME_MS);
+    }
+
+    @Test
+    public void workTabExists() {
+        mDevice.pressHome();
+        waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
+        waitForState("Launcher internal state didn't switch to Normal", () -> NORMAL);
+        executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
+        waitForState("Launcher internal state didn't switch to All Apps", () -> ALL_APPS);
+        waitForLauncherCondition("Personal tab is missing",
+                launcher -> launcher.getAppsView().isPersonalTabVisible(),
+                LauncherInstrumentation.WAIT_TIME_MS);
+        waitForLauncherCondition("Work tab is missing",
+                launcher -> launcher.getAppsView().isWorkTabVisible(),
+                LauncherInstrumentation.WAIT_TIME_MS);
+    }
+
+    @Test
+    public void toggleWorks() {
+        mDevice.pressHome();
+        waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
+        waitForState("Launcher internal state didn't switch to Normal", () -> NORMAL);
+        executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
+        waitForState("Launcher internal state didn't switch to All Apps", () -> ALL_APPS);
+
+        waitForWorkTabSetup();
+
+        executeOnLauncher(launcher -> {
+            AllAppsPagedView pagedView = (AllAppsPagedView) launcher.getAppsView().getContentView();
+            pagedView.setCurrentPage(WORK_PAGE);
+        });
+
+        WorkProfileManager manager = getFromLauncher(l -> l.getAppsView().getWorkManager());
+
+
+        waitForLauncherCondition("work profile initial state check failed", launcher ->
+                        manager.getWorkModeSwitch() != null
+                                && manager.getCurrentState() == WorkProfileManager.STATE_ENABLED
+                                && manager.getWorkModeSwitch().isEnabled(),
+                LauncherInstrumentation.WAIT_TIME_MS);
+
+        //start work profile toggle OFF test
+        executeOnLauncher(l -> l.getAppsView().getWorkManager().getWorkModeSwitch().performClick());
+
+        waitForLauncherCondition("Work profile toggle OFF failed", launcher -> {
+            manager.reset(); // pulls current state from system
+            return manager.getCurrentState() == WorkProfileManager.STATE_DISABLED;
+        }, LauncherInstrumentation.WAIT_TIME_MS);
+
+        // start work profile toggle ON test
+        executeOnLauncher(l -> {
+            AllAppsContainerView allApps = l.getAppsView();
+            assertEquals("Work tab is not focused", allApps.getCurrentPage(), WORK_PAGE);
+            View workPausedCard = allApps.getActiveRecyclerView().findViewHolderForAdapterPosition(
+                    0).itemView;
+            workPausedCard.findViewById(R.id.enable_work_apps).performClick();
+        });
+        waitForLauncherCondition("Work profile toggle ON failed", launcher -> {
+            manager.reset(); // pulls current state from system
+            return manager.getCurrentState() == WorkProfileManager.STATE_ENABLED;
+        }, LauncherInstrumentation.WAIT_TIME_MS);
+
+    }
+
+    @Test
+    public void testEdu() {
+        mDevice.pressHome();
+        waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
+        waitForState("Launcher internal state didn't switch to Normal", () -> NORMAL);
+        executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
+        waitForState("Launcher internal state didn't switch to All Apps", () -> ALL_APPS);
+        waitForWorkTabSetup();
+        executeOnLauncher(l -> {
+            l.getSharedPrefs().edit().putInt(WorkAdapterProvider.KEY_WORK_EDU_STEP, 0).commit();
+            ((AllAppsPagedView) l.getAppsView().getContentView()).setCurrentPage(WORK_PAGE);
+            l.getAppsView().getWorkManager().reset();
+        });
+
+        waitForLauncherCondition("Work profile education not shown",
+                l -> l.getAppsView().getActiveRecyclerView()
+                        .findViewHolderForAdapterPosition(0).itemView instanceof WorkEduCard,
+                LauncherInstrumentation.WAIT_TIME_MS);
+    }
+}
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 4978c01..2c9785c 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -15,28 +15,24 @@
  */
 package com.android.launcher3.ui.widget;
 
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
 
 import android.appwidget.AppWidgetManager;
 import android.content.Intent;
-import android.view.View;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.launcher3.Workspace;
-import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.tapl.Widget;
+import com.android.launcher3.tapl.WidgetResizeFrame;
 import com.android.launcher3.tapl.Widgets;
 import com.android.launcher3.testcomponent.WidgetConfigActivity;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.TestViewHelpers;
-import com.android.launcher3.util.Wait;
-import com.android.launcher3.util.Wait.Condition;
 import com.android.launcher3.util.rule.ShellCommandRule;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 
@@ -92,48 +88,26 @@
 
         // Drag widget to homescreen
         WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor();
-        widgets.getWidget(mWidgetInfo.getLabel(mTargetContext.getPackageManager()))
-                .dragToWorkspace(true, false);
+        WidgetResizeFrame resizeFrame =
+                widgets.getWidget(mWidgetInfo.getLabel(mTargetContext.getPackageManager()))
+                        .dragConfigWidgetToWorkspace(acceptConfig);
         // Widget id for which the config activity was opened
         mWidgetId = monitor.getWidgetId();
 
         // Verify that the widget id is valid and bound
         assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
 
-        setResult(acceptConfig);
         if (acceptConfig) {
-            // TODO(b/192655785) Assert widget resize frame is shown and then dismiss it.
-            Wait.atMost("", new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
-            assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
+            assertNotNull("Widget resize frame not shown after widget added", resizeFrame);
+            resizeFrame.dismiss();
+
+            final Widget widget =
+                    mLauncher.getWorkspace().tryGetWidget(mWidgetInfo.label, DEFAULT_UI_TIMEOUT);
+            assertNotNull("Widget not found on the workspace", widget);
         } else {
-            // Verify that the widget id is deleted.
-            Wait.atMost("", () -> mAppWidgetManager.getAppWidgetInfo(mWidgetId) == null,
-                    DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
-        }
-    }
-
-    private void setResult(boolean success) {
-        getInstrumentation().getTargetContext().sendBroadcast(
-                WidgetConfigActivity.getCommandIntent(WidgetConfigActivity.class,
-                        success ? "clickOK" : "clickCancel"));
-    }
-
-    /**
-     * Condition for searching widget id
-     */
-    private class WidgetSearchCondition implements Condition, Workspace.ItemOperator {
-
-        @Override
-        public boolean isTrue() throws Throwable {
-            return mMainThreadExecutor.submit(mActivityMonitor.itemExists(this)).get();
-        }
-
-        @Override
-        public boolean evaluate(ItemInfo info, View view) {
-            return info instanceof LauncherAppWidgetInfo &&
-                    ((LauncherAppWidgetInfo) info).providerName.getClassName().equals(
-                            mWidgetInfo.provider.getClassName()) &&
-                    ((LauncherAppWidgetInfo) info).appWidgetId == mWidgetId;
+            final Widget widget =
+                    mLauncher.getWorkspace().tryGetWidget(mWidgetInfo.label, DEFAULT_UI_TIMEOUT);
+            assertNull("Widget unexpectedly found on the workspace", widget);
         }
     }
 
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index dad4f2b..194ee4f 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -25,6 +25,7 @@
 
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.tapl.Widget;
+import com.android.launcher3.tapl.WidgetResizeFrame;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.TestViewHelpers;
 import com.android.launcher3.util.rule.ShellCommandRule;
@@ -53,19 +54,20 @@
         final LauncherAppWidgetProviderInfo widgetInfo =
                 TestViewHelpers.findWidgetProvider(this, false /* hasConfigureScreen */);
 
-        mLauncher.
+        WidgetResizeFrame resizeFrame = mLauncher.
                 getWorkspace().
                 openAllWidgets().
                 getWidget(widgetInfo.getLabel(mTargetContext.getPackageManager())).
-                dragToWorkspace(false, false);
-        // Dismiss widget resize frame.
-        mDevice.pressHome();
+                dragWidgetToWorkspace();
 
         assertTrue(mActivityMonitor.itemExists(
                 (info, view) -> info instanceof LauncherAppWidgetInfo &&
                         ((LauncherAppWidgetInfo) info).providerName.getClassName().equals(
                                 widgetInfo.provider.getClassName())).call());
 
+        assertNotNull("Widget resize frame not shown after widget add", resizeFrame);
+        resizeFrame.dismiss();
+
         final Widget widget = mLauncher.getWorkspace().tryGetWidget(widgetInfo.label,
                 DEFAULT_UI_TIMEOUT);
         assertNotNull("Widget not found on the workspace", widget);
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index 9c6c317..fa39ce0 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -17,7 +17,7 @@
 
 import static androidx.test.InstrumentationRegistry.getTargetContext;
 
-import static com.android.launcher3.common.WidgetUtils.createWidgetInfo;
+import static com.android.launcher3.util.WidgetUtils.createWidgetInfo;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 745dc22..ccbb662 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -30,7 +30,6 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.Workspace.ItemOperator;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -40,9 +39,9 @@
 import com.android.launcher3.testcomponent.AppWidgetWithConfig;
 import com.android.launcher3.testcomponent.RequestPinItemActivity;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
 import com.android.launcher3.util.Wait;
 import com.android.launcher3.util.Wait.Condition;
-import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.launcher3.util.rule.ShellCommandRule;
 
 import org.junit.Before;
@@ -78,7 +77,6 @@
     public void testEmpty() throws Throwable { /* needed while the broken tests are being fixed */ }
 
     @Test
-    @ScreenRecord  //b/192010616
     public void testPinWidgetNoConfig() throws Throwable {
         runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo &&
                 ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
@@ -87,7 +85,6 @@
     }
 
     @Test
-    @ScreenRecord  //b/192005114
     public void testPinWidgetNoConfig_customPreview() throws Throwable {
         // Command to set custom preview
         Intent command = RequestPinItemActivity.getCommandIntent(
diff --git a/tests/src/com/android/launcher3/util/ActivityContextWrapper.java b/tests/src/com/android/launcher3/util/ActivityContextWrapper.java
new file mode 100644
index 0000000..2618a2e
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/ActivityContextWrapper.java
@@ -0,0 +1,62 @@
+/*
+ * 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.content.Context;
+import android.content.ContextWrapper;
+import android.view.ContextThemeWrapper;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.BaseDragLayer;
+
+/**
+ * {@link ContextWrapper} with internal Launcher interface for testing
+ */
+public class ActivityContextWrapper extends ContextThemeWrapper implements ActivityContext {
+
+    private final DeviceProfile mProfile;
+    private final MyDragLayer mMyDragLayer;
+
+    public ActivityContextWrapper(Context base) {
+        super(base, android.R.style.Theme_DeviceDefault);
+        mProfile = InvariantDeviceProfile.INSTANCE.get(base).getDeviceProfile(base).copy(base);
+        mMyDragLayer = new MyDragLayer(this);
+    }
+
+    @Override
+    public BaseDragLayer getDragLayer() {
+        return mMyDragLayer;
+    }
+
+    @Override
+    public DeviceProfile getDeviceProfile() {
+        return mProfile;
+    }
+
+    private static class MyDragLayer extends BaseDragLayer<ActivityContextWrapper> {
+
+        MyDragLayer(Context context) {
+            super(context, null, 1);
+        }
+
+        @Override
+        public void recreateControllers() {
+            mControllers = new TouchController[0];
+        }
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/util/GridOccupancyTest.java b/tests/src/com/android/launcher3/util/GridOccupancyTest.java
similarity index 92%
rename from robolectric_tests/src/com/android/launcher3/util/GridOccupancyTest.java
rename to tests/src/com/android/launcher3/util/GridOccupancyTest.java
index 2f3fc37..b498077 100644
--- a/robolectric_tests/src/com/android/launcher3/util/GridOccupancyTest.java
+++ b/tests/src/com/android/launcher3/util/GridOccupancyTest.java
@@ -4,14 +4,17 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
 
 /**
  * Unit tests for {@link GridOccupancy}
  */
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class GridOccupancyTest {
 
     @Test
diff --git a/robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java b/tests/src/com/android/launcher3/util/IntArrayTest.java
similarity index 86%
rename from robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java
rename to tests/src/com/android/launcher3/util/IntArrayTest.java
index c08e198..a3c7007 100644
--- a/robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java
+++ b/tests/src/com/android/launcher3/util/IntArrayTest.java
@@ -17,14 +17,17 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
 
 /**
- * Robolectric unit tests for {@link IntArray}
+ * Unit tests for {@link IntArray}
  */
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class IntArrayTest {
 
     @Test
diff --git a/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java b/tests/src/com/android/launcher3/util/IntSetTest.java
similarity index 91%
rename from robolectric_tests/src/com/android/launcher3/util/IntSetTest.java
rename to tests/src/com/android/launcher3/util/IntSetTest.java
index 7a8c00b..cdb2891 100644
--- a/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java
+++ b/tests/src/com/android/launcher3/util/IntSetTest.java
@@ -21,14 +21,17 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
 
 /**
- * Robolectric unit tests for {@link IntSet}
+ * Unit tests for {@link IntSet}
  */
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class IntSetTest {
 
     @Test
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java b/tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java
similarity index 100%
rename from robolectric_tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java
rename to tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
similarity index 61%
rename from robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
rename to tests/src/com/android/launcher3/util/LauncherModelHelper.java
index 846e201..59966ee 100644
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -15,26 +15,40 @@
  */
 package com.android.launcher3.util;
 
-import static android.content.Intent.ACTION_CREATE_SHORTCUT;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static com.android.launcher3.LauncherSettings.Favorites.CONTENT_URI;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
-import static org.robolectric.Shadows.shadowOf;
 
 import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.res.Resources;
 import android.database.sqlite.SQLiteDatabase;
 import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
 import android.os.Process;
 import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+import android.util.ArrayMap;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.uiautomator.UiDevice;
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
@@ -45,27 +59,29 @@
 import com.android.launcher3.model.AllAppsList;
 import com.android.launcher3.model.BgDataModel;
 import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.model.ItemInstallQueue;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.shadows.ShadowLooperExecutor;
+import com.android.launcher3.testing.TestInformationProvider;
+import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
+import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
+import com.android.launcher3.widget.custom.CustomWidgetManager;
 
 import org.mockito.ArgumentCaptor;
-import org.robolectric.Robolectric;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadow.api.Shadow;
-import org.robolectric.shadows.ShadowContentResolver;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
 
 import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.lang.reflect.Field;
 import java.util.HashMap;
 import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.function.Function;
@@ -81,7 +97,9 @@
     public static final int APP_ICON = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
     public static final int SHORTCUT = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
     public static final int NO__ICON = -1;
-    public static final String TEST_PACKAGE = "com.android.launcher3.validpackage";
+
+    public static final String TEST_PACKAGE = testContext().getPackageName();
+    public static final String TEST_ACTIVITY = "com.android.launcher3.tests.Activity2";
 
     // Authority for providing a test default-workspace-layout data.
     private static final String TEST_PROVIDER_AUTHORITY =
@@ -90,21 +108,42 @@
     private static final int DEFAULT_GRID_SIZE = 4;
 
     private final HashMap<Class, HashMap<String, Field>> mFieldCache = new HashMap<>();
+    private final MockContentResolver mMockResolver = new MockContentResolver();
     public final TestLauncherProvider provider;
-    private final long mDefaultProfileId;
+    public final SanboxModelContext sandboxContext;
+
+    public final long defaultProfileId;
 
     private BgDataModel mDataModel;
     private AllAppsList mAllAppsList;
 
     public LauncherModelHelper() {
-        provider = Robolectric.setupContentProvider(TestLauncherProvider.class);
-        mDefaultProfileId = UserCache.INSTANCE.get(RuntimeEnvironment.application)
+        Context context = getApplicationContext();
+        // System settings cache content provider. Ensure that they are statically initialized
+        Settings.Secure.getString(context.getContentResolver(), "test");
+        Settings.System.getString(context.getContentResolver(), "test");
+        Settings.Global.getString(context.getContentResolver(), "test");
+
+        provider = new TestLauncherProvider();
+        sandboxContext = new SanboxModelContext();
+        defaultProfileId = UserCache.INSTANCE.get(sandboxContext)
                 .getSerialNumberForUser(Process.myUserHandle());
-        ShadowContentResolver.registerProviderInternal(LauncherProvider.AUTHORITY, provider);
+        setupProvider(LauncherProvider.AUTHORITY, provider);
+    }
+
+    protected void setupProvider(String authority, ContentProvider provider) {
+        ProviderInfo providerInfo = new ProviderInfo();
+        providerInfo.authority = authority;
+        providerInfo.applicationInfo = sandboxContext.getApplicationInfo();
+        provider.attachInfo(sandboxContext, providerInfo);
+        mMockResolver.addProvider(providerInfo.authority, provider);
+        doReturn(providerInfo)
+                .when(sandboxContext.mPm)
+                .resolveContentProvider(eq(authority), anyInt());
     }
 
     public LauncherModel getModel() {
-        return LauncherAppState.getInstance(RuntimeEnvironment.application).getModel();
+        return LauncherAppState.getInstance(sandboxContext).getModel();
     }
 
     public synchronized BgDataModel getBgDataModel() {
@@ -121,6 +160,28 @@
         return mAllAppsList;
     }
 
+    public void destroy() {
+        // When destroying the context, make sure that the model thread is blocked, so that no
+        // new jobs get posted while we are cleaning up
+        CountDownLatch l1 = new CountDownLatch(1);
+        CountDownLatch l2 = new CountDownLatch(1);
+        MODEL_EXECUTOR.execute(() -> {
+            l1.countDown();
+            waitOrThrow(l2);
+        });
+        waitOrThrow(l1);
+        sandboxContext.onDestroy();
+        l2.countDown();
+    }
+
+    private void waitOrThrow(CountDownLatch latch) {
+        try {
+            latch.await();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     /**
      * Synchronously executes the task and returns all the UI callbacks posted.
      */
@@ -161,13 +222,16 @@
      * Initializes mock data for the test.
      */
     public void initializeData(String resourceName) throws Exception {
-        Context targetContext = RuntimeEnvironment.application;
         BgDataModel bgDataModel = getBgDataModel();
         AllAppsList allAppsList = getAllAppsList();
 
         MODEL_EXECUTOR.submit(() -> {
+            // Copy apk from resources to a local file and install from there.
+            Resources resources = testContext().getResources();
+            int resId = resources.getIdentifier(
+                    resourceName, "raw", testContext().getPackageName());
             try (BufferedReader reader = new BufferedReader(new InputStreamReader(
-                    this.getClass().getResourceAsStream(resourceName)))) {
+                    resources.openRawResource(resId)))) {
                 String line;
                 HashMap<String, Class> classMap = new HashMap<>();
                 while ((line = reader.readLine()) != null) {
@@ -181,7 +245,7 @@
                             classMap.put(commands[1], Class.forName(commands[2]));
                             break;
                         case "bgItem":
-                            bgDataModel.addItem(targetContext,
+                            bgDataModel.addItem(sandboxContext,
                                     (ItemInfo) initItem(classMap.get(commands[1]), commands, 2),
                                     false);
                             break;
@@ -236,7 +300,7 @@
     }
 
     public int addItem(int type, int screen, int container, int x, int y) {
-        return addItem(type, screen, container, x, y, mDefaultProfileId, TEST_PACKAGE);
+        return addItem(type, screen, container, x, y, defaultProfileId, TEST_PACKAGE);
     }
 
     public int addItem(int type, int screen, int container, int x, int y, long profileId) {
@@ -244,12 +308,12 @@
     }
 
     public int addItem(int type, int screen, int container, int x, int y, String packageName) {
-        return addItem(type, screen, container, x, y, mDefaultProfileId, packageName);
+        return addItem(type, screen, container, x, y, defaultProfileId, packageName);
     }
 
     public int addItem(int type, int screen, int container, int x, int y, String packageName,
             int id, Uri contentUri) {
-        addItem(type, screen, container, x, y, mDefaultProfileId, packageName, id, contentUri);
+        addItem(type, screen, container, x, y, defaultProfileId, packageName, id, contentUri);
         return id;
     }
 
@@ -260,8 +324,7 @@
      */
     public int addItem(int type, int screen, int container, int x, int y, long profileId,
             String packageName) {
-        Context context = RuntimeEnvironment.application;
-        int id = LauncherSettings.Settings.call(context.getContentResolver(),
+        int id = LauncherSettings.Settings.call(sandboxContext.getContentResolver(),
                 LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
                 .getInt(LauncherSettings.Settings.EXTRA_VALUE);
         addItem(type, screen, container, x, y, profileId, packageName, id, CONTENT_URI);
@@ -270,8 +333,6 @@
 
     public void addItem(int type, int screen, int container, int x, int y, long profileId,
             String packageName, int id, Uri contentUri) {
-        Context context = RuntimeEnvironment.application;
-
         ContentValues values = new ContentValues();
         values.put(LauncherSettings.Favorites._ID, id);
         values.put(LauncherSettings.Favorites.CONTAINER, container);
@@ -295,7 +356,7 @@
             }
         }
 
-        context.getContentResolver().insert(contentUri, values);
+        sandboxContext.getContentResolver().insert(contentUri, values);
     }
 
     public int[][][] createGrid(int[][][] typeArray) {
@@ -303,12 +364,11 @@
     }
 
     public int[][][] createGrid(int[][][] typeArray, int startScreen) {
-        final Context context = RuntimeEnvironment.application;
-        LauncherSettings.Settings.call(context.getContentResolver(),
+        LauncherSettings.Settings.call(sandboxContext.getContentResolver(),
                 LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
-        LauncherSettings.Settings.call(context.getContentResolver(),
+        LauncherSettings.Settings.call(sandboxContext.getContentResolver(),
                 LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
-        return createGrid(typeArray, startScreen, mDefaultProfileId);
+        return createGrid(typeArray, startScreen, defaultProfileId);
     }
 
     /**
@@ -320,14 +380,13 @@
      * @return the same grid representation where each entry is the corresponding item id.
      */
     public int[][][] createGrid(int[][][] typeArray, int startScreen, long profileId) {
-        Context context = RuntimeEnvironment.application;
         int[][][] ids = new int[typeArray.length][][];
         for (int i = 0; i < typeArray.length; i++) {
             // Add screen to DB
             int screenId = startScreen + i;
 
             // Keep the screen id counter up to date
-            LauncherSettings.Settings.call(context.getContentResolver(),
+            LauncherSettings.Settings.call(sandboxContext.getContentResolver(),
                     LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
 
             ids[i] = new int[typeArray[i].length][];
@@ -353,69 +412,45 @@
      */
     public LauncherModelHelper setupDefaultLayoutProvider(LauncherLayoutBuilder builder)
             throws Exception {
-        Context context = RuntimeEnvironment.application;
-        InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(context);
+        InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(sandboxContext);
         idp.numRows = idp.numColumns = idp.numDatabaseHotseatIcons = DEFAULT_GRID_SIZE;
         idp.iconBitmapSize = DEFAULT_BITMAP_SIZE;
 
-        Settings.Secure.putString(context.getContentResolver(),
-                "launcher3.layout.provider", TEST_PROVIDER_AUTHORITY);
+        UiDevice.getInstance(getInstrumentation()).executeShellCommand(
+                "settings put secure launcher3.layout.provider " + TEST_PROVIDER_AUTHORITY);
+        ContentProvider cp = new TestInformationProvider() {
 
-        shadowOf(context.getPackageManager())
-                .addProviderIfNotPresent(new ComponentName("com.test", "Mock")).authority =
-                TEST_PROVIDER_AUTHORITY;
-
-        ByteArrayOutputStream bos = new ByteArrayOutputStream();
-        builder.build(new OutputStreamWriter(bos));
-        Uri layoutUri = LauncherProvider.getLayoutUri(TEST_PROVIDER_AUTHORITY, context);
-        shadowOf(context.getContentResolver()).registerInputStream(layoutUri,
-                new ByteArrayInputStream(bos.toByteArray()));
+            @Override
+            public ParcelFileDescriptor openFile(Uri uri, String mode)
+                    throws FileNotFoundException {
+                try {
+                    ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+                    AutoCloseOutputStream outputStream = new AutoCloseOutputStream(pipe[1]);
+                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+                    builder.build(new OutputStreamWriter(bos));
+                    outputStream.write(bos.toByteArray());
+                    outputStream.flush();
+                    outputStream.close();
+                    return pipe[0];
+                } catch (Exception e) {
+                    throw new FileNotFoundException(e.getMessage());
+                }
+            }
+        };
+        setupProvider(TEST_PROVIDER_AUTHORITY, cp);
         return this;
     }
 
     /**
-     * Simulates an apk install with a default main activity with same class and package name
-     */
-    public void installApp(String component) throws NameNotFoundException {
-        IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN);
-        filter.addCategory(Intent.CATEGORY_LAUNCHER);
-        installApp(component, component, filter);
-    }
-
-    /**
-     * Simulates a custom shortcut install
-     */
-    public void installCustomShortcut(String pkg, String clazz) throws NameNotFoundException {
-        installApp(pkg, clazz, new IntentFilter(ACTION_CREATE_SHORTCUT));
-    }
-
-    private void installApp(String pkg, String clazz, IntentFilter filter)
-            throws NameNotFoundException {
-        ShadowPackageManager spm = shadowOf(RuntimeEnvironment.application.getPackageManager());
-        ComponentName cn = new ComponentName(pkg, clazz);
-        spm.addActivityIfNotPresent(cn);
-
-        filter.addCategory(Intent.CATEGORY_DEFAULT);
-        spm.addIntentFilterForActivity(cn, filter);
-    }
-
-    /**
      * Loads the model in memory synchronously
      */
     public void loadModelSync() throws ExecutionException, InterruptedException {
-        // Since robolectric tests run on main thread, we run the loader-UI calls on a temp thread,
-        // so that we can wait appropriately for the loader to complete.
-        ShadowLooperExecutor sle = Shadow.extract(Executors.MAIN_EXECUTOR);
-        sle.setHandler(Executors.UI_HELPER_EXECUTOR.getHandler());
-
-        Callbacks mockCb = mock(Callbacks.class);
-        getModel().addCallbacksAndLoad(mockCb);
+        Callbacks mockCb = new Callbacks() { };
+        Executors.MAIN_EXECUTOR.submit(() -> getModel().addCallbacksAndLoad(mockCb)).get();
 
         Executors.MODEL_EXECUTOR.submit(() -> { }).get();
-        Executors.UI_HELPER_EXECUTOR.submit(() -> { }).get();
-
-        sle.setHandler(null);
-        getModel().removeCallbacks(mockCb);
+        Executors.MAIN_EXECUTOR.submit(() -> { }).get();
+        Executors.MAIN_EXECUTOR.submit(() -> getModel().removeCallbacks(mockCb)).get();
     }
 
     /**
@@ -437,4 +472,91 @@
             return mOpenHelper;
         }
     }
+
+    public static boolean deleteContents(File dir) {
+        File[] files = dir.listFiles();
+        boolean success = true;
+        if (files != null) {
+            for (File file : files) {
+                if (file.isDirectory()) {
+                    success &= deleteContents(file);
+                }
+                if (!file.delete()) {
+                    success = false;
+                }
+            }
+        }
+        return success;
+    }
+
+    public class SanboxModelContext extends SandboxContext {
+
+        private final ArrayMap<String, Object> mSpiedServices = new ArrayMap<>();
+        private final PackageManager mPm;
+        private final File mDbDir;
+
+        SanboxModelContext() {
+            super(ApplicationProvider.getApplicationContext(),
+                    UserCache.INSTANCE, InstallSessionHelper.INSTANCE,
+                    LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
+                    DisplayController.INSTANCE, CustomWidgetManager.INSTANCE,
+                    SettingsCache.INSTANCE, PluginManagerWrapper.INSTANCE,
+                    ItemInstallQueue.INSTANCE);
+            mPm = spy(getBaseContext().getPackageManager());
+            mDbDir = new File(getCacheDir(), UUID.randomUUID().toString());
+        }
+
+        public SanboxModelContext allow(MainThreadInitializedObject object) {
+            mAllowedObjects.add(object);
+            return this;
+        }
+
+        @Override
+        public File getDatabasePath(String name) {
+            if (!mDbDir.exists()) {
+                mDbDir.mkdirs();
+            }
+            return new File(mDbDir, name);
+        }
+
+        @Override
+        public ContentResolver getContentResolver() {
+            return mMockResolver;
+        }
+
+        @Override
+        public void onDestroy() {
+            if (deleteContents(mDbDir)) {
+                mDbDir.delete();
+            }
+            super.onDestroy();
+        }
+
+        @Override
+        public PackageManager getPackageManager() {
+            return mPm;
+        }
+
+        @Override
+        public Object getSystemService(String name) {
+            Object service = mSpiedServices.get(name);
+            return service != null ? service : super.getSystemService(name);
+        }
+
+        public <T> T spyService(Class<T> tClass) {
+            String name = getSystemServiceName(tClass);
+            Object service = mSpiedServices.get(name);
+            if (service != null) {
+                return (T) service;
+            }
+
+            T result = spy(getSystemService(tClass));
+            mSpiedServices.put(name, result);
+            return result;
+        }
+    }
+
+    private static Context testContext() {
+        return getInstrumentation().getContext();
+    }
 }
diff --git a/tests/src/com/android/launcher3/util/PackageUserKeyTest.java b/tests/src/com/android/launcher3/util/PackageUserKeyTest.java
new file mode 100644
index 0000000..99490eb
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/PackageUserKeyTest.java
@@ -0,0 +1,162 @@
+/*
+ * 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 static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.UserHandle;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.model.data.PackageItemInfo;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class PackageUserKeyTest {
+    @Rule
+    public ExpectedException exception = ExpectedException.none();
+
+    private static final String TEST_PACKAGE = "com.android.test.package";
+    private static final int CONVERSATIONS = 0;
+    private static final int WEATHER = 1;
+
+    @Test
+    public void fromPackageItemInfo_shouldCreateExpectedObject() {
+        PackageUserKey packageUserKey = PackageUserKey.fromPackageItemInfo(
+                new PackageItemInfo(TEST_PACKAGE, UserHandle.CURRENT));
+
+        assertThat(packageUserKey.mPackageName).isEqualTo(TEST_PACKAGE);
+        assertThat(packageUserKey.mWidgetCategory).isEqualTo(NO_CATEGORY);
+        assertThat(packageUserKey.mUser).isEqualTo(UserHandle.CURRENT);
+    }
+
+    @Test
+    public void constructor_packageNameAndUserHandle_shouldCreateExpectedObject() {
+        PackageUserKey packageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.CURRENT);
+
+        assertThat(packageUserKey.mPackageName).isEqualTo(TEST_PACKAGE);
+        assertThat(packageUserKey.mWidgetCategory).isEqualTo(NO_CATEGORY);
+        assertThat(packageUserKey.mUser).isEqualTo(UserHandle.CURRENT);
+    }
+
+    @Test
+    public void constructor_widgetCategoryAndUserHandle_shouldCreateExpectedObject() {
+        PackageUserKey packageUserKey = new PackageUserKey(CONVERSATIONS, UserHandle.CURRENT);
+
+        assertThat(packageUserKey.mPackageName).isEqualTo("");
+        assertThat(packageUserKey.mWidgetCategory).isEqualTo(CONVERSATIONS);
+        assertThat(packageUserKey.mUser).isEqualTo(UserHandle.CURRENT);
+    }
+
+    @Test
+    public void equals_sameObject_shouldReturnTrue() {
+        PackageUserKey packageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.CURRENT);
+        PackageUserKey otherPackageUserKey = packageUserKey;
+
+        assertThat(packageUserKey).isEqualTo(otherPackageUserKey);
+    }
+
+    @Test
+    public void equals_differentObjectSameContent_shouldReturnTrue() {
+        PackageUserKey packageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.CURRENT);
+        PackageUserKey otherPackageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.CURRENT);
+
+        assertThat(packageUserKey).isEqualTo(otherPackageUserKey);
+    }
+
+    @Test
+    public void equals_compareAgainstNull_shouldReturnFalse() {
+        PackageUserKey packageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.CURRENT);
+
+        assertThat(packageUserKey).isNotEqualTo(null);
+    }
+
+    @Test
+    public void equals_differentPackage_shouldReturnFalse() {
+        PackageUserKey packageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.CURRENT);
+        PackageUserKey otherPackageUserKey = new PackageUserKey(TEST_PACKAGE + "1",
+                UserHandle.CURRENT);
+
+        assertThat(packageUserKey).isNotEqualTo(otherPackageUserKey);
+    }
+
+
+    @Test
+    public void equals_differentCategory_shouldReturnFalse() {
+        PackageUserKey packageUserKey = new PackageUserKey(WEATHER, UserHandle.CURRENT);
+        PackageUserKey otherPackageUserKey = new PackageUserKey(CONVERSATIONS, UserHandle.CURRENT);
+
+        assertThat(packageUserKey).isNotEqualTo(otherPackageUserKey);
+    }
+
+    @Test
+    public void equals_differentUser_shouldReturnFalse() {
+        PackageUserKey packageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.of(1));
+        PackageUserKey otherPackageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.of(2));
+
+        assertThat(packageUserKey).isNotEqualTo(otherPackageUserKey);
+    }
+
+    @Test
+    public void hashCode_sameObject_shouldBeTheSame() {
+        PackageUserKey packageUserKey = new PackageUserKey(WEATHER, UserHandle.CURRENT);
+        PackageUserKey otherPackageUserKey = packageUserKey;
+
+        assertThat(packageUserKey.hashCode()).isEqualTo(otherPackageUserKey.hashCode());
+    }
+
+    @Test
+    public void hashCode_differentObjectSameContent_shouldBeTheSame() {
+        PackageUserKey packageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.CURRENT);
+        PackageUserKey otherPackageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.CURRENT);
+
+        assertThat(packageUserKey.hashCode()).isEqualTo(otherPackageUserKey.hashCode());
+    }
+
+    @Test
+    public void hashCode_differentPackage_shouldBeDifferent() {
+        PackageUserKey packageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.CURRENT);
+        PackageUserKey otherPackageUserKey = new PackageUserKey(TEST_PACKAGE + "1",
+                UserHandle.CURRENT);
+
+        assertThat(packageUserKey.hashCode()).isNotEqualTo(otherPackageUserKey.hashCode());
+    }
+
+
+    @Test
+    public void hashCode_differentCategory_shouldBeDifferent() {
+        PackageUserKey packageUserKey = new PackageUserKey(WEATHER, UserHandle.CURRENT);
+        PackageUserKey otherPackageUserKey = new PackageUserKey(CONVERSATIONS, UserHandle.CURRENT);
+
+        assertThat(packageUserKey.hashCode()).isNotEqualTo(otherPackageUserKey.hashCode());
+    }
+
+    @Test
+    public void hashCode_differentUser_shouldBeDifferent() {
+        PackageUserKey packageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.of(1));
+        PackageUserKey otherPackageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.of(2));
+
+        assertThat(packageUserKey.hashCode()).isNotEqualTo(otherPackageUserKey.hashCode());
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/ReflectionHelpers.java b/tests/src/com/android/launcher3/util/ReflectionHelpers.java
new file mode 100644
index 0000000..d89975d
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/ReflectionHelpers.java
@@ -0,0 +1,58 @@
+/*
+ * 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 java.lang.reflect.Field;
+
+public class ReflectionHelpers {
+
+    /**
+     * Reflectively get the value of a field.
+     *
+     * @param object Target object.
+     * @param fieldName The field name.
+     * @param <R> The return type.
+     * @return Value of the field on the object.
+     */
+    public static <R> R getField(Object object, String fieldName) {
+        try {
+            Field field = object.getClass().getDeclaredField(fieldName);
+            field.setAccessible(true);
+            return (R) field.get(object);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Reflectively set the value of a field.
+     *
+     * @param object Target object.
+     * @param fieldName The field name.
+     * @param fieldNewValue New value.
+     */
+    public static void setField(Object object, String fieldName, Object fieldNewValue) {
+        try {
+            Field field = object.getClass().getDeclaredField(fieldName);
+            field.setAccessible(true);
+            field.set(object, fieldNewValue);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+}
diff --git a/tests/src/com/android/launcher3/util/Wait.java b/tests/src/com/android/launcher3/util/Wait.java
index fe6143c..50bc32e 100644
--- a/tests/src/com/android/launcher3/util/Wait.java
+++ b/tests/src/com/android/launcher3/util/Wait.java
@@ -52,7 +52,7 @@
             throw new RuntimeException(t);
         }
         Log.d("Wait", "atMost: timed out: " + SystemClock.uptimeMillis());
-        launcher.checkForAnomaly();
+        launcher.checkForAnomaly(false, false);
         Assert.fail(message.get());
     }
 
diff --git a/tests/src_common/com/android/launcher3/common/WidgetUtils.java b/tests/src/com/android/launcher3/util/WidgetUtils.java
similarity index 82%
rename from tests/src_common/com/android/launcher3/common/WidgetUtils.java
rename to tests/src/com/android/launcher3/util/WidgetUtils.java
index 97500e3..6fc8491 100644
--- a/tests/src_common/com/android/launcher3/common/WidgetUtils.java
+++ b/tests/src/com/android/launcher3/util/WidgetUtils.java
@@ -13,19 +13,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.common;
+package com.android.launcher3.util;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
 
 import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.os.Bundle;
+import android.os.Process;
 
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.util.ContentWriter;
 import com.android.launcher3.widget.LauncherAppWidgetHost;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -102,4 +108,17 @@
         resolver.insert(LauncherSettings.Favorites.CONTENT_URI,
                 writer.getValues(targetContext));
     }
+
+
+    /**
+     * Creates a {@link AppWidgetProviderInfo} for the provided component name
+     */
+    public static AppWidgetProviderInfo createAppWidgetProviderInfo(ComponentName cn) {
+        AppWidgetProviderInfo info = AppWidgetManager.getInstance(getApplicationContext())
+                .getInstalledProvidersForPackage(
+                        getInstrumentation().getContext().getPackageName(), Process.myUserHandle())
+                .get(0);
+        info.provider = cn;
+        return info;
+    }
 }
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index dc59bdd..65aaa24 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -2,6 +2,7 @@
 
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
 
+import android.content.Context;
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor.AutoCloseInputStream;
 import android.util.Log;
@@ -13,7 +14,9 @@
 
 import org.junit.rules.TestWatcher;
 import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
 
+import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -29,6 +32,7 @@
     public FailureWatcher(UiDevice device, LauncherInstrumentation launcher) {
         mDevice = device;
         mLauncher = launcher;
+        Log.d("b/196820244", "FailureWatcher.ctor", new Exception());
     }
 
     @Override
@@ -38,17 +42,63 @@
     }
 
     @Override
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                boolean success = false;
+                try {
+                    Log.d("b/196820244", "Before evaluate");
+                    mDevice.executeShellCommand("cmd statusbar tracing start");
+                    FailureWatcher.super.apply(base, description).evaluate();
+                    Log.d("b/196820244", "After evaluate");
+                    success = true;
+                } finally {
+                    // Save artifact for Launcher Winscope trace.
+                    mDevice.executeShellCommand("cmd statusbar tracing stop");
+                    final Context nexusLauncherContext =
+                            getInstrumentation().getTargetContext()
+                                    .createPackageContext("com.google.android.apps.nexuslauncher",
+                                            0);
+                    final File launcherTrace =
+                            new File(nexusLauncherContext.getFilesDir(), "launcher_trace.pb");
+                    if (success) {
+                        mDevice.executeShellCommand("rm " + launcherTrace);
+                    } else {
+                        mDevice.executeShellCommand("mv " + launcherTrace + " "
+                                + diagFile(description, "LauncherWinscope", "pb"));
+                    }
+
+                    // Detect touch events coming from physical screen.
+                    if (mLauncher.hadNontestEvents()) {
+                        throw new AssertionError(
+                                "Launcher received events not sent by the test. This may mean "
+                                        + "that the touch screen of the lab device has sent false"
+                                        + " events. See the logcat for TaplEvents tag and look "
+                                        + "for events with deviceId != -1");
+                    }
+                }
+            }
+        };
+    }
+
+    @Override
     protected void failed(Throwable e, Description description) {
         onError(mDevice, description, e);
     }
 
+    static File diagFile(Description description, String prefix, String ext) {
+        return new File(getInstrumentation().getTargetContext().getFilesDir(),
+                prefix + "-" + description.getTestClass().getSimpleName() + "."
+                        + description.getMethodName() + "." + ext);
+    }
+
     public static void onError(UiDevice device, Description description, Throwable e) {
+        Log.d("b/196820244", "onError 1");
         if (device == null) return;
-        final File parentFile = getInstrumentation().getTargetContext().getFilesDir();
-        final File sceenshot = new File(parentFile,
-                "TestScreenshot-" + description.getMethodName() + ".png");
-        final File hierarchy = new File(parentFile,
-                "Hierarchy-" + description.getMethodName() + ".zip");
+        Log.d("b/196820244", "onError 2");
+        final File sceenshot = diagFile(description, "TestScreenshot", "png");
+        final File hierarchy = diagFile(description, "Hierarchy", "zip");
 
         // Dump window hierarchy
         try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(hierarchy))) {
@@ -61,13 +111,23 @@
             out.putNextEntry(new ZipEntry("visible_windows.zip"));
             dumpCommand("cmd window dump-visible-window-views", out);
             out.closeEntry();
-        } catch (IOException ex) { }
+        } catch (IOException ex) {
+        }
 
         Log.e(TAG, "Failed test " + description.getMethodName()
                 + ",\nscreenshot will be saved to " + sceenshot
                 + ",\nUI dump at: " + hierarchy
                 + " (use go/web-hv to open the dump file)", e);
         device.takeScreenshot(sceenshot);
+
+        // Dump accessibility hierarchy
+        try {
+            device.dumpWindowHierarchy(diagFile(description, "AccessibilityHierarchy", "uix"));
+        } catch (IOException ex) {
+            Log.e(TAG, "Failed to save accessibility hierarchy", ex);
+        }
+
+        dumpCommand("logcat -d -s TestRunner", diagFile(description, "FilteredLogcat", "txt"));
     }
 
     private static void dumpStringCommand(String cmd, OutputStream out) throws IOException {
@@ -75,6 +135,14 @@
         dumpCommand(cmd, out);
     }
 
+    private static void dumpCommand(String cmd, File out) {
+        try (BufferedOutputStream buffered = new BufferedOutputStream(
+                new FileOutputStream(out))) {
+            dumpCommand(cmd, buffered);
+        } catch (IOException ex) {
+        }
+    }
+
     private static void dumpCommand(String cmd, OutputStream out) throws IOException {
         try (AutoCloseInputStream in = new AutoCloseInputStream(getInstrumentation()
                 .getUiAutomation().executeShellCommand(cmd))) {
diff --git a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
index 6a6ec3e..2093682 100644
--- a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
@@ -18,7 +18,7 @@
 import android.app.Activity;
 
 import com.android.launcher3.Launcher;
-import com.android.launcher3.Workspace.ItemOperator;
+import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
 
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
diff --git a/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java b/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java
index 2b2fef4..08953fc 100644
--- a/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java
+++ b/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java
@@ -21,7 +21,6 @@
 
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
-import android.util.Log;
 
 import androidx.annotation.Nullable;
 import androidx.test.InstrumentationRegistry;
@@ -103,8 +102,6 @@
      */
     public static ShellCommandRule setDefaultLauncher() {
         final ActivityInfo launcher = getLauncherInMyProcess();
-        Log.d("b/187080582", "Launcher: " + new ComponentName(launcher.packageName, launcher.name)
-                .flattenToString());
         return new ShellCommandRule(getLauncherCommand(launcher), null, true, () ->
                 Assert.assertEquals("Setting default launcher failed",
                         new ComponentName(launcher.packageName, launcher.name)
diff --git a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
index 0e27b61..de36d5f 100644
--- a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
@@ -41,9 +41,7 @@
             Pattern.compile("^("
                     + "(?<local>(BuildFromAndroidStudio|"
                     + "([0-9]+|[A-Z])-eng\\.[a-z]+\\.[0-9]+\\.[0-9]+))|"
-                    + "(?<presubmit>([0-9]+|[A-Z])-P[0-9]+)|"
-                    + "(?<postsubmit>([0-9]+|[A-Z])-[0-9]+)|"
-                    + "(?<platform>[0-9]+|[A-Z])"
+                    + "(?<platform>[A-Z]([a-z]|[0-9])*)"
                     + ")$");
     private static final Pattern PLATFORM_BUILD =
             Pattern.compile("^("
diff --git a/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java b/tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
similarity index 93%
rename from robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
rename to tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
index a6f892c..b534a41 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
+++ b/tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.widget;
 
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -25,6 +27,9 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
 
@@ -32,17 +37,16 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public final class LauncherAppWidgetProviderInfoTest {
 
+    private static final int SPACE_SIZE = 10;
     private static final int CELL_SIZE = 50;
     private static final int NUM_OF_COLS = 4;
     private static final int NUM_OF_ROWS = 5;
@@ -51,8 +55,7 @@
 
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mContext = RuntimeEnvironment.application;
+        mContext = getApplicationContext();
     }
 
     @Test
@@ -157,7 +160,7 @@
         AppWidgetHostView.getDefaultPaddingForWidget(mContext, null, padding);
         int maxPadding = Math.max(Math.max(padding.left, padding.right),
                 Math.max(padding.top, padding.bottom));
-        dp.cellLayoutBorderSpacingPx = maxPadding + 1;
+        dp.cellLayoutBorderSpacePx.x = dp.cellLayoutBorderSpacePx.y = maxPadding + 1;
         Mockito.when(dp.shouldInsetWidgets()).thenReturn(true);
 
         LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
@@ -180,7 +183,7 @@
         AppWidgetHostView.getDefaultPaddingForWidget(mContext, null, padding);
         int maxPadding = Math.max(Math.max(padding.left, padding.right),
                 Math.max(padding.top, padding.bottom));
-        dp.cellLayoutBorderSpacingPx = maxPadding - 1;
+        dp.cellLayoutBorderSpacePx.x = dp.cellLayoutBorderSpacePx.y = maxPadding - 1;
         Mockito.when(dp.shouldInsetWidgets()).thenReturn(false);
         LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
         info.minWidth = CELL_SIZE * 3;
@@ -260,6 +263,8 @@
             return null;
         }).when(profile).getCellSize(any(Point.class));
         Mockito.when(profile.getCellSize()).thenReturn(new Point(CELL_SIZE, CELL_SIZE));
+        profile.cellLayoutBorderSpacePx = new Point(SPACE_SIZE, SPACE_SIZE);
+        Mockito.when(profile.shouldInsetWidgets()).thenReturn(true);
 
         InvariantDeviceProfile idp = new InvariantDeviceProfile();
         List<DeviceProfile> supportedProfiles = new ArrayList<>(idp.supportedProfiles);
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java
similarity index 94%
rename from robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java
rename to tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java
index b9f183c..b480a4c 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java
@@ -15,13 +15,16 @@
  */
 package com.android.launcher3.widget.picker;
 
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.robolectric.Shadows.shadowOf;
 
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
@@ -30,6 +33,8 @@
 import android.os.UserHandle;
 
 import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.icons.BitmapInfo;
@@ -48,15 +53,12 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
 
 import java.util.ArrayList;
 import java.util.List;
 
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public final class WidgetsDiffReporterTest {
     private static final String TEST_PACKAGE_PREFIX = "com.android.test";
     private static final WidgetListBaseRowEntryComparator COMPARATOR =
@@ -87,7 +89,7 @@
                 .getComponent().getPackageName())
                 .when(mIconCache).getTitleNoCache(any());
 
-        mContext = RuntimeEnvironment.application;
+        mContext = getApplicationContext();
         mWidgetsDiffReporter = new WidgetsDiffReporter(mIconCache, mAdapter);
         mHeaderA = createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "A",
                 /* appName= */ "A", /* numOfWidgets= */ 3);
@@ -286,22 +288,17 @@
 
     private PackageItemInfo createPackageItemInfo(String packageName, String appName,
             UserHandle userHandle) {
-        PackageItemInfo pInfo = new PackageItemInfo(packageName);
+        PackageItemInfo pInfo = new PackageItemInfo(packageName, userHandle);
         pInfo.title = appName;
-        pInfo.user = userHandle;
         pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
         return pInfo;
     }
 
     private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
-        ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
         ArrayList<WidgetItem> widgetItems = new ArrayList<>();
         for (int i = 0; i < numOfWidgets; i++) {
             ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
-            AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
-            widgetInfo.provider = cn;
-            ReflectionHelpers.setField(widgetInfo, "providerInfo",
-                    packageManager.addReceiverIfNotPresent(cn));
+            AppWidgetProviderInfo widgetInfo = createAppWidgetProviderInfo(cn);
 
             WidgetItem widgetItem = new WidgetItem(
                     LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
similarity index 87%
rename from robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
rename to tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
index c730fc0..4e0bdda 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
@@ -15,12 +15,13 @@
  */
 package com.android.launcher3.widget.picker;
 
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.isNull;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.verify;
-import static org.robolectric.Shadows.shadowOf;
 
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
@@ -31,6 +32,8 @@
 import android.view.LayoutInflater;
 
 import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.icons.BitmapInfo;
@@ -38,8 +41,9 @@
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.util.ActivityContextWrapper;
 import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.widget.DatabaseWidgetPreviewLoader;
+import com.android.launcher3.util.WidgetUtils;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 import com.android.launcher3.widget.model.WidgetsListContentEntry;
@@ -51,20 +55,20 @@
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
 
 import java.util.ArrayList;
 import java.util.List;
 
-@RunWith(RobolectricTestRunner.class)
+/**
+ * Unit tests for WidgetsListAdapter
+ * Note that all indices matching are shifted by 1 to account for the empty space at the start.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public final class WidgetsListAdapterTest {
     private static final String TEST_PACKAGE_PLACEHOLDER = "com.google.test";
 
     @Mock private LayoutInflater mMockLayoutInflater;
-    @Mock private DatabaseWidgetPreviewLoader mMockWidgetCache;
     @Mock private RecyclerView.AdapterDataObserver mListener;
     @Mock private IconCache mIconCache;
 
@@ -76,13 +80,13 @@
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mContext = RuntimeEnvironment.application;
+        mContext = new ActivityContextWrapper(getApplicationContext());
         mTestProfile = new InvariantDeviceProfile();
         mTestProfile.numRows = 5;
         mTestProfile.numColumns = 5;
         mUserHandle = Process.myUserHandle();
-        mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater, mMockWidgetCache,
-                mIconCache, null, null);
+        mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater,
+                mIconCache, () -> 0, null, null);
         mAdapter.registerAdapterDataObserver(mListener);
 
         doAnswer(invocation -> ((ComponentWithLabel) invocation.getArgument(0))
@@ -102,7 +106,7 @@
         mAdapter.setWidgets(generateSampleMap(1));
         mAdapter.setWidgets(generateSampleMap(2));
 
-        verify(mListener).onItemRangeInserted(eq(1), eq(1));
+        verify(mListener).onItemRangeInserted(eq(2), eq(1));
     }
 
     @Test
@@ -110,7 +114,7 @@
         mAdapter.setWidgets(generateSampleMap(2));
         mAdapter.setWidgets(generateSampleMap(1));
 
-        verify(mListener).onItemRangeRemoved(eq(1), eq(1));
+        verify(mListener).onItemRangeRemoved(eq(2), eq(1));
     }
 
     @Test
@@ -118,7 +122,7 @@
         mAdapter.setWidgets(generateSampleMap(1));
         mAdapter.setWidgets(generateSampleMap(1));
 
-        verify(mListener).onItemRangeChanged(eq(0), eq(1), isNull());
+        verify(mListener).onItemRangeChanged(eq(1), eq(1), isNull());
     }
 
     @Test
@@ -137,7 +141,7 @@
         // THEN the visible entries list becomes:
         // [com.google.test0, com.google.test1, com.google.test1 content, com.google.test2]
         // com.google.test.1 content is inserted into position 2.
-        verify(mListener).onItemRangeInserted(eq(2), eq(1));
+        verify(mListener).onItemRangeInserted(eq(3), eq(1));
     }
 
     @Test
@@ -165,7 +169,7 @@
         mAdapter.setWidgets(allEntries);
 
         // THEN the onItemRangeChanged is invoked for "com.google.test1 content" at index 2.
-        verify(mListener).onItemRangeChanged(eq(2), eq(1), isNull());
+        verify(mListener).onItemRangeChanged(eq(3), eq(1), isNull());
     }
 
     @Test
@@ -196,28 +200,38 @@
                 allAppsWithWidgets.get(6), allAppsWithWidgets.get(7));
         mAdapter.setWidgets(newList);
 
+        // Account for 1st items as empty space
         // Computation logic                           | [Intermediate list during computation]
         // THEN B <> C < 0, removed B from index 1     | [A, E]
-        verify(mListener).onItemRangeRemoved(/* positionStart= */ 1, /* itemCount= */ 1);
+        verify(mListener).onItemRangeRemoved(/* positionStart= */ 2, /* itemCount= */ 1);
         // THEN E <> C > 0, C inserted to index 1      | [A, C, E]
-        verify(mListener).onItemRangeInserted(/* positionStart= */ 1, /* itemCount= */ 1);
-        // THEN E <> D > 0, D inserted to index 2      | [A, C, D, E]
         verify(mListener).onItemRangeInserted(/* positionStart= */ 2, /* itemCount= */ 1);
+        // THEN E <> D > 0, D inserted to index 2      | [A, C, D, E]
+        verify(mListener).onItemRangeInserted(/* positionStart= */ 3, /* itemCount= */ 1);
         // THEN E <> null = -1, E deleted from index 3 | [A, C, D]
-        verify(mListener).onItemRangeRemoved(/* positionStart= */ 3, /* itemCount= */ 1);
+        verify(mListener).onItemRangeRemoved(/* positionStart= */ 4, /* itemCount= */ 1);
     }
 
     @Test
     public void setWidgetsOnSearch_expandedApp_shouldResetExpandedApp() {
         // GIVEN a list of widgets entries:
-        // [com.google.test0, com.google.test0 content,
-        //  com.google.test1, com.google.test1 content,
-        //  com.google.test2, com.google.test2 content]
-        // The visible widgets entries: [com.google.test0, com.google.test1, com.google.test2].
-        ArrayList<WidgetsListBaseEntry> allEntries = generateSampleMap(2);
+        // [Empty item
+        //  com.google.test0,
+        //  com.google.test0 content,
+        //  com.google.test1,
+        //  com.google.test1 content,
+        //  com.google.test2,
+        //  com.google.test2 content]
+        // The visible widgets entries:
+        // [Empty item,
+        // com.google.test0,
+        // com.google.test1,
+        // com.google.test2].
+        ArrayList<WidgetsListBaseEntry> allEntries = generateSampleMap(3);
         mAdapter.setWidgetsOnSearch(allEntries);
         // GIVEN com.google.test.1 header is expanded. The visible entries list becomes:
-        // [com.google.test0, com.google.test1, com.google.test1 content, com.google.test2]
+        // [Empty item, com.google.test0, com.google.test1, com.google.test1 content,
+        // com.google.test2]
         mAdapter.onHeaderClicked(/* showWidgets= */ true,
                 new PackageUserKey(TEST_PACKAGE_PLACEHOLDER + 1, mUserHandle));
         Mockito.reset(mListener);
@@ -226,9 +240,9 @@
         mAdapter.setWidgetsOnSearch(allEntries);
 
         // THEN expanded app is reset and the visible entries list becomes:
-        // [com.google.test0, com.google.test1, com.google.test2]
-        verify(mListener).onItemRangeChanged(eq(1), eq(1), isNull());
-        verify(mListener).onItemRangeRemoved(/* positionStart= */ 2, /* itemCount= */ 1);
+        // [Empty item, com.google.test0, com.google.test1, com.google.test2]
+        verify(mListener).onItemRangeChanged(eq(2), eq(1), isNull());
+        verify(mListener).onItemRangeRemoved(/* positionStart= */ 3, /* itemCount= */ 1);
     }
 
     /**
@@ -252,9 +266,8 @@
 
             List<WidgetItem> widgetItems = generateWidgetItems(packageName, /* numOfWidgets= */ 1);
 
-            PackageItemInfo pInfo = new PackageItemInfo(packageName);
+            PackageItemInfo pInfo = new PackageItemInfo(packageName, widgetItems.get(0).user);
             pInfo.title = pInfo.packageName;
-            pInfo.user = widgetItems.get(0).user;
             pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
 
             result.add(new WidgetsListHeaderEntry(pInfo, /* titleSectionName= */ "", widgetItems));
@@ -265,14 +278,10 @@
     }
 
     private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
-        ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
         ArrayList<WidgetItem> widgetItems = new ArrayList<>();
         for (int i = 0; i < numOfWidgets; i++) {
             ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
-            AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
-            widgetInfo.provider = cn;
-            ReflectionHelpers.setField(widgetInfo, "providerInfo",
-                    packageManager.addReceiverIfNotPresent(cn));
+            AppWidgetProviderInfo widgetInfo = WidgetUtils.createAppWidgetProviderInfo(cn);
 
             widgetItems.add(new WidgetItem(
                     LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
similarity index 70%
rename from robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
rename to tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
index 81b0c5f..211318c 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
@@ -15,23 +15,29 @@
  */
 package com.android.launcher3.widget.picker;
 
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.verify;
-import static org.robolectric.Shadows.shadowOf;
+
+import static java.util.Collections.EMPTY_LIST;
 
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.os.UserHandle;
 import android.view.LayoutInflater;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
-import com.android.launcher3.DeviceProfile;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.icons.BitmapInfo;
@@ -39,29 +45,23 @@
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.model.data.PackageItemInfo;
-import com.android.launcher3.testing.TestActivity;
+import com.android.launcher3.util.ActivityContextWrapper;
 import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.widget.DatabaseWidgetPreviewLoader;
+import com.android.launcher3.util.WidgetUtils;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.android.controller.ActivityController;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
 
 import java.util.ArrayList;
 import java.util.List;
 
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public final class WidgetsListHeaderViewHolderBinderTest {
     private static final String TEST_PACKAGE = "com.google.test";
     private static final String APP_NAME = "Test app";
@@ -69,65 +69,41 @@
     private Context mContext;
     private WidgetsListHeaderViewHolderBinder mViewHolderBinder;
     private InvariantDeviceProfile mTestProfile;
-    // Replace ActivityController with ActivityScenario, which is the recommended way for activity
-    // testing.
-    private ActivityController<TestActivity> mActivityController;
-    private TestActivity mTestActivity;
 
     @Mock
     private IconCache mIconCache;
     @Mock
-    private DeviceProfile mDeviceProfile;
-    @Mock
-    private DatabaseWidgetPreviewLoader mWidgetPreviewLoader;
-    @Mock
     private OnHeaderClickListener mOnHeaderClickListener;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mContext = RuntimeEnvironment.application;
+
+        mContext = new ActivityContextWrapper(getApplicationContext());
         mTestProfile = new InvariantDeviceProfile();
         mTestProfile.numRows = 5;
         mTestProfile.numColumns = 5;
 
-        mActivityController = Robolectric.buildActivity(TestActivity.class);
-        mTestActivity = mActivityController.setup().get();
-        mTestActivity.setDeviceProfile(mDeviceProfile);
-
         doAnswer(invocation -> {
             ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
             return componentWithLabel.getComponent().getShortClassName();
         }).when(mIconCache).getTitleNoCache(any());
-
-        WidgetsListAdapter widgetsListAdapter = new WidgetsListAdapter(mContext,
-                LayoutInflater.from(mTestActivity),
-                mWidgetPreviewLoader,
-                mIconCache,
-                /* iconClickListener= */ view -> {},
-                /* iconLongClickListener= */ view -> false);
         mViewHolderBinder = new WidgetsListHeaderViewHolderBinder(
-                LayoutInflater.from(mTestActivity),
+                LayoutInflater.from(mContext),
                 mOnHeaderClickListener,
-                new WidgetsListDrawableFactory(mTestActivity),
-                widgetsListAdapter);
-    }
-
-    @After
-    public void tearDown() {
-        mActivityController.destroy();
+                new WidgetsListDrawableFactory(mContext));
     }
 
     @Test
     public void bindViewHolder_appWith3Widgets_shouldShowTheCorrectAppNameAndSubtitle() {
         WidgetsListHeaderHolder viewHolder = mViewHolderBinder.newViewHolder(
-                new FrameLayout(mTestActivity));
+                new FrameLayout(mContext));
         WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
         WidgetsListHeaderEntry entry = generateSampleAppHeader(
                 APP_NAME,
                 TEST_PACKAGE,
                 /* numOfWidgets= */ 3);
-        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
+        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0, EMPTY_LIST);
 
         TextView appTitle = widgetsListHeader.findViewById(R.id.app_title);
         TextView appSubtitle = widgetsListHeader.findViewById(R.id.app_subtitle);
@@ -138,23 +114,23 @@
     @Test
     public void bindViewHolder_shouldAttachOnHeaderClickListener() {
         WidgetsListHeaderHolder viewHolder = mViewHolderBinder.newViewHolder(
-                new FrameLayout(mTestActivity));
+                new FrameLayout(mContext));
         WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
         WidgetsListHeaderEntry entry = generateSampleAppHeader(
                 APP_NAME,
                 TEST_PACKAGE,
                 /* numOfWidgets= */ 3);
 
-        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
+        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0, EMPTY_LIST);
         widgetsListHeader.callOnClick();
 
         verify(mOnHeaderClickListener).onHeaderClicked(eq(true),
-                eq(new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user)));
+                eq(PackageUserKey.fromPackageItemInfo(entry.mPkgItem)));
     }
 
     private WidgetsListHeaderEntry generateSampleAppHeader(String appName, String packageName,
             int numOfWidgets) {
-        PackageItemInfo appInfo = new PackageItemInfo(packageName);
+        PackageItemInfo appInfo = new PackageItemInfo(packageName, UserHandle.CURRENT);
         appInfo.title = appName;
         appInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
 
@@ -164,14 +140,10 @@
     }
 
     private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
-        ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
         ArrayList<WidgetItem> widgetItems = new ArrayList<>();
         for (int i = 0; i < numOfWidgets; i++) {
             ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
-            AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
-            widgetInfo.provider = cn;
-            ReflectionHelpers.setField(widgetInfo, "providerInfo",
-                    packageManager.addReceiverIfNotPresent(cn));
+            AppWidgetProviderInfo widgetInfo = WidgetUtils.createAppWidgetProviderInfo(cn);
 
             widgetItems.add(new WidgetItem(
                     LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
similarity index 70%
rename from robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
rename to tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
index a0ba7c3..66c2f36 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
@@ -15,23 +15,29 @@
  */
 package com.android.launcher3.widget.picker;
 
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.verify;
-import static org.robolectric.Shadows.shadowOf;
+
+import static java.util.Collections.EMPTY_LIST;
 
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.os.UserHandle;
 import android.view.LayoutInflater;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
-import com.android.launcher3.DeviceProfile;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.icons.BitmapInfo;
@@ -39,29 +45,23 @@
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.model.data.PackageItemInfo;
-import com.android.launcher3.testing.TestActivity;
+import com.android.launcher3.util.ActivityContextWrapper;
 import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.widget.DatabaseWidgetPreviewLoader;
+import com.android.launcher3.util.WidgetUtils;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.android.controller.ActivityController;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
 
 import java.util.ArrayList;
 import java.util.List;
 
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public final class WidgetsListSearchHeaderViewHolderBinderTest {
     private static final String TEST_PACKAGE = "com.google.test";
     private static final String APP_NAME = "Test app";
@@ -69,65 +69,40 @@
     private Context mContext;
     private WidgetsListSearchHeaderViewHolderBinder mViewHolderBinder;
     private InvariantDeviceProfile mTestProfile;
-    // Replace ActivityController with ActivityScenario, which is the recommended way for activity
-    // testing.
-    private ActivityController<TestActivity> mActivityController;
-    private TestActivity mTestActivity;
 
     @Mock
     private IconCache mIconCache;
     @Mock
-    private DeviceProfile mDeviceProfile;
-    @Mock
-    private DatabaseWidgetPreviewLoader mWidgetPreviewLoader;
-    @Mock
     private OnHeaderClickListener mOnHeaderClickListener;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mContext = RuntimeEnvironment.application;
+        mContext = new ActivityContextWrapper(getApplicationContext());
         mTestProfile = new InvariantDeviceProfile();
         mTestProfile.numRows = 5;
         mTestProfile.numColumns = 5;
 
-        mActivityController = Robolectric.buildActivity(TestActivity.class);
-        mTestActivity = mActivityController.setup().get();
-        mTestActivity.setDeviceProfile(mDeviceProfile);
-
         doAnswer(invocation -> {
             ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
             return componentWithLabel.getComponent().getShortClassName();
         }).when(mIconCache).getTitleNoCache(any());
-
-        WidgetsListAdapter widgetsListAdapter = new WidgetsListAdapter(mContext,
-                LayoutInflater.from(mTestActivity),
-                mWidgetPreviewLoader,
-                mIconCache,
-                /* iconClickListener= */ view -> {},
-                /* iconLongClickListener= */ view -> false);
         mViewHolderBinder = new WidgetsListSearchHeaderViewHolderBinder(
-                LayoutInflater.from(mTestActivity),
+                LayoutInflater.from(mContext),
                 mOnHeaderClickListener,
-                new WidgetsListDrawableFactory(mTestActivity),
-                widgetsListAdapter);
-    }
-
-    @After
-    public void tearDown() {
-        mActivityController.destroy();
+                new WidgetsListDrawableFactory(mContext));
     }
 
     @Test
     public void bindViewHolder_appWith3Widgets_shouldShowTheCorrectAppNameAndSubtitle() {
         WidgetsListSearchHeaderHolder viewHolder = mViewHolderBinder.newViewHolder(
-                new FrameLayout(mTestActivity));
+                new FrameLayout(mContext));
         WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
         WidgetsListSearchHeaderEntry entry = generateSampleSearchHeader(
                 APP_NAME,
                 TEST_PACKAGE,
                 /* numOfWidgets= */ 3);
-        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
+        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0, EMPTY_LIST);
 
         TextView appTitle = widgetsListHeader.findViewById(R.id.app_title);
         TextView appSubtitle = widgetsListHeader.findViewById(R.id.app_subtitle);
@@ -139,23 +114,23 @@
     @Test
     public void bindViewHolder_shouldAttachOnHeaderClickListener() {
         WidgetsListSearchHeaderHolder viewHolder = mViewHolderBinder.newViewHolder(
-                new FrameLayout(mTestActivity));
+                new FrameLayout(mContext));
         WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
         WidgetsListSearchHeaderEntry entry = generateSampleSearchHeader(
                 APP_NAME,
                 TEST_PACKAGE,
                 /* numOfWidgets= */ 3);
 
-        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
+        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0, EMPTY_LIST);
         widgetsListHeader.callOnClick();
 
         verify(mOnHeaderClickListener).onHeaderClicked(eq(true),
-                eq(new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user)));
+                eq(PackageUserKey.fromPackageItemInfo(entry.mPkgItem)));
     }
 
     private WidgetsListSearchHeaderEntry generateSampleSearchHeader(String appName,
             String packageName, int numOfWidgets) {
-        PackageItemInfo appInfo = new PackageItemInfo(packageName);
+        PackageItemInfo appInfo = new PackageItemInfo(packageName, UserHandle.CURRENT);
         appInfo.title = appName;
         appInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
 
@@ -165,14 +140,10 @@
     }
 
     private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
-        ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
         ArrayList<WidgetItem> widgetItems = new ArrayList<>();
         for (int i = 0; i < numOfWidgets; i++) {
             ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
-            AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
-            widgetInfo.provider = cn;
-            ReflectionHelpers.setField(widgetInfo, "providerInfo",
-                    packageManager.addReceiverIfNotPresent(cn));
+            AppWidgetProviderInfo widgetInfo = WidgetUtils.createAppWidgetProviderInfo(cn);
 
             widgetItems.add(new WidgetItem(
                     LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
similarity index 67%
rename from robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
rename to tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
index 8f9d132..7ec4d20 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
@@ -15,18 +15,20 @@
  */
 package com.android.launcher3.widget.picker;
 
-import static android.os.Looper.getMainLooper;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doAnswer;
-import static org.robolectric.Shadows.shadowOf;
+
+import static java.util.Collections.EMPTY_LIST;
 
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.os.UserHandle;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -35,7 +37,9 @@
 import android.widget.TableRow;
 import android.widget.TextView;
 
-import com.android.launcher3.DeviceProfile;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.icons.BitmapInfo;
@@ -43,30 +47,24 @@
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.model.data.PackageItemInfo;
-import com.android.launcher3.testing.TestActivity;
-import com.android.launcher3.widget.CachingWidgetPreviewLoader;
-import com.android.launcher3.widget.DatabaseWidgetPreviewLoader;
+import com.android.launcher3.util.ActivityContextWrapper;
+import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.WidgetUtils;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.WidgetCell;
 import com.android.launcher3.widget.model.WidgetsListContentEntry;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.android.controller.ActivityController;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
 
 import java.util.ArrayList;
 import java.util.List;
 
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public final class WidgetsListTableViewHolderBinderTest {
     private static final String TEST_PACKAGE = "com.google.test";
     private static final String APP_NAME = "Test app";
@@ -74,10 +72,6 @@
     private Context mContext;
     private WidgetsListTableViewHolderBinder mViewHolderBinder;
     private InvariantDeviceProfile mTestProfile;
-    // Replace ActivityController with ActivityScenario, which is the recommended way for activity
-    // testing.
-    private ActivityController<TestActivity> mActivityController;
-    private TestActivity mTestActivity;
 
     @Mock
     private OnLongClickListener mOnLongClickListener;
@@ -85,63 +79,42 @@
     private OnClickListener mOnIconClickListener;
     @Mock
     private IconCache mIconCache;
-    @Mock
-    private DatabaseWidgetPreviewLoader mWidgetPreviewLoader;
-    @Mock
-    private DeviceProfile mDeviceProfile;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mContext = RuntimeEnvironment.application;
+        mContext = new ActivityContextWrapper(getApplicationContext());
         mTestProfile = new InvariantDeviceProfile();
         mTestProfile.numRows = 5;
         mTestProfile.numColumns = 5;
 
-        mActivityController = Robolectric.buildActivity(TestActivity.class);
-        mTestActivity = mActivityController.setup().get();
-        mTestActivity.setDeviceProfile(mDeviceProfile);
-
         doAnswer(invocation -> {
             ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
             return componentWithLabel.getComponent().getShortClassName();
         }).when(mIconCache).getTitleNoCache(any());
 
-        WidgetsListAdapter widgetsListAdapter = new WidgetsListAdapter(mContext,
-                LayoutInflater.from(mTestActivity),
-                mWidgetPreviewLoader,
-                mIconCache,
-                /* iconClickListener= */ view -> {},
-                /* iconLongClickListener= */ view -> false);
         mViewHolderBinder = new WidgetsListTableViewHolderBinder(
-                LayoutInflater.from(mTestActivity),
+                LayoutInflater.from(mContext),
                 mOnIconClickListener,
                 mOnLongClickListener,
-                new CachingWidgetPreviewLoader(mWidgetPreviewLoader),
-                new WidgetsListDrawableFactory(mTestActivity),
-                widgetsListAdapter);
-    }
-
-    @After
-    public void tearDown() {
-        mActivityController.destroy();
+                new WidgetsListDrawableFactory(mContext));
     }
 
     @Test
-    public void bindViewHolder_appWith3Widgets_shouldHave3Widgets() {
+    public void bindViewHolder_appWith3Widgets_shouldHave3Widgets() throws Exception {
         WidgetsRowViewHolder viewHolder = mViewHolderBinder.newViewHolder(
-                new FrameLayout(mTestActivity));
+                new FrameLayout(mContext));
         WidgetsListContentEntry entry = generateSampleAppWithWidgets(
                 APP_NAME,
                 TEST_PACKAGE,
                 /* numOfWidgets= */ 3);
-        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
-        shadowOf(getMainLooper()).idle();
+        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0, EMPTY_LIST);
+        Executors.MAIN_EXECUTOR.submit(() -> { }).get();
 
         // THEN the table container has one row, which contains 3 widgets.
         // View:  .SampleWidget0 | .SampleWidget1 | .SampleWidget2
-        assertThat(viewHolder.mTableContainer.getChildCount()).isEqualTo(1);
-        TableRow row = (TableRow) viewHolder.mTableContainer.getChildAt(0);
+        assertThat(viewHolder.tableContainer.getChildCount()).isEqualTo(1);
+        TableRow row = (TableRow) viewHolder.tableContainer.getChildAt(0);
         assertThat(row.getChildCount()).isEqualTo(3);
         // Widget 0 label is .SampleWidget0.
         assertWidgetCellWithLabel(row.getChildAt(0), ".SampleWidget0");
@@ -153,24 +126,21 @@
 
     private WidgetsListContentEntry generateSampleAppWithWidgets(String appName, String packageName,
             int numOfWidgets) {
-        PackageItemInfo appInfo = new PackageItemInfo(packageName);
+        PackageItemInfo appInfo = new PackageItemInfo(packageName, UserHandle.CURRENT);
         appInfo.title = appName;
         appInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
 
         return new WidgetsListContentEntry(appInfo,
                 /* titleSectionName= */ "",
-                generateWidgetItems(packageName, numOfWidgets));
+                generateWidgetItems(packageName, numOfWidgets),
+                Integer.MAX_VALUE);
     }
 
     private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
-        ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
         ArrayList<WidgetItem> widgetItems = new ArrayList<>();
         for (int i = 0; i < numOfWidgets; i++) {
             ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
-            AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
-            widgetInfo.provider = cn;
-            ReflectionHelpers.setField(widgetInfo, "providerInfo",
-                    packageManager.addReceiverIfNotPresent(cn));
+            AppWidgetProviderInfo widgetInfo = WidgetUtils.createAppWidgetProviderInfo(cn);
 
             widgetItems.add(new WidgetItem(
                     LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java b/tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
similarity index 92%
rename from robolectric_tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
rename to tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
index 106cac0..d8f1f14 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
@@ -15,15 +15,21 @@
  */
 package com.android.launcher3.widget.picker.model;
 
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doAnswer;
-import static org.robolectric.Shadows.shadowOf;
 
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
-import android.content.Context;
+import android.os.UserHandle;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.icons.ComponentWithLabel;
@@ -38,21 +44,20 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
 
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public final class WidgetsListContentEntryTest {
     private static final String PACKAGE_NAME = "com.android.test";
     private static final String PACKAGE_NAME_2 = "com.android.test2";
-    private final PackageItemInfo mPackageItemInfo1 = new PackageItemInfo(PACKAGE_NAME);
-    private final PackageItemInfo mPackageItemInfo2 = new PackageItemInfo(PACKAGE_NAME_2);
+    private final PackageItemInfo mPackageItemInfo1 = new PackageItemInfo(PACKAGE_NAME,
+            UserHandle.CURRENT);
+    private final PackageItemInfo mPackageItemInfo2 = new PackageItemInfo(PACKAGE_NAME_2,
+            UserHandle.CURRENT);
     private final ComponentName mWidget1 = ComponentName.createRelative(PACKAGE_NAME, ".mWidget1");
     private final ComponentName mWidget2 = ComponentName.createRelative(PACKAGE_NAME, ".mWidget2");
     private final ComponentName mWidget3 = ComponentName.createRelative(PACKAGE_NAME, ".mWidget3");
@@ -60,7 +65,6 @@
 
     @Mock private IconCache mIconCache;
 
-    private Context mContext;
     private InvariantDeviceProfile mTestProfile;
 
     @Before
@@ -71,7 +75,6 @@
         mWidgetsToLabels.put(mWidget2, "Dog");
         mWidgetsToLabels.put(mWidget3, "Bird");
 
-        mContext = RuntimeEnvironment.application;
         mTestProfile = new InvariantDeviceProfile();
         mTestProfile.numRows = 5;
         mTestProfile.numColumns = 5;
@@ -242,17 +245,12 @@
         assertThat(widgetsListRowEntry1.equals(widgetsListRowEntry2)).isTrue();
     }
 
-
     private WidgetItem createWidgetItem(ComponentName componentName, int spanX, int spanY) {
         String label = mWidgetsToLabels.get(componentName);
-        ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
-        AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
-        widgetInfo.provider = componentName;
-        ReflectionHelpers.setField(widgetInfo, "providerInfo",
-                packageManager.addReceiverIfNotPresent(componentName));
+        AppWidgetProviderInfo widgetInfo = createAppWidgetProviderInfo(componentName);
 
         LauncherAppWidgetProviderInfo launcherAppWidgetProviderInfo =
-                LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo);
+                LauncherAppWidgetProviderInfo.fromProviderInfo(getApplicationContext(), widgetInfo);
         launcherAppWidgetProviderInfo.spanX = spanX;
         launcherAppWidgetProviderInfo.spanY = spanY;
         launcherAppWidgetProviderInfo.label = label;
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java b/tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
similarity index 89%
rename from robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
rename to tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
index 36b6f01..d812ab0 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
@@ -16,7 +16,10 @@
 
 package com.android.launcher3.widget.picker.search;
 
-import static android.os.Looper.getMainLooper;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo;
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
@@ -25,7 +28,6 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
-import static org.robolectric.Shadows.shadowOf;
 
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
@@ -33,6 +35,9 @@
 import android.graphics.Bitmap;
 import android.os.UserHandle;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.ComponentWithLabel;
@@ -52,16 +57,13 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class SimpleWidgetsSearchAlgorithmTest {
 
     @Mock private IconCache mIconCache;
@@ -82,7 +84,7 @@
     private SearchCallback<WidgetsListBaseEntry> mSearchCallback;
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         doAnswer(invocation -> {
             ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
@@ -91,7 +93,7 @@
         mTestProfile = new InvariantDeviceProfile();
         mTestProfile.numRows = 5;
         mTestProfile.numColumns = 5;
-        mContext = RuntimeEnvironment.application;
+        mContext = getApplicationContext();
 
         mCalendarHeaderEntry =
                 createWidgetsHeaderEntry("com.example.android.Calendar", "Calendar", 2);
@@ -102,8 +104,8 @@
         mClockHeaderEntry = createWidgetsHeaderEntry("com.example.android.Clock", "Clock", 3);
         mClockContentEntry = createWidgetsContentEntry("com.example.android.Clock", "Clock", 3);
 
-
-        mSimpleWidgetsSearchAlgorithm = new SimpleWidgetsSearchAlgorithm(mDataProvider);
+        mSimpleWidgetsSearchAlgorithm = MAIN_EXECUTOR.submit(
+                () -> new SimpleWidgetsSearchAlgorithm(mDataProvider)).get();
         doReturn(Collections.EMPTY_LIST).when(mDataProvider).getAllWidgets();
     }
 
@@ -156,13 +158,13 @@
     }
 
     @Test
-    public void doSearch_shouldInformCallback() {
+    public void doSearch_shouldInformCallback() throws Exception {
         doReturn(List.of(mCalendarHeaderEntry, mCalendarContentEntry, mCameraHeaderEntry,
                 mCameraContentEntry, mClockHeaderEntry, mClockContentEntry))
                 .when(mDataProvider)
                 .getAllWidgets();
         mSimpleWidgetsSearchAlgorithm.doSearch("Ca", mSearchCallback);
-        shadowOf(getMainLooper()).idle();
+        MAIN_EXECUTOR.submit(() -> { }).get();
         verify(mSearchCallback).onSearchResult(
                 matches("Ca"), argThat(a -> a != null && !a.isEmpty()));
     }
@@ -187,22 +189,17 @@
 
     private PackageItemInfo createPackageItemInfo(String packageName, String appName,
             UserHandle userHandle) {
-        PackageItemInfo pInfo = new PackageItemInfo(packageName);
+        PackageItemInfo pInfo = new PackageItemInfo(packageName, userHandle);
         pInfo.title = appName;
-        pInfo.user = userHandle;
         pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
         return pInfo;
     }
 
     private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
-        ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
         ArrayList<WidgetItem> widgetItems = new ArrayList<>();
         for (int i = 0; i < numOfWidgets; i++) {
             ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
-            AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
-            widgetInfo.provider = cn;
-            ReflectionHelpers.setField(widgetInfo, "providerInfo",
-                    packageManager.addReceiverIfNotPresent(cn));
+            AppWidgetProviderInfo widgetInfo = createAppWidgetProviderInfo(cn);
 
             WidgetItem widgetItem = new WidgetItem(
                     LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java b/tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
similarity index 83%
rename from robolectric_tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
rename to tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
index 7ac879a..583d37f 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
@@ -16,39 +16,38 @@
 
 package com.android.launcher3.widget.picker.search;
 
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
+import android.content.Context;
 import android.view.View;
 import android.widget.ImageButton;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
 import com.android.launcher3.ExtendedEditText;
 import com.android.launcher3.search.SearchAlgorithm;
-import com.android.launcher3.testing.TestActivity;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.android.controller.ActivityController;
 
 import java.util.ArrayList;
 
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class WidgetsSearchBarControllerTest {
 
     private WidgetsSearchBarController mController;
-    // TODO: Replace ActivityController with ActivityScenario, which is the recommended way for
-    // activity testing.
-    private ActivityController<TestActivity> mActivityController;
     private ExtendedEditText mEditText;
     private ImageButton mCancelButton;
     @Mock
@@ -59,20 +58,14 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mActivityController = Robolectric.buildActivity(TestActivity.class);
-        TestActivity testActivity = mActivityController.setup().get();
+        Context context = getApplicationContext();
 
-        mEditText = new ExtendedEditText(testActivity);
-        mCancelButton = new ImageButton(testActivity);
+        mEditText = new ExtendedEditText(context);
+        mCancelButton = new ImageButton(context);
         mController = new WidgetsSearchBarController(
                 mSearchAlgorithm, mEditText, mCancelButton, mSearchModeListener);
     }
 
-    @After
-    public void tearDown() {
-        mActivityController.destroy();
-    }
-
     @Test
     public void onSearchResult_shouldInformSearchModeListener() {
         ArrayList<WidgetsListBaseEntry> entries = new ArrayList<>();
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java b/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
similarity index 73%
rename from robolectric_tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
rename to tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
index 56d7d68..715dcca 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
@@ -15,11 +15,14 @@
  */
 package com.android.launcher3.widget.picker.util;
 
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doAnswer;
-import static org.robolectric.Shadows.shadowOf;
 
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
@@ -29,6 +32,9 @@
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.icons.ComponentWithLabel;
 import com.android.launcher3.icons.IconCache;
@@ -42,15 +48,12 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
 
 import java.util.ArrayList;
 import java.util.List;
 
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public final class WidgetsTableUtilsTest {
     private static final String TEST_PACKAGE = "com.google.test";
 
@@ -73,7 +76,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        mContext = RuntimeEnvironment.application;
+        mContext = getApplicationContext();
 
         mTestProfile = new InvariantDeviceProfile();
         mTestProfile.numRows = 5;
@@ -89,12 +92,13 @@
 
 
     @Test
-    public void groupWidgetItemsIntoTable_widgetsOnly_maxSpansPerRow5_shouldGroupWidgetsInTable() {
+    public void groupWidgetItemsIntoTableWithReordering_widgetsOnly_maxSpansPerRow5_shouldGroupWidgetsInTable() {
         List<WidgetItem> widgetItems = List.of(mWidget4x4, mWidget2x3, mWidget1x1, mWidget2x4,
                 mWidget2x2);
 
-        List<ArrayList<WidgetItem>> widgetItemInTable = WidgetsTableUtils.groupWidgetItemsIntoTable(
-                widgetItems, /* maxSpansPerRow= */ 5);
+        List<ArrayList<WidgetItem>> widgetItemInTable =
+                WidgetsTableUtils.groupWidgetItemsIntoTableWithReordering(
+                        widgetItems, /* maxSpansPerRow= */ 5);
 
         // Row 0: 1x1, 2x2
         // Row 1: 2x3, 2x4
@@ -106,12 +110,13 @@
     }
 
     @Test
-    public void groupWidgetItemsIntoTable_widgetsOnly_maxSpansPerRow4_shouldGroupWidgetsInTable() {
+    public void groupWidgetItemsIntoTableWithReordering_widgetsOnly_maxSpansPerRow4_shouldGroupWidgetsInTable() {
         List<WidgetItem> widgetItems = List.of(mWidget4x4, mWidget2x3, mWidget1x1, mWidget2x4,
                 mWidget2x2);
 
-        List<ArrayList<WidgetItem>> widgetItemInTable = WidgetsTableUtils.groupWidgetItemsIntoTable(
-                widgetItems, /* maxSpansPerRow= */ 4);
+        List<ArrayList<WidgetItem>> widgetItemInTable =
+                WidgetsTableUtils.groupWidgetItemsIntoTableWithReordering(
+                        widgetItems, /* maxSpansPerRow= */ 4);
 
         // Row 0: 1x1, 2x2
         // Row 1: 2x3,
@@ -125,12 +130,13 @@
     }
 
     @Test
-    public void groupWidgetItemsIntoTable_mixItems_maxSpansPerRow4_shouldGroupWidgetsInTable() {
+    public void groupWidgetItemsIntoTableWithReordering_mixItems_maxSpansPerRow4_shouldGroupWidgetsInTable() {
         List<WidgetItem> widgetItems = List.of(mWidget4x4, mShortcut3, mWidget2x3, mShortcut1,
                 mWidget1x1, mShortcut2, mWidget2x4, mWidget2x2);
 
-        List<ArrayList<WidgetItem>> widgetItemInTable = WidgetsTableUtils.groupWidgetItemsIntoTable(
-                widgetItems, /* maxSpansPerRow= */ 4);
+        List<ArrayList<WidgetItem>> widgetItemInTable =
+                WidgetsTableUtils.groupWidgetItemsIntoTableWithReordering(
+                        widgetItems, /* maxSpansPerRow= */ 4);
 
         // Row 0: 1x1, 2x2
         // Row 1: 2x3,
@@ -145,6 +151,24 @@
         assertThat(widgetItemInTable.get(4)).containsExactly(mShortcut3, mShortcut2, mShortcut1);
     }
 
+    @Test
+    public void groupWidgetItemsIntoTableWithoutReordering_shouldMaintainTheOrder() {
+        List<WidgetItem> widgetItems =
+                List.of(mWidget4x4, mWidget2x3, mWidget1x1, mWidget2x4, mWidget2x2);
+
+        List<ArrayList<WidgetItem>> widgetItemInTable =
+                WidgetsTableUtils.groupWidgetItemsIntoTableWithoutReordering(
+                        widgetItems, /* maxSpansPerRow= */ 5);
+
+        // Row 0: 4x4
+        // Row 1: 2x3, 1x1
+        // Row 2: 2x4, 2x2
+        assertThat(widgetItemInTable).hasSize(3);
+        assertThat(widgetItemInTable.get(0)).containsExactly(mWidget4x4);
+        assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x3, mWidget1x1);
+        assertThat(widgetItemInTable.get(2)).containsExactly(mWidget2x4, mWidget2x2);
+    }
+
     private void initTestWidgets() {
         List<Point> widgetSizes = List.of(new Point(1, 1), new Point(2, 2), new Point(2, 3),
                 new Point(2, 4), new Point(4, 4));
@@ -152,16 +176,14 @@
         ArrayList<WidgetItem> widgetItems = new ArrayList<>();
         widgetSizes.stream().forEach(
                 widgetSize -> {
-                    ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
-                    AppWidgetProviderInfo info = new AppWidgetProviderInfo();
-                    info.provider = ComponentName.createRelative(TEST_PACKAGE,
-                            ".WidgetProvider_" + widgetSize.x + "x" + widgetSize.y);
+                    AppWidgetProviderInfo info = createAppWidgetProviderInfo(
+                            ComponentName.createRelative(
+                                    TEST_PACKAGE,
+                                    ".WidgetProvider_" + widgetSize.x + "x" + widgetSize.y));
                     LauncherAppWidgetProviderInfo widgetInfo =
                             LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
                     widgetInfo.spanX = widgetSize.x;
                     widgetInfo.spanY = widgetSize.y;
-                    ReflectionHelpers.setField(widgetInfo, "providerInfo",
-                            packageManager.addReceiverIfNotPresent(widgetInfo.provider));
                     widgetItems.add(new WidgetItem(widgetInfo, mTestProfile, mIconCache));
                 }
         );
diff --git a/tests/src_common/README.md b/tests/src_common/README.md
deleted file mode 100644
index 2bc9e73..0000000
--- a/tests/src_common/README.md
+++ /dev/null
@@ -1 +0,0 @@
-Common source code used by both android tests and robolectric tests
\ No newline at end of file
diff --git a/tests/src_disabled/WorkTabTest.java b/tests/src_disabled/WorkTabTest.java
deleted file mode 100644
index bfacc74..0000000
--- a/tests/src_disabled/WorkTabTest.java
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright 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.ui;
-
-import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import android.os.Process;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
-import android.widget.TextView;
-
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.allapps.AllAppsPagedView;
-import com.android.launcher3.allapps.WorkModeSwitch;
-import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.tapl.LauncherInstrumentation;
-import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.views.WorkEduView;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicInteger;
-
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class WorkTabTest extends AbstractLauncherUiTest {
-
-    private int mProfileUserId;
-
-    private static final int WORK_PAGE = AllAppsContainerView.AdapterHolder.WORK;
-
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        String output =
-                mDevice.executeShellCommand(
-                        "pm create-user --profileOf 0 --managed TestProfile");
-        assertTrue("Failed to create work profile", output.startsWith("Success"));
-
-        String[] tokens = output.split("\\s+");
-        mProfileUserId = Integer.parseInt(tokens[tokens.length - 1]);
-        Log.d(TestProtocol.WORK_PROFILE_REMOVED, "Created new user uid" + mProfileUserId);
-        mDevice.executeShellCommand("am start-user " + mProfileUserId);
-    }
-
-    @After
-    public void removeWorkProfile() throws Exception {
-        Log.d(TestProtocol.WORK_PROFILE_REMOVED, "(teardown) removing uid" + mProfileUserId,
-                new Exception());
-        mDevice.executeShellCommand("pm remove-user " + mProfileUserId);
-    }
-
-    @After
-    public void resumeAppStoreUpdate() {
-        executeOnLauncher(launcher -> {
-            if (launcher == null || launcher.getAppsView() == null) {
-                return;
-            }
-            launcher.getAppsView().getAppsStore().disableDeferUpdates(DEFER_UPDATES_TEST);
-            Log.d(TestProtocol.WORK_PROFILE_REMOVED, "resuming AppStore updates");
-        });
-    }
-
-    @Ignore("b/182844465")
-    @Test
-    public void workTabExists() {
-        mDevice.pressHome();
-        waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
-        executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
-        waitForState("Launcher internal state didn't switch to All Apps", () -> ALL_APPS);
-        waitForLauncherCondition("Personal tab is missing",
-                launcher -> launcher.getAppsView().isPersonalTabVisible(), 60000);
-        waitForLauncherCondition("Work tab is missing",
-                launcher -> launcher.getAppsView().isWorkTabVisible(), 60000);
-    }
-
-    @Ignore("b/182844465")
-    @Test
-    public void toggleWorks() {
-        mDevice.pressHome();
-        waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
-        executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
-        waitForState("Launcher internal state didn't switch to All Apps", () -> ALL_APPS);
-        getOnceNotNull("Apps view did not bind",
-                launcher -> launcher.getAppsView().getWorkModeSwitch(), 60000);
-
-        UserManager userManager = getFromLauncher(l -> l.getSystemService(UserManager.class));
-        assertEquals(2, userManager.getUserProfiles().size());
-        UserHandle workProfile = getFromLauncher(l -> {
-            UserHandle myHandle = Process.myUserHandle();
-            List<UserHandle> userProfiles = userManager.getUserProfiles();
-            return userProfiles.get(0) == myHandle ? userProfiles.get(1) : userProfiles.get(0);
-        });
-
-        waitForLauncherCondition("work profile can't be turned off",
-                l -> userManager.requestQuietModeEnabled(true, workProfile));
-
-        assertTrue(userManager.isQuietModeEnabled(workProfile));
-        executeOnLauncher(launcher -> {
-            WorkModeSwitch wf = launcher.getAppsView().getWorkModeSwitch();
-            ((AllAppsPagedView) launcher.getAppsView().getContentView()).snapToPageImmediately(
-                    AllAppsContainerView.AdapterHolder.WORK);
-            wf.toggle();
-        });
-        waitForLauncherCondition("Work toggle did not work",
-                l -> l.getSystemService(UserManager.class).isQuietModeEnabled(workProfile));
-    }
-
-    @Ignore("b/182844465")
-    @Test
-    public void testWorkEduFlow() {
-        mDevice.pressHome();
-        waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
-        executeOnLauncher(launcher -> launcher.getSharedPrefs().edit().remove(
-                WorkEduView.KEY_WORK_EDU_STEP).remove(
-                WorkEduView.KEY_LEGACY_WORK_EDU_SEEN).commit());
-
-        waitForLauncherCondition("Work tab not setup", launcher -> {
-            if (launcher.getAppsView().getContentView() instanceof AllAppsPagedView) {
-                launcher.getAppsView().getAppsStore().enableDeferUpdates(DEFER_UPDATES_TEST);
-                return true;
-            }
-            return false;
-        }, LauncherInstrumentation.WAIT_TIME_MS);
-
-        executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
-        WorkEduView workEduView = getEduView();
-        // verify personal app edu is seen first and click "next"
-        executeOnLauncher(l -> {
-            assertEquals(((TextView) workEduView.findViewById(R.id.content_text)).getText(),
-                    l.getResources().getString(R.string.work_profile_edu_personal_apps));
-            workEduView.findViewById(R.id.proceed).callOnClick();
-        });
-
-        AtomicInteger attempt = new AtomicInteger(0);
-        // verify work edu is seen next
-        waitForLauncherCondition("Launcher did not show the next edu screen", l -> {
-            Log.d(TestProtocol.WORK_PROFILE_REMOVED,
-                    "running test attempt" + attempt.getAndIncrement());
-            if (!(l.getAppsView().getContentView() instanceof AllAppsPagedView)) {
-                Log.d(TestProtocol.WORK_PROFILE_REMOVED, "Work tab not setup. Skipping test");
-                return false;
-            }
-            if (((AllAppsPagedView) l.getAppsView().getContentView()).getCurrentPage()
-                    != WORK_PAGE) {
-                Log.d(TestProtocol.WORK_PROFILE_REMOVED, "Work page not highlighted");
-            }
-            return ((TextView) workEduView.findViewById(R.id.content_text)).getText().equals(
-                    l.getResources().getString(R.string.work_profile_edu_work_apps));
-        });
-    }
-
-    @Ignore("b/182844465")
-    @Test
-    public void testWorkEduIntermittent() {
-        mDevice.pressHome();
-        waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
-        executeOnLauncher(launcher -> launcher.getSharedPrefs().edit().remove(
-                WorkEduView.KEY_WORK_EDU_STEP).remove(
-                WorkEduView.KEY_LEGACY_WORK_EDU_SEEN).commit());
-
-
-        waitForLauncherCondition("Work tab not setup",
-                launcher -> launcher.getAppsView().getContentView() instanceof AllAppsPagedView,
-                60000);
-        executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
-
-        // verify personal app edu is seen
-        getEduView();
-
-        // dismiss personal edu
-        mDevice.pressHome();
-        waitForState("Launcher did not go home", () -> NORMAL);
-
-        // open work tab
-        executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
-        waitForState("Launcher did not switch to all apps", () -> ALL_APPS);
-        waitForLauncherCondition("Work tab not setup",
-                launcher -> launcher.getAppsView().getContentView() instanceof AllAppsPagedView,
-                60000);
-
-        executeOnLauncher(launcher -> {
-            AllAppsPagedView pagedView = (AllAppsPagedView) launcher.getAppsView().getContentView();
-            pagedView.setCurrentPage(WORK_PAGE);
-        });
-
-        WorkEduView workEduView = getEduView();
-
-        // verify work tab edu is shown
-        waitForLauncherCondition("Launcher did not show the next edu screen",
-                l -> ((TextView) workEduView.findViewById(R.id.content_text)).getText().equals(
-                        l.getResources().getString(R.string.work_profile_edu_work_apps)));
-    }
-
-
-    private WorkEduView getEduView() {
-        waitForLauncherCondition("Edu did not show", l -> {
-            DragLayer dragLayer = l.getDragLayer();
-            return dragLayer.getChildCount() > 0 && dragLayer.getChildAt(
-                    dragLayer.getChildCount() - 1) instanceof WorkEduView;
-        }, 6000);
-        return getFromLauncher(launcher -> (WorkEduView) launcher.getDragLayer().getChildAt(
-                launcher.getDragLayer().getChildCount() - 1));
-    }
-
-}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 1cb6b2d..78301e4 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -135,6 +135,7 @@
                                         .collect(Collectors.toList()),
                                 mLauncher.getVisibleBounds(searchBox).bottom
                                         - mLauncher.getVisibleBounds(allAppsContainer).top);
+                        verifyActiveContainer();
                         final int newScroll = getAllAppsScroll();
                         mLauncher.assertTrue(
                                 "Scrolled in a wrong direction in AllApps: from " + scroll + " to "
@@ -144,7 +145,6 @@
                         mLauncher.assertTrue(
                                 "Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
                                 ++attempts <= MAX_SCROLL_ATTEMPTS);
-                        verifyActiveContainer();
                         scroll = newScroll;
                     }
                 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 4b1610e..b290bb1 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -68,6 +68,10 @@
     }
 
     protected boolean zeroButtonToOverviewGestureStartsInLauncher() {
+        return mLauncher.isTablet();
+    }
+
+    protected boolean zeroButtonToOverviewGestureStateTransitionWhileHolding() {
         return false;
     }
 
@@ -90,21 +94,32 @@
 
                 mLauncher.sendPointer(
                         downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
-                mLauncher.executeAndWaitForLauncherEvent(
-                        () -> mLauncher.movePointer(
-                                downTime,
-                                downTime,
-                                ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION,
-                                start,
-                                end,
-                                gestureScope),
-                        event -> TestProtocol.PAUSE_DETECTED_MESSAGE.equals(event.getClassName()),
-                        () -> "Pause wasn't detected", "swiping and holding");
-                mLauncher.runToState(
-                        () -> mLauncher.sendPointer(
-                                downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, end,
-                                gestureScope),
-                        OVERVIEW_STATE_ORDINAL, "sending UP event");
+                Runnable swipeAndHold = () -> mLauncher.movePointer(
+                        downTime,
+                        downTime,
+                        ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION,
+                        start,
+                        end,
+                        gestureScope);
+                String swipeAndHoldAction = "swiping and holding";
+                Runnable up = () -> mLauncher.sendPointer(
+                        downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, end,
+                        gestureScope);
+                String upAction = "sending UP event";
+                if (zeroButtonToOverviewGestureStateTransitionWhileHolding()) {
+                    mLauncher.runToState(swipeAndHold, OVERVIEW_STATE_ORDINAL, swipeAndHoldAction);
+                    try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(upAction)) {
+                        up.run();
+                    }
+                } else {
+                    mLauncher.executeAndWaitForLauncherEvent(
+                            swipeAndHold,
+                            event -> TestProtocol.PAUSE_DETECTED_MESSAGE.equals(
+                                    event.getClassName()),
+                            () -> "Pause wasn't detected",
+                            swipeAndHoldAction);
+                    mLauncher.runToState(up, OVERVIEW_STATE_ORDINAL, upAction);
+                }
                 break;
             }
 
@@ -134,9 +149,15 @@
             }
 
             case THREE_BUTTON:
+                if (mLauncher.isTablet()) {
+                    mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN,
+                            LauncherInstrumentation.EVENT_TOUCH_DOWN);
+                    mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN,
+                            LauncherInstrumentation.EVENT_TOUCH_UP);
+                }
                 mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
                 mLauncher.runToState(
-                        () -> mLauncher.waitForSystemUiObject("recent_apps").click(),
+                        () -> mLauncher.waitForNavigationUiObject("recent_apps").click(),
                         OVERVIEW_STATE_ORDINAL, "clicking Recents button");
                 break;
         }
@@ -224,11 +245,23 @@
 
                 case THREE_BUTTON:
                     // Double press the recents button.
-                    UiObject2 recentsButton = mLauncher.waitForSystemUiObject("recent_apps");
+                    UiObject2 recentsButton = mLauncher.waitForNavigationUiObject("recent_apps");
+                    if (mLauncher.isTablet()) {
+                        mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN,
+                                LauncherInstrumentation.EVENT_TOUCH_DOWN);
+                        mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN,
+                                LauncherInstrumentation.EVENT_TOUCH_UP);
+                    }
                     mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
                     mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL,
                             "clicking Recents button for the first time");
                     mLauncher.getOverview();
+                    if (mLauncher.isTablet()) {
+                        mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN,
+                                LauncherInstrumentation.EVENT_TOUCH_DOWN);
+                        mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN,
+                                LauncherInstrumentation.EVENT_TOUCH_UP);
+                    }
                     mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
                     mLauncher.executeAndWaitForEvent(
                             () -> recentsButton.click(),
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 588b6b8..0e3b501 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -25,6 +25,7 @@
 
 import java.util.Collections;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * Common overview panel for both Launcher and fallback recents
@@ -35,6 +36,7 @@
     BaseOverview(LauncherInstrumentation launcher) {
         super(launcher);
         verifyActiveContainer();
+        verifyActionsViewVisibility();
     }
 
     @Override
@@ -52,14 +54,23 @@
     }
 
     private void flingForwardImpl() {
+        flingForwardImpl(0);
+    }
+
+    private void flingForwardImpl(int rightMargin) {
         try (LauncherInstrumentation.Closable c =
                      mLauncher.addContextLayer("want to fling forward in overview")) {
             LauncherInstrumentation.log("Overview.flingForward before fling");
             final UiObject2 overview = verifyActiveContainer();
-            final int leftMargin = mLauncher.getTargetInsets().left;
-            mLauncher.scroll(
-                    overview, Direction.LEFT, new Rect(leftMargin + 1, 0, 0, 0), 20, false);
-            verifyActiveContainer();
+            final int leftMargin =
+                    mLauncher.getTargetInsets().left + mLauncher.getEdgeSensitivityWidth();
+            mLauncher.scroll(overview, Direction.LEFT, new Rect(leftMargin + 1, 0, rightMargin, 0),
+                    20, false);
+            try (LauncherInstrumentation.Closable c2 =
+                         mLauncher.addContextLayer("flung forwards")) {
+                verifyActiveContainer();
+                verifyActionsViewVisibility();
+            }
         }
     }
 
@@ -80,6 +91,8 @@
 
             mLauncher.clickLauncherObject(
                     mLauncher.waitForObjectInContainer(verifyActiveContainer(), clearAllSelector));
+
+            mLauncher.waitUntilLauncherObjectGone(clearAllSelector);
         }
     }
 
@@ -92,10 +105,49 @@
                      mLauncher.addContextLayer("want to fling backward in overview")) {
             LauncherInstrumentation.log("Overview.flingBackward before fling");
             final UiObject2 overview = verifyActiveContainer();
-            final int rightMargin = mLauncher.getTargetInsets().right;
+            final int rightMargin =
+                    mLauncher.getTargetInsets().right + mLauncher.getEdgeSensitivityWidth();
             mLauncher.scroll(
                     overview, Direction.RIGHT, new Rect(0, 0, rightMargin + 1, 0), 20, false);
+            try (LauncherInstrumentation.Closable c2 =
+                         mLauncher.addContextLayer("flung backwards")) {
+                verifyActiveContainer();
+                verifyActionsViewVisibility();
+            }
+        }
+    }
+
+    /**
+     * Scrolls the current task via flinging forward until it is off screen.
+     *
+     * If only one task is present, it is only partially scrolled off screen and will still be
+     * the current task.
+     */
+    public void scrollCurrentTaskOffScreen() {
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+             LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                     "want to scroll current task off screen in overview")) {
             verifyActiveContainer();
+
+            OverviewTask task = getCurrentTask();
+            mLauncher.assertNotNull("current task is null", task);
+            flingForwardImpl(task.getTaskCenterX());
+
+            try (LauncherInstrumentation.Closable c2 =
+                         mLauncher.addContextLayer("scrolled task off screen")) {
+                verifyActiveContainer();
+                verifyActionsViewVisibility();
+
+                if (getTaskCount() > 1) {
+                    if (mLauncher.isTablet()) {
+                        mLauncher.assertTrue("current task is not grid height",
+                                getCurrentTask().getVisibleHeight() == mLauncher
+                                        .getGridTaskRectForTablet().height());
+                    }
+                    mLauncher.assertTrue("Current task not scrolled off screen",
+                            !getCurrentTask().equals(task));
+                }
+            }
         }
     }
 
@@ -119,6 +171,20 @@
         return new OverviewTask(mLauncher, widestTask, this);
     }
 
+    /**
+     * Returns a list of all tasks fully visible in the tablet grid overview.
+     */
+    @NonNull
+    public List<OverviewTask> getCurrentTasksForTablet() {
+        final List<UiObject2> taskViews = getTasks();
+        mLauncher.assertNotEquals("Unable to find a task", 0, taskViews.size());
+
+        final int gridTaskWidth = mLauncher.getGridTaskRectForTablet().width();
+
+        return taskViews.stream().filter(t -> t.getVisibleBounds().width() == gridTaskWidth).map(
+                t -> new OverviewTask(mLauncher, t, this)).collect(Collectors.toList());
+    }
+
     @NonNull
     private List<UiObject2> getTasks() {
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
@@ -129,6 +195,10 @@
         }
     }
 
+    int getTaskCount() {
+        return getTasks().size();
+    }
+
     /**
      * Returns whether Overview has tasks.
      */
@@ -150,4 +220,63 @@
             return new OverviewActions(overviewActions, mLauncher);
         }
     }
+
+    /**
+     * Returns if clear all button is visible.
+     */
+    public boolean isClearAllVisible() {
+        return mLauncher.hasLauncherObject(mLauncher.getOverviewObjectSelector("clear_all"));
+    }
+
+    /* TODO(b/197630182): Once b/188790554 is fixed, remove instanceof check. Currently, when
+        swiping from app to overview in Fallback Recents, taskbar remains and no action buttons
+        are visible, so we are only testing Overview for now, not BaseOverview. */
+    private void verifyActionsViewVisibility() {
+        if (!(this instanceof Overview) || !hasTasks()) {
+            return;
+        }
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to assert overview actions view visibility")) {
+            if (mLauncher.isTablet() && !isOverviewSnappedToFocusedTaskForTablet()) {
+                mLauncher.waitUntilLauncherObjectGone("action_buttons");
+            } else {
+                mLauncher.waitForLauncherObject("action_buttons");
+            }
+        }
+    }
+
+    /**
+     * Returns if focused task is currently snapped task in tablet grid overview.
+     */
+    private boolean isOverviewSnappedToFocusedTaskForTablet() {
+        UiObject2 focusedTask = getFocusedTaskForTablet();
+        if (focusedTask == null) {
+            return false;
+        }
+        return Math.abs(
+                focusedTask.getVisibleBounds().exactCenterX() - mLauncher.getExactScreenCenterX())
+                < 1;
+    }
+
+    /**
+     * Returns Overview focused task if it exists.
+     *
+     * @throws IllegalStateException if not run on a tablet device.
+     */
+    UiObject2 getFocusedTaskForTablet() {
+        if (!mLauncher.isTablet()) {
+            throw new IllegalStateException("Must be run on tablet device.");
+        }
+        final List<UiObject2> taskViews = getTasks();
+        if (taskViews.size() == 0) {
+            return null;
+        }
+        int focusedTaskHeight = mLauncher.getFocusedTaskHeightForTablet();
+        for (UiObject2 task : taskViews) {
+            if (task.getVisibleBounds().height() == focusedTaskHeight) {
+                return task;
+            }
+        }
+        return null;
+    }
 }
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/Home.java b/tests/tapl/com/android/launcher3/tapl/Home.java
index 0060844..ee9dd1a 100644
--- a/tests/tapl/com/android/launcher3/tapl/Home.java
+++ b/tests/tapl/com/android/launcher3/tapl/Home.java
@@ -63,4 +63,8 @@
         return true;
     }
 
+    @Override
+    protected boolean zeroButtonToOverviewGestureStateTransitionWhileHolding() {
+        return true;
+    }
 }
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index a15131d..7ec5208 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -56,24 +56,29 @@
     protected abstract String launchableType();
 
     private Background launch(BySelector selector) {
-        LauncherInstrumentation.log("Launchable.launch before click "
-                + mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject));
-        final String label = mObject.getText();
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to launch an app from " + launchableType())) {
+            LauncherInstrumentation.log("Launchable.launch before click "
+                    + mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject));
+            final String label = mObject.getText();
 
-        mLauncher.executeAndWaitForEvent(
-                () -> {
-                    mLauncher.clickLauncherObject(mObject);
-                    expectActivityStartEvents();
-                },
-                event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
-                () -> "Launching an app didn't open a new window: " + label,
-                "clicking " + launchableType());
+            mLauncher.executeAndWaitForEvent(
+                    () -> {
+                        mLauncher.clickLauncherObject(mObject);
+                        expectActivityStartEvents();
+                    },
+                    event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
+                    () -> "Launching an app didn't open a new window: " + label,
+                    "clicking " + launchableType());
 
-        mLauncher.assertTrue(
-                "App didn't start: " + label + " (" + selector + ")",
-                TestHelpers.wait(Until.hasObject(selector),
-                        LauncherInstrumentation.WAIT_TIME_MS));
-        return new Background(mLauncher);
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("clicked")) {
+                mLauncher.assertTrue(
+                        "App didn't start: " + label + " (" + selector + ")",
+                        TestHelpers.wait(Until.hasObject(selector),
+                                LauncherInstrumentation.WAIT_TIME_MS));
+                return new Background(mLauncher);
+            }
+        }
     }
 
     /**
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 19094f8..9ee0e25 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -80,6 +80,7 @@
 import java.util.Deque;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -96,15 +97,17 @@
     private static final String TAG = "Tapl";
     private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 20;
     private static final int GESTURE_STEP_MS = 16;
+    private static final long FORCE_PAUSE_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(2);
 
-    private static final Pattern EVENT_TOUCH_DOWN = getTouchEventPattern("ACTION_DOWN");
-    private static final Pattern EVENT_TOUCH_UP = getTouchEventPattern("ACTION_UP");
+    static final Pattern EVENT_TOUCH_DOWN = getTouchEventPattern("ACTION_DOWN");
+    static final Pattern EVENT_TOUCH_UP = getTouchEventPattern("ACTION_UP");
     private static final Pattern EVENT_TOUCH_CANCEL = getTouchEventPattern("ACTION_CANCEL");
     private static final Pattern EVENT_PILFER_POINTERS = Pattern.compile("pilferPointers");
     static final Pattern EVENT_START = Pattern.compile("start:");
 
     static final Pattern EVENT_TOUCH_DOWN_TIS = getTouchEventPatternTIS("ACTION_DOWN");
     static final Pattern EVENT_TOUCH_UP_TIS = getTouchEventPatternTIS("ACTION_UP");
+
     private final String mLauncherPackage;
     private Boolean mIsLauncher3;
     private long mTestStartTime = -1;
@@ -123,8 +126,6 @@
         OUTSIDE_WITHOUT_PILFER, OUTSIDE_WITH_PILFER, INSIDE, INSIDE_TO_OUTSIDE
     }
 
-    ;
-
     // Base class for launcher containers.
     static abstract class VisibleContainer {
         protected final LauncherInstrumentation mLauncher;
@@ -207,6 +208,11 @@
     public LauncherInstrumentation(Instrumentation instrumentation) {
         mInstrumentation = instrumentation;
         mDevice = UiDevice.getInstance(instrumentation);
+        try {
+            mDevice.executeShellCommand("am wait-for-broadcast-idle");
+        } catch (IOException e) {
+            log("Failed to wait for broadcast idle");
+        }
 
         // Launcher should run in test harness so that custom accessibility protocol between
         // Launcher and TAPL is enabled. In-process tests enable this protocol with a direct call
@@ -242,11 +248,24 @@
         if (pm.getComponentEnabledSetting(cn) != COMPONENT_ENABLED_STATE_ENABLED) {
             if (TestHelpers.isInLauncherProcess()) {
                 pm.setComponentEnabledSetting(cn, COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP);
+                // b/195031154
+                SystemClock.sleep(5000);
             } else {
                 try {
                     final int userId = ContextUtils.getUserId(getContext());
+                    final String launcherPidCommand = "pidof " + pi.packageName;
+                    final String initialPid = mDevice.executeShellCommand(launcherPidCommand)
+                            .replaceAll("\\s", "");
                     mDevice.executeShellCommand(
                             "pm enable --user " + userId + " " + cn.flattenToString());
+                    // Wait for Launcher restart after enabling test provider.
+                    for (int i = 0; i < 100; ++i) {
+                        final String currentPid = mDevice.executeShellCommand(launcherPidCommand)
+                                .replaceAll("\\s", "");
+                        if (!currentPid.isEmpty() && !currentPid.equals(initialPid)) break;
+                        if (i == 99) fail("Launcher didn't restart after enabling test provider");
+                        SystemClock.sleep(100);
+                    }
                 } catch (IOException e) {
                     fail(e.toString());
                 }
@@ -267,9 +286,13 @@
     }
 
     Bundle getTestInfo(String request) {
+        return getTestInfo(request, /*arg=*/ null);
+    }
+
+    Bundle getTestInfo(String request, String arg) {
         try (ContentProviderClient client = getContext().getContentResolver()
                 .acquireContentProviderClient(mTestProviderUri)) {
-            return client.call(request, null, null);
+            return client.call(request, arg, null);
         } catch (DeadObjectException e) {
             fail("Launcher crashed");
             return null;
@@ -279,10 +302,48 @@
     }
 
     Insets getTargetInsets() {
+        return getTestInfo(TestProtocol.REQUEST_TARGET_INSETS)
+                .getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+    }
+
+    Insets getWindowInsets() {
         return getTestInfo(TestProtocol.REQUEST_WINDOW_INSETS)
                 .getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
 
+    public boolean isTablet() {
+        return getTestInfo(TestProtocol.REQUEST_IS_TABLET)
+                .getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+    }
+
+    public boolean isTwoPanels() {
+        return getTestInfo(TestProtocol.REQUEST_IS_TWO_PANELS)
+                .getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+    }
+
+    int getFocusedTaskHeightForTablet() {
+        return getTestInfo(TestProtocol.REQUEST_GET_FOCUSED_TASK_HEIGHT_FOR_TABLET).getInt(
+                TestProtocol.TEST_INFO_RESPONSE_FIELD);
+    }
+
+    Rect getGridTaskRectForTablet() {
+        return ((Rect) getTestInfo(TestProtocol.REQUEST_GET_GRID_TASK_SIZE_RECT_FOR_TABLET)
+                .getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD));
+    }
+
+    float getExactScreenCenterX() {
+        return getRealDisplaySize().x / 2f;
+    }
+
+    private void setForcePauseTimeout(long timeout) {
+        getTestInfo(TestProtocol.REQUEST_SET_FORCE_PAUSE_TIMEOUT, Long.toString(timeout));
+    }
+
+    public boolean hadNontestEvents() {
+        return getTestInfo(TestProtocol.REQUEST_GET_HAD_NONTEST_EVENTS)
+                .getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+    }
+
     void setActiveContainer(VisibleContainer container) {
         sActiveContainer = new WeakReference<>(container);
     }
@@ -345,7 +406,8 @@
         }
     }
 
-    private String getSystemAnomalyMessage() {
+    private String getSystemAnomalyMessage(
+            boolean ignoreNavmodeChangeStates, boolean ignoreOnlySystemUiViews) {
         try {
             {
                 final StringBuilder sb = new StringBuilder();
@@ -368,8 +430,19 @@
 
             if (hasSystemUiObject("keyguard_status_view")) return "Phone is locked";
 
-            if (!mDevice.wait(Until.hasObject(getAnyObjectSelector()), WAIT_TIME_MS)) {
-                return "Screen is empty";
+            if (!ignoreOnlySystemUiViews) {
+                final String visibleApps = mDevice.findObjects(getAnyObjectSelector())
+                        .stream()
+                        .map(LauncherInstrumentation::getApplicationPackageSafe)
+                        .distinct()
+                        .filter(pkg -> pkg != null)
+                        .collect(Collectors.joining(","));
+                if (SYSTEMUI_PACKAGE.equals(visibleApps)) return "Only System UI views are visible";
+            }
+            if (!ignoreNavmodeChangeStates) {
+                if (!mDevice.wait(Until.hasObject(getAnyObjectSelector()), WAIT_TIME_MS)) {
+                    return "Screen is empty";
+                }
             }
 
             final String navigationModeError = getNavigationModeMismatchError(true);
@@ -381,8 +454,14 @@
         return null;
     }
 
-    public void checkForAnomaly() {
-        final String systemAnomalyMessage = getSystemAnomalyMessage();
+    private void checkForAnomaly() {
+        checkForAnomaly(false, false);
+    }
+
+    public void checkForAnomaly(
+            boolean ignoreNavmodeChangeStates, boolean ignoreOnlySystemUiViews) {
+        final String systemAnomalyMessage =
+                getSystemAnomalyMessage(ignoreNavmodeChangeStates, ignoreOnlySystemUiViews);
         if (systemAnomalyMessage != null) {
             Assert.fail(formatSystemHealthMessage(formatErrorWithEvents(
                     "http://go/tapl : Tests are broken by a non-Launcher system error: "
@@ -391,12 +470,15 @@
     }
 
     private String getVisiblePackages() {
-        return mDevice.findObjects(getAnyObjectSelector())
+        final String apps = mDevice.findObjects(getAnyObjectSelector())
                 .stream()
                 .map(LauncherInstrumentation::getApplicationPackageSafe)
                 .distinct()
-                .filter(pkg -> pkg != null && !"com.android.systemui".equals(pkg))
+                .filter(pkg -> pkg != null && !SYSTEMUI_PACKAGE.equals(pkg))
                 .collect(Collectors.joining(", "));
+        return !apps.isEmpty()
+                ? "active app: " + apps
+                : "the test doesn't see views from any app, including Launcher";
     }
 
     private static String getApplicationPackageSafe(UiObject2 object) {
@@ -499,9 +581,8 @@
     void fail(String message) {
         checkForAnomaly();
         Assert.fail(formatSystemHealthMessage(formatErrorWithEvents(
-                "http://go/tapl test failure:\nContext: " + getContextDescription()
-                        + " - visible state is " + getVisibleStateMessage()
-                        + ";\nDetails: " + message, true)));
+                "http://go/tapl test failure: " + message + ";\nContext: " + getContextDescription()
+                        + "; now visible state is " + getVisibleStateMessage(), true)));
     }
 
     private String getContextDescription() {
@@ -554,29 +635,33 @@
     public String getNavigationModeMismatchError(boolean waitForCorrectState) {
         final int waitTime = waitForCorrectState ? WAIT_TIME_MS : 0;
         final NavigationModel navigationModel = getNavigationModel();
-
+        String resPackage = getNavigationButtonResPackage();
         if (navigationModel == NavigationModel.THREE_BUTTON) {
-            if (!mDevice.wait(Until.hasObject(By.res(SYSTEMUI_PACKAGE, "recent_apps")), waitTime)) {
+            if (!mDevice.wait(Until.hasObject(By.res(resPackage, "recent_apps")), waitTime)) {
                 return "Recents button not present in 3-button mode";
             }
         } else {
-            if (!mDevice.wait(Until.gone(By.res(SYSTEMUI_PACKAGE, "recent_apps")), waitTime)) {
+            if (!mDevice.wait(Until.gone(By.res(resPackage, "recent_apps")), waitTime)) {
                 return "Recents button is present in non-3-button mode";
             }
         }
 
         if (navigationModel == NavigationModel.ZERO_BUTTON) {
-            if (!mDevice.wait(Until.gone(By.res(SYSTEMUI_PACKAGE, "home")), waitTime)) {
+            if (!mDevice.wait(Until.gone(By.res(resPackage, "home")), waitTime)) {
                 return "Home button is present in gestural mode";
             }
         } else {
-            if (!mDevice.wait(Until.hasObject(By.res(SYSTEMUI_PACKAGE, "home")), waitTime)) {
+            if (!mDevice.wait(Until.hasObject(By.res(resPackage, "home")), waitTime)) {
                 return "Home button not present in non-gestural mode";
             }
         }
         return null;
     }
 
+    private String getNavigationButtonResPackage() {
+        return isTablet() ? getLauncherPackageName() : SYSTEMUI_PACKAGE;
+    }
+
     private UiObject2 verifyContainerType(ContainerType containerType) {
         waitForLauncherInitialized();
 
@@ -688,7 +773,8 @@
      * @return the Workspace object.
      */
     public Workspace pressHome() {
-        try (LauncherInstrumentation.Closable e = eventsCheck()) {
+        try (LauncherInstrumentation.Closable e = eventsCheck();
+             LauncherInstrumentation.Closable c = addContextLayer("want to switch to home")) {
             waitForLauncherInitialized();
             // Click home, then wait for any accessibility event, then wait until accessibility
             // events stop.
@@ -696,24 +782,27 @@
             // otherwise waitForIdle may return immediately in case when there was a big enough
             // pause in accessibility events prior to pressing Home.
             final String action;
-            final boolean launcherWasVisible = isLauncherVisible();
             if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
-                checkForAnomaly();
+                checkForAnomaly(false, true);
+                setForcePauseTimeout(FORCE_PAUSE_TIMEOUT_MS);
 
                 final Point displaySize = getRealDisplaySize();
+                boolean gestureStartFromLauncher = isTablet()
+                        ? !isLauncher3() || hasLauncherObject(WORKSPACE_RES_ID)
+                        : isLauncherVisible();
 
                 if (hasLauncherObject(CONTEXT_MENU_RES_ID)) {
+                    GestureScope gestureScope = gestureStartFromLauncher
+                            ? (isTablet() ? GestureScope.INSIDE : GestureScope.INSIDE_TO_OUTSIDE)
+                            : GestureScope.OUTSIDE_WITH_PILFER;
                     linearGesture(
                             displaySize.x / 2, displaySize.y - 1,
                             displaySize.x / 2, 0,
                             ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME,
-                            false, GestureScope.INSIDE_TO_OUTSIDE);
-                    try (LauncherInstrumentation.Closable c = addContextLayer(
+                            false, gestureScope);
+                    try (LauncherInstrumentation.Closable c1 = addContextLayer(
                             "Swiped up from context menu to home")) {
                         waitUntilLauncherObjectGone(CONTEXT_MENU_RES_ID);
-                        // Swiping up can temporarily bring Nexus Launcher if the current
-                        // Launcher is a Launcher3 one. Wait for the current launcher to reappear.
-                        SystemClock.sleep(5000); // b/187080582
                         waitForLauncherObject(getAnyObjectSelector());
                     }
                 }
@@ -728,8 +817,7 @@
                             displaySize.x / 2, displaySize.y - 1,
                             displaySize.x / 2, 0,
                             ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME, NORMAL_STATE_ORDINAL,
-                            launcherWasVisible
-                                    ? GestureScope.INSIDE_TO_OUTSIDE
+                            gestureStartFromLauncher ? GestureScope.INSIDE_TO_OUTSIDE
                                     : GestureScope.OUTSIDE_WITH_PILFER);
                 }
             } else {
@@ -740,16 +828,20 @@
                     expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
                     expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
                 }
+                if (isTablet()) {
+                    expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_DOWN);
+                    expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_UP);
+                }
 
                 runToState(
-                        waitForSystemUiObject("home")::click,
+                        waitForNavigationUiObject("home")::click,
                         NORMAL_STATE_ORDINAL,
                         !hasLauncherObject(WORKSPACE_RES_ID)
                                 && (hasLauncherObject(APPS_RES_ID)
                                 || hasLauncherObject(OVERVIEW_RES_ID)),
                         action);
             }
-            try (LauncherInstrumentation.Closable c = addContextLayer(
+            try (LauncherInstrumentation.Closable c1 = addContextLayer(
                     "performed action to switch to Home - " + action)) {
                 return getWorkspace();
             }
@@ -892,6 +984,15 @@
         return object;
     }
 
+    @NonNull
+    UiObject2 waitForNavigationUiObject(String resId) {
+        String resPackage = getNavigationButtonResPackage();
+        final UiObject2 object = mDevice.wait(
+                Until.findObject(By.res(resPackage, resId)), WAIT_TIME_MS);
+        assertNotNull("Can't find a navigation UI object with id: " + resId, object);
+        return object;
+    }
+
     @Nullable
     UiObject2 findObjectInContainer(UiObject2 container, BySelector selector) {
         try {
@@ -930,7 +1031,7 @@
     void waitForObjectEnabled(UiObject2 object, String waitReason) {
         try {
             assertTrue("Timed out waiting for object to be enabled for " + waitReason + " "
-                    + object.getResourceName(),
+                            + object.getResourceName(),
                     object.wait(Until.enabled(true), WAIT_TIME_MS));
         } catch (StaleObjectException e) {
             fail("The object disappeared from screen");
@@ -952,6 +1053,15 @@
         }
     }
 
+    List<UiObject2> getChildren(UiObject2 container) {
+        try {
+            return container.getChildren();
+        } catch (StaleObjectException e) {
+            fail("The container disappeared from screen");
+            return null;
+        }
+    }
+
     private boolean hasLauncherObject(String resId) {
         return mDevice.hasObject(getLauncherObjectSelector(resId));
     }
@@ -1049,7 +1159,7 @@
                 () -> "Failed to receive an event for the state change: expected ["
                         + TestProtocol.stateOrdinalToString(expectedState)
                         + "], actual: " + eventListToString(actualEvents),
-                        actionName);
+                actionName);
     }
 
     private boolean isSwitchToStateEvent(
@@ -1070,9 +1180,9 @@
                 "swiping");
     }
 
-    private int getBottomGestureSize() {
-        return ResourceUtils.getNavbarSize(
-                ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, getResources()) + 1;
+    int getBottomGestureSize() {
+        return Math.max(getWindowInsets().bottom, ResourceUtils.getNavbarSize(
+                ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, getResources())) + 1;
     }
 
     int getBottomGestureMarginInContainer(UiObject2 container) {
@@ -1176,7 +1286,7 @@
                 event -> TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName()),
                 () -> "Didn't receive a scroll end message: " + startX + ", " + startY
                         + ", " + endX + ", " + endY,
-                        "scrolling");
+                "scrolling");
     }
 
     // Inject a swipe gesture. Inject exactly 'steps' motion points, incrementing event time by a
@@ -1267,13 +1377,6 @@
         }
 
         final MotionEvent event = getMotionEvent(downTime, currentTime, action, point.x, point.y);
-        // b/190748682
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-            case MotionEvent.ACTION_UP:
-                log("b/190748682: injecting " + event);
-                break;
-        }
         assertTrue("injectInputEvent failed",
                 mInstrumentation.getUiAutomation().injectInputEvent(event, true, false));
         event.recycle();
@@ -1366,16 +1469,6 @@
         getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
     }
 
-    boolean overviewShareEnabled() {
-        return getTestInfo(TestProtocol.REQUEST_OVERVIEW_SHARE_ENABLED).getBoolean(
-                TestProtocol.TEST_INFO_RESPONSE_FIELD);
-    }
-
-    boolean overviewContentPushEnabled() {
-        return getTestInfo(TestProtocol.REQUEST_OVERVIEW_CONTENT_PUSH_ENABLED).getBoolean(
-                TestProtocol.TEST_INFO_RESPONSE_FIELD);
-    }
-
     private void disableSensorRotation() {
         getTestInfo(TestProtocol.REQUEST_MOCK_SENSOR_ROTATION);
     }
@@ -1413,6 +1506,30 @@
         getTestInfo(TestProtocol.REQUEST_CLEAR_DATA);
     }
 
+    private String[] getActivities() {
+        return getTestInfo(TestProtocol.REQUEST_GET_ACTIVITIES)
+                .getStringArray(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+    }
+
+    public String getRootedActivitiesList() {
+        return String.join(", ", getActivities());
+    }
+
+    public boolean noLeakedActivities() {
+        final String[] activities = getActivities();
+        for (String activity : activities) {
+            if (activity.contains("(destroyed)")) {
+                return false;
+            }
+        }
+        return activities.length <= 2;
+    }
+
+    public int getActivitiesCreated() {
+        return getTestInfo(TestProtocol.REQUEST_GET_ACTIVITIES_CREATED_COUNT)
+                .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+    }
+
     public Closable eventsCheck() {
         Assert.assertTrue("Nested event checking", mEventChecker == null);
         disableSensorRotation();
@@ -1465,7 +1582,7 @@
         try {
             return object.getVisibleBounds();
         } catch (StaleObjectException e) {
-            fail("Object " + object + " disappeared from screen");
+            fail("Object disappeared from screen");
             return null;
         } catch (Throwable t) {
             fail(t.toString());
@@ -1474,9 +1591,12 @@
     }
 
     float getWindowCornerRadius() {
+        // TODO(b/197326121): Check if the touch is overlapping with the corners by offsetting
+        final float tmpBuffer = 100f;
         final Resources resources = getResources();
         if (!supportsRoundedCornersOnWindows(resources)) {
-            return 0f;
+            Log.d(TAG, "No rounded corners");
+            return tmpBuffer;
         }
 
         // Radius that should be used in case top or bottom aren't defined.
@@ -1494,7 +1614,8 @@
 
         // Always use the smallest radius to make sure the rounded corners will
         // completely cover the display.
-        return Math.min(topRadius, bottomRadius);
+        Log.d(TAG, "Rounded corners top: " + topRadius + " bottom: " + bottomRadius);
+        return Math.max(topRadius, bottomRadius) + tmpBuffer;
     }
 
     private static boolean supportsRoundedCornersOnWindows(Resources resources) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Overview.java b/tests/tapl/com/android/launcher3/tapl/Overview.java
index 4d673a8..0d06be3 100644
--- a/tests/tapl/com/android/launcher3/tapl/Overview.java
+++ b/tests/tapl/com/android/launcher3/tapl/Overview.java
@@ -30,7 +30,6 @@
 
     Overview(LauncherInstrumentation launcher) {
         super(launcher);
-        verifyActiveContainer();
     }
 
     @Override
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
index 950c052..c8c06e4 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
@@ -34,27 +34,6 @@
     }
 
     /**
-     * Clicks content push button.
-     */
-    @NonNull
-    public Overview clickAndDismissContentPush() {
-        if (mLauncher.overviewContentPushEnabled()) {
-            try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
-                 LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
-                         "want to click content push button and exit screenshot ui")) {
-                UiObject2 exo = mLauncher.waitForObjectInContainer(mOverviewActions,
-                        "action_content_push");
-                mLauncher.clickLauncherObject(exo);
-                try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
-                        "clicked content push button")) {
-                    return new Overview(mLauncher);
-                }
-            }
-        }
-        return new Overview(mLauncher);
-    }
-
-    /**
      * Clicks screenshot button and closes screenshot ui.
      */
     @NonNull
@@ -87,35 +66,6 @@
     }
 
     /**
-     * Click share button, then drags sharesheet down to remove it.
-     *
-     * Share is currently hidden behind flag, test is kept in case share becomes a default feature.
-     * If share is completely removed then remove this test as well.
-     */
-    @NonNull
-    public Overview clickAndDismissShare() {
-        if (mLauncher.overviewShareEnabled()) {
-            try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
-                 LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
-                         "want to click share button and dismiss sharesheet")) {
-                UiObject2 share = mLauncher.waitForObjectInContainer(mOverviewActions,
-                        "action_share");
-                mLauncher.clickLauncherObject(share);
-                try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
-                        "clicked share button")) {
-                    mLauncher.waitForAndroidObject("contentPanel");
-                    mLauncher.getDevice().pressBack();
-                    try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
-                            "dismissed sharesheet")) {
-                        return new Overview(mLauncher);
-                    }
-                }
-            }
-        }
-        return new Overview(mLauncher);
-    }
-
-    /**
      * Click select button
      *
      * @return The select mode buttons that are now shown instead of action buttons.
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 657b74d..15bddd7 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -24,7 +24,9 @@
 
 import com.android.launcher3.testing.TestProtocol;
 
+import java.util.List;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 /**
  * A recent task in the overview panel carousel.
@@ -47,24 +49,77 @@
         mOverview.verifyActiveContainer();
     }
 
+    int getVisibleHeight() {
+        return mTask.getVisibleBounds().height();
+    }
+
+    int getTaskCenterX() {
+        return mTask.getVisibleCenter().x;
+    }
+
     /**
-     * Swipes the task up.
+     * Dismisses the task by swiping up.
      */
     public void dismiss() {
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
              LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
-                     "want to dismiss a task")) {
+                     "want to dismiss an overview task")) {
             verifyActiveContainer();
-            // Dismiss the task via flinging it up.
-            final Rect taskBounds = mLauncher.getVisibleBounds(mTask);
-            final int centerX = taskBounds.centerX();
-            final int centerY = taskBounds.centerY();
-            mLauncher.linearGesture(centerX, centerY, centerX, 0, 10, false,
-                    LauncherInstrumentation.GestureScope.INSIDE);
-            mLauncher.waitForIdle();
+            int taskCountBeforeDismiss = mOverview.getTaskCount();
+            mLauncher.assertNotEquals("Unable to find a task", 0, taskCountBeforeDismiss);
+            if (taskCountBeforeDismiss == 1) {
+                dismissBySwipingUp();
+                return;
+            }
+
+            boolean taskWasFocused = mLauncher.isTablet() && getVisibleHeight() == mLauncher
+                    .getFocusedTaskHeightForTablet();
+            List<Integer> originalTasksCenterX = getCurrentTasksCenterXList();
+            boolean isClearAllVisibleBeforeDismiss = mOverview.isClearAllVisible();
+
+            dismissBySwipingUp();
+
+            try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("dismissed")) {
+                if (taskWasFocused) {
+                    mLauncher.assertNotNull("No task became focused",
+                            mOverview.getFocusedTaskForTablet());
+                }
+                if (!isClearAllVisibleBeforeDismiss) {
+                    List<Integer> currentTasksCenterX = getCurrentTasksCenterXList();
+                    if (originalTasksCenterX.size() == currentTasksCenterX.size()) {
+                        // Check for the same number of visible tasks before and after to
+                        // avoid asserting on cases of shifting all tasks to close the distance
+                        // between clear all and tasks at the end of the grid.
+                        mLauncher.assertTrue("Task centers not aligned",
+                                originalTasksCenterX.equals(currentTasksCenterX));
+                    }
+                }
+            }
         }
     }
 
+    private void dismissBySwipingUp() {
+        verifyActiveContainer();
+        // Dismiss the task via flinging it up.
+        final Rect taskBounds = mLauncher.getVisibleBounds(mTask);
+        final int centerX = taskBounds.centerX();
+        final int centerY = taskBounds.centerY();
+        mLauncher.executeAndWaitForLauncherEvent(
+                () -> mLauncher.linearGesture(centerX, centerY, centerX, 0, 10, false,
+                        LauncherInstrumentation.GestureScope.INSIDE),
+                event -> TestProtocol.DISMISS_ANIMATION_ENDS_MESSAGE.equals(event.getClassName()),
+                () -> "Didn't receive a dismiss animation ends message: " + centerX + ", "
+                        + centerY, "swiping to dismiss");
+    }
+
+    private List<Integer> getCurrentTasksCenterXList() {
+        return mLauncher.isTablet()
+                ? mOverview.getCurrentTasksForTablet().stream()
+                    .map(OverviewTask::getTaskCenterX)
+                    .collect(Collectors.toList())
+                : List.of(mOverview.getCurrentTask().getTaskCenterX());
+    }
+
     /**
      * Clicks at the task.
      */
diff --git a/tests/tapl/com/android/launcher3/tapl/Widget.java b/tests/tapl/com/android/launcher3/tapl/Widget.java
index 3520318..f569ef4 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widget.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widget.java
@@ -16,7 +16,12 @@
 
 package com.android.launcher3.tapl;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
 import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
 
 import com.android.launcher3.testing.TestProtocol;
 
@@ -51,4 +56,55 @@
     protected String launchableType() {
         return "widget";
     }
+
+    /**
+     * Drags a non-configurable widget from the widgets container to the workspace and returns the
+     * resize frame that is shown after the widget is added.
+     */
+    @NonNull
+    public WidgetResizeFrame dragWidgetToWorkspace() {
+        return dragWidgetToWorkspace(/* configurable= */ false, /* acceptsConfig= */ false);
+    }
+
+    /**
+     * Drags a configurable widget from the widgets container to the workspace, either accepts or
+     * cancels the configuration based on {@code acceptsConfig}, and returns the resize frame that
+     * is shown if the widget is added.
+     */
+    @Nullable
+    public WidgetResizeFrame dragConfigWidgetToWorkspace(boolean acceptsConfig) {
+        return dragWidgetToWorkspace(/* configurable= */ true, acceptsConfig);
+    }
+
+    /**
+     * Drags a widget from the widgets container to the workspace and returns the resize frame that
+     * is shown after the widget is added.
+     *
+     * <p> If {@code configurable} is true, then either accepts or cancels the configuration based
+     * on {@code acceptsConfig}.
+     */
+    @Nullable
+    private WidgetResizeFrame dragWidgetToWorkspace(
+            boolean configurable, boolean acceptsConfig) {
+        dragToWorkspace(/* startsActivity= */ configurable, /* isWidgetShortcut= */ false);
+
+        if (configurable) {
+            // Configure the widget.
+            BySelector selector = By.text(acceptsConfig ? "OK" : "Cancel");
+            mLauncher.getDevice()
+                    .wait(Until.findObject(selector), LauncherInstrumentation.WAIT_TIME_MS)
+                    .click();
+
+            // If the widget configuration was cancelled, then the widget wasn't added to the home
+            // screen. In that case, we cannot return a resize frame.
+            if (!acceptsConfig) {
+                return null;
+            }
+        }
+
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to get widget resize frame")) {
+            return new WidgetResizeFrame(mLauncher);
+        }
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java b/tests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java
new file mode 100644
index 0000000..8f51d04
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java
@@ -0,0 +1,37 @@
+/*
+ * 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.tapl;
+
+/** The resize frame that is shown for a widget on the workspace. */
+public class WidgetResizeFrame {
+
+    private final LauncherInstrumentation mLauncher;
+
+    WidgetResizeFrame(LauncherInstrumentation launcher) {
+        mLauncher = launcher;
+        launcher.waitForLauncherObject("widget_resize_frame");
+    }
+
+    /** Dismisses the resize frame. */
+    public void dismiss() {
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+             LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                     "want to dismiss widget resize frame")) {
+            // Dismiss the resize frame by pressing the home button.
+            mLauncher.getDevice().pressHome();
+        }
+    }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 99d9889..6e7264a 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -116,9 +116,9 @@
                     "widget_preview");
             int i = 0;
             for (; ; ) {
-                final Collection<UiObject2> tableRows = widgetsContainer.getChildren();
+                final Collection<UiObject2> tableRows = mLauncher.getChildren(widgetsContainer);
                 for (UiObject2 row : tableRows) {
-                    final Collection<UiObject2> widgetCells = row.getChildren();
+                    final Collection<UiObject2> widgetCells = mLauncher.getChildren(row);
                     for (UiObject2 widget : widgetCells) {
                         final UiObject2 label = mLauncher.findObjectInContainer(widget,
                                 labelSelector);
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 43134d9..288c853 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -34,7 +34,6 @@
 import androidx.test.uiautomator.Direction;
 import androidx.test.uiautomator.UiObject2;
 
-import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.testing.TestProtocol;
 
 import java.util.regex.Pattern;
@@ -63,7 +62,7 @@
     /**
      * Swipes up to All Apps.
      *
-     * @return the App Apps object.
+     * @return the All Apps object.
      */
     @NonNull
     public AllApps switchToAllApps() {
@@ -72,8 +71,7 @@
                      mLauncher.addContextLayer("want to switch from workspace to all apps")) {
             verifyActiveContainer();
             final int deviceHeight = mLauncher.getDevice().getDisplayHeight();
-            final int bottomGestureMargin = ResourceUtils.getNavbarSize(
-                    ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources());
+            final int bottomGestureMargin = mLauncher.getBottomGestureSize();
             final int windowCornerRadius = (int) Math.ceil(mLauncher.getWindowCornerRadius());
             final int startY = deviceHeight - Math.max(bottomGestureMargin, windowCornerRadius) - 1;
             final int swipeHeight = mLauncher.getTestInfo(
@@ -85,9 +83,9 @@
                             + mLauncher.getTouchSlop());
 
             mLauncher.swipeToState(
-                    0,
+                    windowCornerRadius,
                     startY,
-                    0,
+                    windowCornerRadius,
                     startY - swipeHeight - mLauncher.getTouchSlop(),
                     12,
                     ALL_APPS_STATE_ORDINAL, LauncherInstrumentation.GestureScope.INSIDE);
@@ -163,7 +161,7 @@
     }
 
     private boolean isWorkspaceScrollable(UiObject2 workspace) {
-        return workspace.getChildCount() > 1;
+        return workspace.getChildCount() > (mLauncher.isTwoPanels() ? 2 : 1);
     }
 
     @NonNull